diff --git a/.gitmodules b/.gitmodules
index 632c38e..498a346 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
[submodule "plugins/win-dshow/libdshowcapture"]
path = plugins/win-dshow/libdshowcapture
- url = https://github.com/jp9000/libdshowcapture.git
+ url = https://github.com/obsproject/libdshowcapture.git
[submodule "plugins/mac-syphon/syphon-framework"]
path = plugins/mac-syphon/syphon-framework
url = https://github.com/palana/Syphon-Framework.git
@@ -9,10 +9,10 @@
url = https://github.com/Xaymar/obs-studio_amf-encoder-plugin.git
[submodule "plugins/obs-browser"]
path = plugins/obs-browser
- url = https://github.com/kc5nra/obs-browser.git
+ url = https://github.com/obsproject/obs-browser.git
[submodule "plugins/obs-vst"]
path = plugins/obs-vst
- url = https://github.com/DDRBoxman/obs-vst.git
+ url = https://github.com/obsproject/obs-vst.git
[submodule "plugins/obs-outputs/ftl-sdk"]
path = plugins/obs-outputs/ftl-sdk
url = https://github.com/Mixer/ftl-sdk.git
diff --git a/.travis.yml b/.travis.yml
index 9efe056..9100196 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,6 +20,7 @@ env:
matrix:
include:
- os: osx
+ osx_image: xcode9.4
env:
- CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
- CEF_BUILD_VERSION=3.3282.1726.gc8368c8
@@ -45,7 +46,7 @@ deploy:
region: us-west-2
acl: public_read
on:
- repo: jp9000/obs-studio
+ repo: obsproject/obs-studio
condition: "$TRAVIS_OS_NAME = osx"
all_branches: true
@@ -63,6 +64,7 @@ notifications:
webhooks:
urls:
- secure: T5RBY818nO40nr5eC8pdrCfAdQKGkjQdbyYw7mfFrhxWxgt/U5tyKXpX0l9zNGfobS0SnLSqF71OrfW04V97oijXx3q5Y24xV6mSrlLQZOq19+XvGp82LDpkVd4yi2N0kBYpoANB9Pkof4jWT/rKfdQCQttluOLjgr5SM0uWHRg=
+ - secure: EVI2cu5OnNxVTl4jdVppps7O869gGN1PDcSi8fqq/HJVM5kif8iDe4wCrIKv6yWrK3dSNwRgBAwpcPZglRJnKRh23PdFoCdnTjgzBQlmjUR6BYlunQvoKR9mVX6AdT8zrFDgmtC4aOtGD2paptpqt+Equo25KrLwv+qOHJOTrSQ=
on_success: change
on_failure: always
diff --git a/CI/before-deploy-osx.sh b/CI/before-deploy-osx.sh
index f1a87f4..38528d6 100755
--- a/CI/before-deploy-osx.sh
+++ b/CI/before-deploy-osx.sh
@@ -14,11 +14,6 @@ export FILENAME=$FILE_DATE-$GIT_HASH-$TRAVIS_BRANCH-osx.pkg
cd ./build
-# Move the CEF plugin out before running build_app so that it doesn't get packaged twice
-hr "Moving CEF out to preserve linking"
-mv ./rundir/RelWithDebInfo/obs-plugins/CEF.app ./
-mv ./rundir/RelWithDebInfo/obs-plugins/obs-browser.so ./
-
# Move obslua
hr "Moving OBS LUA"
mv ./rundir/RelWithDebInfo/data/obs-scripting/obslua.so ./rundir/RelWithDebInfo/bin/
@@ -35,12 +30,20 @@ if [ -n "${TRAVIS_TAG}" ]; then
STABLE=true
fi
-sudo python ../CI/install/osx/build_app.py --public-key ../CI/install/osx/OBSPublicDSAKey.pem --sparkle-framework ../../sparkle/Sparkle.framework --base-url "https://obsproject.com/osx_update" --stable=$STABLE
+sudo python ../CI/install/osx/build_app.py --public-key ../CI/install/osx/OBSPublicDSAKey.pem --sparkle-framework ../../sparkle/Sparkle.framework --stable=$STABLE
-# Move the CEF plugin back to where it belongs
-hr "Moving CEF back"
-mv ./CEF.app ./rundir/RelWithDebInfo/obs-plugins/
-mv ./obs-browser.so ./rundir/RelWithDebInfo/obs-plugins/
+# Copy Chromium embedded framework to app Frameworks directory
+hr "Copying Chromium Embedded Framework.framework"
+sudo mkdir -p OBS.app/Contents/Frameworks
+sudo cp -r ../../cef_binary_${CEF_BUILD_VERSION}_macosx64/Release/Chromium\ Embedded\ Framework.framework OBS.app/Contents/Frameworks/
+sudo install_name_tool -change \
+ @rpath/Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
+ ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
+ OBS.app/Contents/Resources/obs-plugins/obs-browser.so
+sudo install_name_tool -change \
+ @rpath/Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
+ ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
+ OBS.app/Contents/Resources/obs-plugins/obs-browser-page
# Package app
hr "Generating .pkg"
diff --git a/CI/before-script-osx.sh b/CI/before-script-osx.sh
index 24d1ff5..a0cf54b 100755
--- a/CI/before-script-osx.sh
+++ b/CI/before-script-osx.sh
@@ -1,15 +1,14 @@
# Make sure ccache is found
export PATH=/usr/local/opt/ccache/libexec:$PATH
-cd ./plugins/obs-browser
-git checkout origin/osx
-cd -
+git fetch --tags
mkdir build
cd build
cmake -DENABLE_SPARKLE_UPDATER=ON \
--DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
+-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \
+-DQTDIR=/usr/local/Cellar/qt/5.10.1 \
-DDepsPath=/tmp/obsdeps \
-DVLCPath=$PWD/../../vlc-master \
-DBUILD_BROWSER=ON \
--DCEF_ROOT_DIR=$PWD/../../cef_binary_${CEF_BUILD_VERSION}_macosx64 ..
\ No newline at end of file
+-DCEF_ROOT_DIR=$PWD/../../cef_binary_${CEF_BUILD_VERSION}_macosx64 ..
diff --git a/CI/install-dependencies-osx.sh b/CI/install-dependencies-osx.sh
index 4d53296..82f3aac 100755
--- a/CI/install-dependencies-osx.sh
+++ b/CI/install-dependencies-osx.sh
@@ -1,3 +1,9 @@
+hr() {
+ echo "───────────────────────────────────────────────────"
+ echo $1
+ echo "───────────────────────────────────────────────────"
+}
+
# Exit if something fails
set -e
@@ -11,40 +17,46 @@ cd ../
# Install Packages app so we can build a package later
# http://s.sudre.free.fr/Software/Packages/about.html
-wget --retry-connrefused --waitretry=1 https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
+hr "Downloading Packages app"
+wget --quiet --retry-connrefused --waitretry=1 https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
sudo installer -pkg ./Packages.pkg -target /
brew update
#Base OBS Deps and ccache
-brew install qt5 jack speexdsp ccache swig
+brew install jack speexdsp ccache swig mbedtls
+brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/9a70413d137839de0054571e5f85fd07ee400955/Formula/qt.rb
export PATH=/usr/local/opt/ccache/libexec:$PATH
ccache -s || echo "CCache is not available."
# Fetch and untar prebuilt OBS deps that are compatible with older versions of OSX
-wget --retry-connrefused --waitretry=1 https://s3-us-west-2.amazonaws.com/obs-nightly/osx-deps.tar.gz
-tar -xf ./osx-deps.tar.gz -C /tmp
+hr "Downloading OBS deps"
+wget --quiet --retry-connrefused --waitretry=1 https://obs-nightly.s3.amazonaws.com/osx-deps-2018-08-09.tar.gz
+tar -xf ./osx-deps-2018-08-09.tar.gz -C /tmp
# Fetch vlc codebase
-wget --retry-connrefused --waitretry=1 -O vlc-master.zip https://github.com/videolan/vlc/archive/master.zip
+hr "Downloading VLC repo"
+wget --quiet --retry-connrefused --waitretry=1 -O vlc-master.zip https://github.com/videolan/vlc/archive/master.zip
unzip -q ./vlc-master.zip
# Get sparkle
-wget --retry-connrefused --waitretry=1 -O sparkle.tar.bz2 https://github.com/sparkle-project/Sparkle/releases/download/1.16.0/Sparkle-1.16.0.tar.bz2
+hr "Downloading Sparkle framework"
+wget --quiet --retry-connrefused --waitretry=1 -O sparkle.tar.bz2 https://github.com/sparkle-project/Sparkle/releases/download/1.20.0/Sparkle-1.20.0.tar.bz2
mkdir ./sparkle
tar -xf ./sparkle.tar.bz2 -C ./sparkle
sudo cp -R ./sparkle/Sparkle.framework /Library/Frameworks/Sparkle.framework
# CEF Stuff
-wget --retry-connrefused --waitretry=1 https://obs-nightly.s3-us-west-2.amazonaws.com/cef_binary_${CEF_BUILD_VERSION}_macosx64.tar.bz2
+hr "Downloading CEF"
+wget --quiet --retry-connrefused --waitretry=1 https://obs-nightly.s3-us-west-2.amazonaws.com/cef_binary_${CEF_BUILD_VERSION}_macosx64.tar.bz2
tar -xf ./cef_binary_${CEF_BUILD_VERSION}_macosx64.tar.bz2
cd ./cef_binary_${CEF_BUILD_VERSION}_macosx64
# remove a broken test
sed -i '.orig' '/add_subdirectory(tests\/ceftests)/d' ./CMakeLists.txt
mkdir build
cd ./build
-cmake -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++" -DCMAKE_EXE_LINKER_FLAGS="-std=c++11 -stdlib=libc++" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 ..
+cmake -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++" -DCMAKE_EXE_LINKER_FLAGS="-std=c++11 -stdlib=libc++" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 ..
make -j4
mkdir libcef_dll
cd ../../
diff --git a/CI/install/osx/CMakeLists.pkgproj b/CI/install/osx/CMakeLists.pkgproj
index eff607a..81f1934 100644
--- a/CI/install/osx/CMakeLists.pkgproj
+++ b/CI/install/osx/CMakeLists.pkgproj
@@ -86,7 +86,7 @@
GID
80
PATH
- ../../../build/plugins/obs-browser/CEF.app
+ ../../../build/plugins/obs-browser/obs-browser-page
PATH_TYPE
3
PERMISSIONS
diff --git a/CI/install/osx/build_app.py b/CI/install/osx/build_app.py
index aa98437..69e7cf2 100644
--- a/CI/install/osx/build_app.py
+++ b/CI/install/osx/build_app.py
@@ -45,7 +45,7 @@ parser.add_argument('-d', '--base-dir', dest='dir', default='rundir/RelWithDebIn
parser.add_argument('-n', '--build-number', dest='build_number', default='0')
parser.add_argument('-k', '--public-key', dest='public_key', default='OBSPublicDSAKey.pem')
parser.add_argument('-f', '--sparkle-framework', dest='sparkle', default=None)
-parser.add_argument('-b', '--base-url', dest='base_url', default='https://builds.catchexception.org/obs-studio')
+parser.add_argument('-b', '--base-url', dest='base_url', default='https://obsproject.com/osx_update')
parser.add_argument('-u', '--user', dest='user', default='jp9000')
parser.add_argument('-c', '--channel', dest='channel', default='master')
add_boolean_argument(parser, 'stable', default=False)
@@ -82,6 +82,16 @@ for i in candidate_paths:
print("Checking " + i)
for root, dirs, files in walk(build_path+"/"+i):
for file_ in files:
+ if ".ini" in file_:
+ continue
+ if ".png" in file_:
+ continue
+ if ".effect" in file_:
+ continue
+ if ".py" in file_:
+ continue
+ if ".json" in file_:
+ continue
path = root + "/" + file_
try:
out = check_output("{0}otool -L '{1}'".format(args.prefix, path), shell=True,
diff --git a/CI/install/osx/post-install.sh b/CI/install/osx/post-install.sh
index f55ae58..f1f641a 100644
--- a/CI/install/osx/post-install.sh
+++ b/CI/install/osx/post-install.sh
@@ -1,5 +1 @@
#!/usr/bin/env bash
-
-# Fix permissions on CEF
-chmod 744 "/Library/Application Support/obs-studio/plugins/obs-browser/bin/CEF.app/Contents/Info.plist"
-chmod 744 "/Library/Application Support/obs-studio/plugins/obs-browser/bin/CEF.app/Contents/Frameworks/CEF Helper.app/Contents/Info.plist"
diff --git a/CI/util/build-package-deps-osx.sh b/CI/util/build-package-deps-osx.sh
index 12fb805..20c0c30 100755
--- a/CI/util/build-package-deps-osx.sh
+++ b/CI/util/build-package-deps-osx.sh
@@ -1,5 +1,13 @@
#!/usr/bin/env bash
+set -e
+
+# This script builds a tar file that contains a bunch of deps that OBS needs for
+# advanced functionality on OSX. Currently this tar file is pulled down off of s3
+# and used in the CI build process on travis.
+# Mostly this sets build flags to compile with older SDKS and make sure that
+# the libs are portable.
+
exists()
{
command -v "$1" >/dev/null 2>&1
@@ -35,15 +43,15 @@ mkdir $DEPS_DEST/include
mkdir $DEPS_DEST/lib
# OSX COMPAT
-export MACOSX_DEPLOYMENT_TARGET=10.9
+export MACOSX_DEPLOYMENT_TARGET=10.11
# If you need an olders SDK and Xcode won't give it to you
# https://github.com/phracker/MacOSX-SDKs
# libopus
-curl -L -O http://downloads.xiph.org/releases/opus/opus-1.1.3.tar.gz
-tar -xf opus-1.1.3.tar.gz
-cd ./opus-1.1.3
+curl -L -O https://ftp.osuosl.org/pub/xiph/releases/opus/opus-1.2.1.tar.gz
+tar -xf opus-1.2.1.tar.gz
+cd ./opus-1.2.1
mkdir build
cd ./build
../configure --disable-shared --enable-static --prefix="/tmp/obsdeps"
@@ -53,9 +61,9 @@ make install
cd $WORK_DIR
# libogg
-curl -L -O http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.gz
-tar -xf libogg-1.3.2.tar.gz
-cd ./libogg-1.3.2
+curl -L -O https://ftp.osuosl.org/pub/xiph/releases/ogg/libogg-1.3.3.tar.gz
+tar -xf libogg-1.3.3.tar.gz
+cd ./libogg-1.3.3
mkdir build
cd ./build
../configure --disable-shared --enable-static --prefix="/tmp/obsdeps"
@@ -65,9 +73,9 @@ make install
cd $WORK_DIR
# libvorbis
-curl -L -O http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.gz
-tar -xf libvorbis-1.3.5.tar.gz
-cd ./libvorbis-1.3.5
+curl -L -O https://ftp.osuosl.org/pub/xiph/releases/vorbis/libvorbis-1.3.6.tar.gz
+tar -xf libvorbis-1.3.6.tar.gz
+cd ./libvorbis-1.3.6
mkdir build
cd ./build
../configure --disable-shared --enable-static --prefix="/tmp/obsdeps"
@@ -77,12 +85,13 @@ make install
cd $WORK_DIR
# libvpx
-curl -L -O http://storage.googleapis.com/downloads.webmproject.org/releases/webm/libvpx-1.6.0.tar.bz2
-tar -xf libvpx-1.6.0.tar.bz2
-cd ./libvpx-1.6.0
-mkdir build
+curl -L -O https://chromium.googlesource.com/webm/libvpx/+archive/v1.7.0.tar.gz
+mkdir -p ./libvpx-v1.7.0
+tar -xf v1.7.0.tar.gz -C $PWD/libvpx-v1.7.0
+cd ./libvpx-v1.7.0
+mkdir -p build
cd ./build
-../configure --disable-shared --libdir="/tmp/obsdeps/bin"
+../configure --disable-shared --prefix="/tmp/obsdeps" --libdir="/tmp/obsdeps/lib"
make -j 12
make install
@@ -94,10 +103,10 @@ cd ./x264
git checkout origin/stable
mkdir build
cd ./build
-../configure --extra-ldflags="-mmacosx-version-min=10.9" --enable-static --prefix="/tmp/obsdeps"
+../configure --extra-ldflags="-mmacosx-version-min=10.11" --enable-static --prefix="/tmp/obsdeps"
make -j 12
make install
-../configure --extra-ldflags="-mmacosx-version-min=10.9" --enable-shared --libdir="/tmp/obsdeps/bin" --prefix="/tmp/obsdeps"
+../configure --extra-ldflags="-mmacosx-version-min=10.11" --enable-shared --libdir="/tmp/obsdeps/bin" --prefix="/tmp/obsdeps"
make -j 12
ln -f -s libx264.*.dylib libx264.dylib
find . -name \*.dylib -exec cp \{\} $DEPS_DEST/bin/ \;
@@ -107,9 +116,9 @@ rsync -avh --include="*/" --include="*.h" --exclude="*" ./* $DEPS_DEST/include/
cd $WORK_DIR
# janson
-curl -L -O http://www.digip.org/jansson/releases/jansson-2.9.tar.gz
-tar -xf jansson-2.9.tar.gz
-cd jansson-2.9
+curl -L -O http://www.digip.org/jansson/releases/jansson-2.11.tar.gz
+tar -xf jansson-2.11.tar.gz
+cd jansson-2.11
mkdir build
cd ./build
../configure --libdir="/tmp/obsdeps/bin" --enable-shared --disable-static
@@ -124,12 +133,12 @@ export LDFLAGS="-L/tmp/obsdeps/lib"
export CFLAGS="-I/tmp/obsdeps/include"
# FFMPEG
-curl -L -O https://github.com/FFmpeg/FFmpeg/archive/n3.2.2.zip
-unzip ./n3.2.2.zip
-cd ./FFmpeg-n3.2.2
+curl -L -O https://github.com/FFmpeg/FFmpeg/archive/n4.0.2.zip
+unzip ./n4.0.2.zip
+cd ./FFmpeg-n4.0.2
mkdir build
cd ./build
-../configure --extra-ldflags="-mmacosx-version-min=10.9" --enable-shared --disable-static --shlibdir="/tmp/obsdeps/bin" --enable-gpl --disable-doc --enable-libx264 --enable-libopus --enable-libvorbis --enable-libvpx --disable-outdev=sdl
+../configure --pkg-config-flags="--static" --extra-ldflags="-mmacosx-version-min=10.11" --enable-shared --disable-static --shlibdir="/tmp/obsdeps/bin" --enable-gpl --disable-doc --enable-libx264 --enable-libopus --enable-libvorbis --enable-libvpx --disable-outdev=sdl
make -j 12
find . -name \*.dylib -exec cp \{\} $DEPS_DEST/bin/ \;
rsync -avh --include="*/" --include="*.h" --exclude="*" ../* $DEPS_DEST/include/
@@ -149,4 +158,4 @@ cd $WORK_DIR
tar -czf osx-deps.tar.gz obsdeps
-cp ./osx-deps.tar.gz $CURDIR
\ No newline at end of file
+cp ./osx-deps.tar.gz $CURDIR
diff --git a/CMakeLists.txt b/CMakeLists.txt
index abe9895..5bac27c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,10 @@
cmake_minimum_required(VERSION 2.8.12)
+if (UNIX AND POLICY CMP0072)
+ # In case of both legacy and glvnd OpenGL libraries found. Prefer GLVND
+ cmake_policy(SET CMP0072 NEW)
+endif()
+
project(obs-studio)
option(BUILD_CAPTIONS "Build captions" FALSE)
@@ -27,6 +32,23 @@ include(ObsHelpers)
include(ObsCpack)
include(GNUInstallDirs)
+# Must be a string in the format of "x.x.x-rcx"
+if(DEFINED RELEASE_CANDIDATE)
+ set(OBS_VERSION "${RELEASE_CANDIDATE}")
+ string(REPLACE "-rc" "." RC_SPLIT ${RELEASE_CANDIDATE})
+ string(REPLACE "." ";" RC_SPLIT ${RC_SPLIT})
+ message(WARNING "******************************************************************************\nRelease candidate deteced, OBS_VERSION is now: ${OBS_VERSION}\n******************************************************************************")
+ list(GET RC_SPLIT 0 OBS_RELEASE_CANDIDATE_MAJOR)
+ list(GET RC_SPLIT 1 OBS_RELEASE_CANDIDATE_MINOR)
+ list(GET RC_SPLIT 2 OBS_RELEASE_CANDIDATE_PATCH)
+ list(GET RC_SPLIT 3 OBS_RELEASE_CANDIDATE)
+else()
+ set(OBS_RELEASE_CANDIDATE_MAJOR 0)
+ set(OBS_RELEASE_CANDIDATE_MINOR 0)
+ set(OBS_RELEASE_CANDIDATE_PATCH 0)
+ set(OBS_RELEASE_CANDIDATE 0)
+endif()
+
if(MSVC AND NOT EXISTS "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user")
file(GENERATE
OUTPUT "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user"
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 29518b1..6ea05c2 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -39,8 +39,8 @@ Coding Guidelines
- 80 columns max
-Commit Guidlines
-----------------
+Commit Guidelines
+-----------------
- OBS Studio uses the 50/72 standard for commits. 50 characters max
for the title (excluding module prefix), an empty line, and then a
diff --git a/INSTALL b/INSTALL
index 258ea26..cf2707d 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1 +1 @@
-For install instructions please visit https://github.com/jp9000/obs-studio/wiki/Install-Instructions
+For install instructions please visit https://github.com/obsproject/obs-studio/wiki/Install-Instructions
diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt
index 880869c..f414488 100644
--- a/UI/CMakeLists.txt
+++ b/UI/CMakeLists.txt
@@ -55,6 +55,8 @@ include_directories(${FFMPEG_INCLUDE_DIRS})
include_directories(SYSTEM "obs-frontend-api")
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/deps/libff")
+include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/deps/json11")
+include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/plugins/obs-browser/panel")
find_package(Libcurl REQUIRED)
include_directories(${LIBCURL_INCLUDE_DIRS})
@@ -127,6 +129,7 @@ endif()
set(obs_SOURCES
${obs_PLATFORM_SOURCES}
${obs_libffutil_SOURCES}
+ ../deps/json11/json11.cpp
obs-app.cpp
api-interface.cpp
window-basic-main.cpp
@@ -152,6 +155,7 @@ set(obs_SOURCES
window-log-reply.cpp
window-projector.cpp
window-remux.cpp
+ source-tree.cpp
properties-view.cpp
focus-list.cpp
menu-button.cpp
@@ -161,6 +165,7 @@ set(obs_SOURCES
item-widget-helpers.cpp
visibility-checkbox.cpp
locked-checkbox.cpp
+ horizontal-scroll-area.cpp
vertical-scroll-area.cpp
visibility-item-widget.cpp
slider-absoluteset-style.cpp
@@ -176,6 +181,7 @@ set(obs_SOURCES
set(obs_HEADERS
${obs_PLATFORM_HEADERS}
${obs_libffutil_HEADERS}
+ ../deps/json11/json11.hpp
obs-app.hpp
platform.hpp
window-main.hpp
@@ -197,6 +203,7 @@ set(obs_HEADERS
window-log-reply.hpp
window-projector.hpp
window-remux.hpp
+ source-tree.hpp
properties-view.hpp
properties-view.moc.hpp
display-helpers.hpp
@@ -209,6 +216,8 @@ set(obs_HEADERS
item-widget-helpers.hpp
visibility-checkbox.hpp
locked-checkbox.hpp
+ horizontal-scroll-area.hpp
+ expand-checkbox.hpp
vertical-scroll-area.hpp
visibility-item-widget.hpp
slider-absoluteset-style.hpp
@@ -227,6 +236,7 @@ set(obs_UI
forms/AutoConfigVideoPage.ui
forms/AutoConfigStreamPage.ui
forms/AutoConfigTestPage.ui
+ forms/ColorSelect.ui
forms/OBSLicenseAgreement.ui
forms/OBSLogReply.ui
forms/OBSBasic.ui
diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp
index 531b001..2c602b6 100644
--- a/UI/adv-audio-control.cpp
+++ b/UI/adv-audio-control.cpp
@@ -13,7 +13,7 @@
#define NSEC_PER_MSEC 1000000
#endif
-OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_)
+OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
: source(source_)
{
QHBoxLayout *hlayout;
@@ -155,20 +155,7 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_)
QWidget::connect(mixer6, SIGNAL(clicked(bool)),
this, SLOT(mixer6Changed(bool)));
- int lastRow = layout->rowCount();
-
- idx = 0;
- layout->addWidget(nameLabel, lastRow, idx++);
- layout->addWidget(volume, lastRow, idx++);
- layout->addWidget(forceMonoContainer, lastRow, idx++);
- layout->addWidget(panningContainer, lastRow, idx++);
- layout->addWidget(syncOffset, lastRow, idx++);
-#if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO
- layout->addWidget(monitoringType, lastRow, idx++);
-#endif
- layout->addWidget(mixerContainer, lastRow, idx++);
- layout->layout()->setAlignment(mixerContainer,
- Qt::AlignHCenter | Qt::AlignVCenter);
+ setObjectName(sourceName);
}
OBSAdvAudioCtrl::~OBSAdvAudioCtrl()
@@ -184,6 +171,24 @@ OBSAdvAudioCtrl::~OBSAdvAudioCtrl()
mixerContainer->deleteLater();
}
+void OBSAdvAudioCtrl::ShowAudioControl(QGridLayout *layout)
+{
+ int lastRow = layout->rowCount();
+ int idx = 0;
+
+ layout->addWidget(nameLabel, lastRow, idx++);
+ layout->addWidget(volume, lastRow, idx++);
+ layout->addWidget(forceMonoContainer, lastRow, idx++);
+ layout->addWidget(panningContainer, lastRow, idx++);
+ layout->addWidget(syncOffset, lastRow, idx++);
+#if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO
+ layout->addWidget(monitoringType, lastRow, idx++);
+#endif
+ layout->addWidget(mixerContainer, lastRow, idx++);
+ layout->layout()->setAlignment(mixerContainer,
+ Qt::AlignHCenter | Qt::AlignVCenter);
+}
+
/* ------------------------------------------------------------------------- */
/* OBS source callbacks */
diff --git a/UI/adv-audio-control.hpp b/UI/adv-audio-control.hpp
index 38d7608..1352a4b 100644
--- a/UI/adv-audio-control.hpp
+++ b/UI/adv-audio-control.hpp
@@ -51,6 +51,7 @@ public:
virtual ~OBSAdvAudioCtrl();
inline obs_source_t *GetSource() const {return source;}
+ void ShowAudioControl(QGridLayout *layout);
public slots:
void SourceFlagsChanged(uint32_t flags);
diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp
index a580b72..e7cc836 100644
--- a/UI/api-interface.cpp
+++ b/UI/api-interface.cpp
@@ -93,9 +93,11 @@ struct OBSStudioAPI : obs_frontend_callbacks {
{
if (main->IsPreviewProgramMode()) {
QMetaObject::invokeMethod(main, "TransitionToScene",
+ WaitConnection(),
Q_ARG(OBSSource, OBSSource(scene)));
} else {
QMetaObject::invokeMethod(main, "SetCurrentScene",
+ WaitConnection(),
Q_ARG(OBSSource, OBSSource(scene)),
Q_ARG(bool, false));
}
@@ -167,6 +169,19 @@ struct OBSStudioAPI : obs_frontend_callbacks {
}
}
+ bool obs_frontend_add_scene_collection(
+ const char *name) override
+ {
+ bool success = false;
+ QMetaObject::invokeMethod(main,
+ "AddSceneCollection",
+ WaitConnection(),
+ Q_RETURN_ARG(bool, success),
+ Q_ARG(bool, true),
+ Q_ARG(QString, QT_UTF8(name)));
+ return success;
+ }
+
void obs_frontend_get_profiles(
std::vector &strings) override
{
@@ -329,6 +344,16 @@ struct OBSStudioAPI : obs_frontend_callbacks {
main->SaveProject();
}
+ void obs_frontend_defer_save_begin(void) override
+ {
+ QMetaObject::invokeMethod(main, "DeferSaveBegin");
+ }
+
+ void obs_frontend_defer_save_end(void) override
+ {
+ QMetaObject::invokeMethod(main, "DeferSaveEnd");
+ }
+
void obs_frontend_add_save_callback(obs_frontend_save_cb callback,
void *private_data) override
{
diff --git a/UI/data/locale.ini b/UI/data/locale.ini
index dd9c6c0..00e4c1f 100644
--- a/UI/data/locale.ini
+++ b/UI/data/locale.ini
@@ -147,3 +147,9 @@ Name=gjuha shqipe
[tl-PH]
Name=Wikang Tagalog
+
+[fa-IR]
+Name=فارسی
+
+[gd-GB]
+Name=Gàidhlig
diff --git a/UI/data/locale/af-ZA.ini b/UI/data/locale/af-ZA.ini
index d44bfd8..ffc182a 100644
--- a/UI/data/locale/af-ZA.ini
+++ b/UI/data/locale/af-ZA.ini
@@ -88,6 +88,9 @@ Rename="Hernoem"
+
+
+
diff --git a/UI/data/locale/ar-SA.ini b/UI/data/locale/ar-SA.ini
index e013963..ae2c6f9 100644
--- a/UI/data/locale/ar-SA.ini
+++ b/UI/data/locale/ar-SA.ini
@@ -122,6 +122,12 @@ Basic.AutoConfig.StreamPage.PerformBandwidthTest="تقدير معدل التدف
Basic.AutoConfig.StreamPage.PreferHardwareEncoding="تفضيل استخدام الترميز بواسطة الهاردوير"
Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="استخدام الترميز بالهاردوير يلغي الحاجة الى معظم موارد المعالج, لكن قد يحتاج الى معدل بث أعلى للحفاظ على نفس مستوى الجودة."
Basic.AutoConfig.StreamPage.StreamWarning.Title="تحذير يتعلق بالبث"
+Basic.AutoConfig.TestPage="النتيجة النهائية"
+Basic.AutoConfig.TestPage.SubTitle.Testing="يقوم البرنامج الآن بتنفيذ مجموعة من الإختبارات لتقدير الإعدادات الأكثر مثالية"
+Basic.AutoConfig.TestPage.SubTitle.Complete="انتهى الاختبار"
+Basic.AutoConfig.TestPage.TestingBandwidth="إجراء اختبار النطاق الترددي، قد يستغرق هذا بضع دقائق..."
+Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="الاتصال ب1:%..."
+Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="فشل الاتصال بأي سيرفر، رجاءا تحقق من اتصالك بالانترنت وأعد المحاولة."
Basic.Stats="إحصائيات"
Basic.Stats.CPUUsage="استخدام المعالج"
@@ -473,3 +479,6 @@ Basic.Settings.Advanced.Video.ColorRange.Full="كامل"
+
+
+
diff --git a/UI/data/locale/bg-BG.ini b/UI/data/locale/bg-BG.ini
index 6a155ed..183e2d4 100644
--- a/UI/data/locale/bg-BG.ini
+++ b/UI/data/locale/bg-BG.ini
@@ -508,10 +508,6 @@ Basic.Settings.General.SaveProjectors="Запамети прожекторите
Basic.Settings.General.SwitchOnDoubleClick="Преминаване към сцена при двойно кликване"
Basic.Settings.General.StudioPortraitLayout="Включи портретен / вертикален изглед"
Basic.Settings.General.MultiviewLayout="Разположение на Множествения Изглед"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Хоризонтално, Горе"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Водоравно, Долу"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Вертикално, от Ляво"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Вертикално, от Дясно"
Basic.Settings.Stream="Стрийм"
Basic.Settings.Stream.StreamType="Тип Стрийм"
@@ -749,3 +745,6 @@ OutputWarnings.MP4Recording="Предупреждение: Записи запа
FinalScene.Title="Изтрий Сцената"
FinalScene.Text="Трябва да има поне една сцена във наличност."
+
+
+
diff --git a/UI/data/locale/bn-BD.ini b/UI/data/locale/bn-BD.ini
index b6d333f..8039b73 100644
--- a/UI/data/locale/bn-BD.ini
+++ b/UI/data/locale/bn-BD.ini
@@ -561,3 +561,6 @@ OutputWarnings.MultiTrackRecording="সতর্কতা: একাধিক
OutputWarnings.MP4Recording="সতর্কতা: রেকর্ডিং MP4 কাছে সংরক্ষিত ফাইল (যেমন: BSODs ফলে, বিদ্যুৎ লোকসান, ইত্যাদি।) চূড়ান্ত করা না হলে নির্বাহ হওয়ার সময়ের অপুনরুদ্ধারযোগ্য করা হবে। আপনি যদি রেকর্ড করতে চান একাধিক অডিও ট্র্যাক MKV এবং remux রেকর্ড করা mp4 ব্যবহার করে এটি সম্পন্ন করার পর বিবেচনা (Remux রেকর্ডিং-> ফাইল)"
+
+
+
diff --git a/UI/data/locale/ca-ES.ini b/UI/data/locale/ca-ES.ini
index eb8a382..1b58259 100644
--- a/UI/data/locale/ca-ES.ini
+++ b/UI/data/locale/ca-ES.ini
@@ -78,6 +78,8 @@ None="Cap"
StudioMode.Preview="Vista prèvia"
StudioMode.Program="Programa"
ShowInMultiview="Mostra en vista múltiple"
+VerticalLayout="Disposició vertical"
+Group="Grup"
AlreadyRunning.Title="L'OBS ja s'està executant"
AlreadyRunning.Text="L'OBS ja s'està executant! A no ser que vulgueu fer això, tanqueu totes les finestres de l'OBS abans d'intentar iniciar una nova. Si teniu configurat OBS perquè es minimitzi a la barra de tasques, proveu a veure si segueix executant-se aquí."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="S'està aturant la reproducció de la memòria
Basic.Main.StopStreaming="Atura l'enregistrament"
Basic.Main.StoppingStreaming="Aturant la transmissió..."
Basic.Main.ForceStopStreaming="Atura l'enregistrament (descarta el retard)"
+Basic.Main.Group="Grup %1"
+Basic.Main.GroupItems="Agrupa els elements seleccionats"
+Basic.Main.Ungroup="Desagrupa"
Basic.MainMenu.File="&Fitxer"
Basic.MainMenu.File.Export="&Exporta"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Eines"
Basic.MainMenu.Help="&Ajuda"
Basic.MainMenu.Help.HelpPortal="Portal d'ajuda"
Basic.MainMenu.Help.Website="Visa el lloc &web"
+Basic.MainMenu.Help.Discord="Uniu-vos a un servidor &Discord"
Basic.MainMenu.Help.Logs="Fitxers de ®istre"
Basic.MainMenu.Help.Logs.ShowLogs="&Mostra els arxius de registre"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Carregar arxiu de registre actual"
Basic.MainMenu.Help.Logs.UploadLastLog="Carregar darrer fitxer de registre"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Visualitza el registre actual"
Basic.MainMenu.Help.CheckForUpdates="Comprova si hi ha cap actualització"
+Basic.MainMenu.Help.CrashLogs="Info&rme de fallada"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Mo&stra els informes de fallada"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Penja el darrer informe de fa&llada"
Basic.Settings.ProgramRestart="El programa ha de ser re-iniciat per tal que aquesta configuració tingui efecte."
Basic.Settings.ConfirmTitle="Confirma els canvis"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Minimitza sempre a la safata del
Basic.Settings.General.SaveProjectors="Desa els projectors en sortir"
Basic.Settings.General.SwitchOnDoubleClick="Transició a l'escena en fer doble clic"
Basic.Settings.General.StudioPortraitLayout="Habilita la disposició horitzontal/vertical"
+Basic.Settings.General.Multiview="Vista múltiple"
+Basic.Settings.General.Multiview.MouseSwitch="Feu clic per canviar entre escenes"
+Basic.Settings.General.Multiview.DrawSourceNames="Mostra el nom de l'escena"
+Basic.Settings.General.Multiview.DrawSafeAreas="Dibuixa les zones segures (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Disposició de vista múltiple"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horitzontal, amunt"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horitzontal, abaix"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, esquerra"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, dreta"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horitzontal, part superior (8 escenes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horitzontal, part inferior (8 escenes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, esquerra (8 escenes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, dreta (8 escenes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horitzontal, part superior (24 escenes)"
Basic.Settings.Stream="Directe"
Basic.Settings.Stream.StreamType="Tipus de directe"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Resposta del ràtio de l'audiòmetre"
Basic.Settings.Audio.MeterDecayRate.Fast="Ràpida"
Basic.Settings.Audio.MeterDecayRate.Medium="Mitja (PPM de tipus I)"
Basic.Settings.Audio.MeterDecayRate.Slow="Lenta (PPM de tipus II)"
+Basic.Settings.Audio.PeakMeterType="Tipus de mesurador de pics"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Pic de mostra"
+Basic.Settings.Audio.PeakMeterType.TruePeak="True Peak (ús elevat de la CPU)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="ATENCIÓ: El so envoltant està habilitat."
Basic.Settings.Audio.MultichannelWarning="Si esteu retransmetent, verifiqueu que el servei triat suporta el so envoltant, tant a la reproducció d'entrada com de sortida. El Twitch, el Facebook 360 Live, el Mixer RTMP i el Smashcast són exemples on està completament suportat. Encara que el Facebook Live i el YouTube Live accepten l'entrada de so envoltant, el Facebook Live la converteix a estèreo i el YouTube Live la reprodueix només en 2 canals.\n\nEls filtres d'àudio de l'OBS són compatibles amb el so envoltant, encara que no es garanteix la compatibilitat amb connectors VST."
Basic.Settings.Audio.MultichannelWarning.Title="Voleu habilitar el so envoltant?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Xarxa"
Basic.Settings.Advanced.Network.BindToIP="Enllaçar amb"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Activa el nou codi de xarxa"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Mode de baixa latència"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Inhabilita les tecles de drecera quan la finestra principal estigui en primer pla"
Basic.AdvAudio="&Propietats avançades d'àudio"
Basic.AdvAudio.Name="Nom"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Advertència: Els enregistraments desats en MP4 ser
FinalScene.Title="Supressió de l'escena"
FinalScene.Text="Cal que hi hagi almenys una escena."
+NoSources.Title="Cap font"
+NoSources.Text="Sembla que encara no heu afegit cap font de vídeo, de manera que només es mostrarà una pantalla en blanc. Esteu segur que voleu fer això?"
+NoSources.Text.AddSource="Podeu afegir fonts fent clic a la icona «+» sota el quadre Fonts de la finestra principal en qualsevol moment."
+
+ChangeBG="Estableix el color"
+CustomColor="Color personalitzat"
+
+BrowserSource.EnableHardwareAcceleration="Habilita l'acceleració per maquinari al navegador"
+
diff --git a/UI/data/locale/cs-CZ.ini b/UI/data/locale/cs-CZ.ini
index 4b2bc0a..8ae5fd3 100644
--- a/UI/data/locale/cs-CZ.ini
+++ b/UI/data/locale/cs-CZ.ini
@@ -78,6 +78,8 @@ None="Žádný"
StudioMode.Preview="Náhled"
StudioMode.Program="Program"
ShowInMultiview="Zobrazit v Multiview"
+VerticalLayout="Vertikální rozložení"
+Group="Skupina"
AlreadyRunning.Title="OBS je již spuštěno"
AlreadyRunning.Text="OBS již běží! Pokud jste to opravdu nechtěli udělat, tak prosím ukončete ostatní běžící instance programu OBS před spuštěním nové. Pokud máte nastavenu minimalizaci do lišty, tak se prosím podívejte, zda neběží tam."
@@ -404,7 +406,10 @@ Basic.Main.StopReplayBuffer="Zastavit záznam do paměti"
Basic.Main.StoppingReplayBuffer="Zastavuji záznam do paměti..."
Basic.Main.StopStreaming="Zastavit vysílání"
Basic.Main.StoppingStreaming="Zastavuji vysílání..."
-Basic.Main.ForceStopStreaming="Zastavit vysání (bez zpoždění)"
+Basic.Main.ForceStopStreaming="Zastavit vysílání (bez zpoždění)"
+Basic.Main.Group="Skupina %1"
+Basic.Main.GroupItems="Seskupit vybrané"
+Basic.Main.Ungroup="Rozdělit skupinu"
Basic.MainMenu.File="Soubor (&F)"
Basic.MainMenu.File.Export="&Exportovat"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="Nás&troje"
Basic.MainMenu.Help="Pomoc (&H)"
Basic.MainMenu.Help.HelpPortal="&Portál pomoci"
Basic.MainMenu.Help.Website="Navštívit &web"
+Basic.MainMenu.Help.Discord="Připojit se na &Discord server"
Basic.MainMenu.Help.Logs="Soubory záznamu (&L)"
Basic.MainMenu.Help.Logs.ShowLogs="Zobrazit soubory záznamu (&S)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Nahrát aktuální soubor záznamu (&C)"
Basic.MainMenu.Help.Logs.UploadLastLog="Nahrát poslední soubor záznamu (&L)"
Basic.MainMenu.Help.Logs.ViewCurrentLog="Zobrazit aktuální záznam (&V)"
Basic.MainMenu.Help.CheckForUpdates="Zkontrolovat aktualizace"
+Basic.MainMenu.Help.CrashLogs="Hlášení o pádech (&R)"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Zobrazit hlášení o pádech (&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Nahrát pos&lední hlášení"
Basic.Settings.ProgramRestart="Pro projevení nastavení je potřeba restartovat aplikaci."
Basic.Settings.ConfirmTitle="Potvrzení změn"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Vždy minimalizovat do systémov
Basic.Settings.General.SaveProjectors="Ukládat projektory při ukončení"
Basic.Settings.General.SwitchOnDoubleClick="Přejít na scénu po dvojitém kliknutí"
Basic.Settings.General.StudioPortraitLayout="Povolit rozložení na výšku (portrét)"
+Basic.Settings.General.Multiview="Multiview"
+Basic.Settings.General.Multiview.MouseSwitch="Kliknutí pro přechod mezi scénami"
+Basic.Settings.General.Multiview.DrawSourceNames="Zobrazovat názvy scén"
+Basic.Settings.General.Multiview.DrawSafeAreas="Vyznačovat viditelné oblasti (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Rozložení Multiview"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontálně, nahoře"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontálně, dole"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertikálně, vlevo"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertikálně, vpravo"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontálně, nahoře (8 scén)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontálně, dole (8 scén)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertikálně, vlevo (8 scén)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertikálně, vpravo (8 scén)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontálně, nahoře (24 scén)"
Basic.Settings.Stream="Vysílání"
Basic.Settings.Stream.StreamType="Typ vysílání"
@@ -631,9 +645,13 @@ Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Ostré při škálování
Basic.Settings.Audio="Zvuk"
Basic.Settings.Audio.SampleRate="Vzorkovací frekvence"
Basic.Settings.Audio.Channels="Kanály"
+Basic.Settings.Audio.MeterDecayRate="Rychlost útlumu snímače zvuku"
Basic.Settings.Audio.MeterDecayRate.Fast="Rychle"
Basic.Settings.Audio.MeterDecayRate.Medium="Střední (typ I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Pomalu (typ II PPM)"
+Basic.Settings.Audio.PeakMeterType="Typ měřiče špičky"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Špička vzorky"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Pravá špička (Vyšší využití CPU)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="VAROVÁNÍ: Prostorový zvuk je zapnut."
Basic.Settings.Audio.MultichannelWarning="Předtím než začnete vysílat si zkontrolujte, zda vaše vysílací služba podporuje příjem a přehrávání prostorového zvuku. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast jsou příklady služeb, které jej plně podporují. I když Facebook Live a YouTube Live oba podporují příjem prostorového zvuku, Facebook Live jej převede na stereo a YouTube Live přehrává pouze dva kanály.\n\nOBS filtry zvuku jej plně podporují, ale podpora u pluginu VST není garantována."
Basic.Settings.Audio.MultichannelWarning.Title="Povolit prostorový zvuk?"
@@ -674,6 +692,7 @@ Basic.Settings.Advanced.Network="Síť"
Basic.Settings.Advanced.Network.BindToIP="Svázat s adresou"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Použít nový síťový kód"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Režim nízké odezvy"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Zakázat klávesové zkratky, když je hlavní okno aktivní"
Basic.AdvAudio="Rozšířené vlastnosti zvuku"
Basic.AdvAudio.Name="Název"
@@ -748,3 +767,12 @@ OutputWarnings.MP4Recording="Varování: Nahrávky uložené v MP4 nebude možn
FinalScene.Title="Odstranění scény"
FinalScene.Text="Musí existovat alespoň jedna scéna, proto tuto není možno odstranit."
+NoSources.Title="Žádné zdroje"
+NoSources.Text="Vypadá to, že jste zatím nepřidali žádné zdroje obrazu, takže budete vysílat černou obrazovku. Opravdu chcete pokračovat ?"
+NoSources.Text.AddSource="Zdroje můžete kdykoliv přidat tlačítkem + pod seznamem Zdroje v hlavním okně."
+
+ChangeBG="Nastavit barvu"
+CustomColor="Vlastní barva"
+
+BrowserSource.EnableHardwareAcceleration="Zapnout hardwarovou akceleraci pro zdroj prohlížeče"
+
diff --git a/UI/data/locale/da-DK.ini b/UI/data/locale/da-DK.ini
index 15bbbe5..615dcd9 100644
--- a/UI/data/locale/da-DK.ini
+++ b/UI/data/locale/da-DK.ini
@@ -78,6 +78,8 @@ None="Ingen"
StudioMode.Preview="Forhåndsvisning"
StudioMode.Program="Program"
ShowInMultiview="Vis i Multi View"
+VerticalLayout="Lodret layout"
+Group="Gruppér"
AlreadyRunning.Title="OBS kører allerede"
AlreadyRunning.Text="OBS kører allerede! Medmindre dette er tilsigtet, så bedes du lukke enhver eksisterende OBS-proces, inden du forsøger at køre en ny. Hvis du har OBS indstillet til at minimeres sig til systembakken, så tjek venligst om den stadig kører dér."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Stopper Genafspilningsbuffer..."
Basic.Main.StopStreaming="Stop streaming"
Basic.Main.StoppingStreaming="Stopper stream..."
Basic.Main.ForceStopStreaming="Stop streaming (ignorer forsinkelse)"
+Basic.Main.Group="Gruppe %1"
+Basic.Main.GroupItems="Gruppér valgte elementer"
+Basic.Main.Ungroup="Ikke-grupperet"
Basic.MainMenu.File="&Fil"
Basic.MainMenu.File.Export="&Eksport"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="Værk&tøjer"
Basic.MainMenu.Help="&Hjælp"
Basic.MainMenu.Help.HelpPortal="Hjælp og &Portal"
Basic.MainMenu.Help.Website="Besøg &websted"
+Basic.MainMenu.Help.Discord="Join &Discord serveren"
Basic.MainMenu.Help.Logs="&Logfiler"
Basic.MainMenu.Help.Logs.ShowLogs="Vis log-filer (&S)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Upload Aktuelle logfil (&C)"
Basic.MainMenu.Help.Logs.UploadLastLog="Upload Sidste logfil (&L)"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Vis aktuel logfil"
Basic.MainMenu.Help.CheckForUpdates="Tjek for opdateringer"
+Basic.MainMenu.Help.CrashLogs="Nedbrudsrapporter"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="&Vis nedbrudsrapporter"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Up&load seneste nedbrudsrapport"
Basic.Settings.ProgramRestart="Programmet skal genstartes, før disse indstillinger træder i kraft."
Basic.Settings.ConfirmTitle="Bekræfte ændringer"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Minimer altid til processlinjen i
Basic.Settings.General.SaveProjectors="Gem projektorer ved afslutning"
Basic.Settings.General.SwitchOnDoubleClick="Overgang til scenen ved dobbeltklik"
Basic.Settings.General.StudioPortraitLayout="Aktivere stående/liggende layout"
+Basic.Settings.General.Multiview="Multiview"
+Basic.Settings.General.Multiview.MouseSwitch="Klik for at skifte mellem scener"
+Basic.Settings.General.Multiview.DrawSourceNames="Vis scenenavne"
+Basic.Settings.General.Multiview.DrawSafeAreas="Tegn sikre områder (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Multivisningslayout"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horisontal, Top"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horisontal, Bund"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertikal, Venstre"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertikal, Højre"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Vandret, øverst (8 scener)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Vandret, nederst (8 scener)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Lodret, venstre (8 scener)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Lodret, højre (8 scener)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Vandret, øverst (24 scener)"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Streamtype"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Lydmålerudstyring, faldhastighed"
Basic.Settings.Audio.MeterDecayRate.Fast="Hurtig"
Basic.Settings.Audio.MeterDecayRate.Medium="Medium (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Langsom (Type II PPM)"
+Basic.Settings.Audio.PeakMeterType="Peak Meter-type"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Intervalspidsværdi"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Sand spidsværdi (højere CPU-belastning)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="ADVARSEL: Surround Sound-lyd er aktiveret."
Basic.Settings.Audio.MultichannelWarning="Tjek ifm. streamer, om din streaming-tjeneste understøtter både Surround Sound-input og -afspilning. Twitch, Facebook 360 Live, Mixer RTMP og Smashcast er eksempler, hvor surroundlyd understøttes fuldt ud. Selvom Facebook Live og YouTube Live begge accepterer surround input, nedmikser Facebook Live til stereo, og YouTube Live afspiller kun to kanaler.\n\nOBS-lydfiltre er kompatible med surroundlyd, dog er VST-pluginsupport ikke garanteret."
Basic.Settings.Audio.MultichannelWarning.Title="Aktivér Surround Sound-lyd?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Netværk"
Basic.Settings.Advanced.Network.BindToIP="Bind til IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Aktivér ny netværkskode"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Lav forsinkelsestilstand"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Deaktivér genvejstaster, når hovedvinduet er i fokus"
Basic.AdvAudio="Avancerede lydegenskaber"
Basic.AdvAudio.Name="Navn"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Advarsel: MP4-optagelser vil ikke kunne genoprettes
FinalScene.Title="Slet scene"
FinalScene.Text="Der kræves mindst én scene."
+NoSources.Title="Ingen kilder"
+NoSources.Text="Det ser ud til, at du ikke har tilføjet nogen videokilder endnu, så dit output bliver en blank/tom skærm. Sikker på, at du vil gøre dette?"
+NoSources.Text.AddSource="Du kan til enhver tid tilføje kilder ved at klikke på +-ikonet under feltet Kilder i hovedvinduet."
+
+ChangeBG="Sæt farve"
+CustomColor="Brugerdefineret farve"
+
+BrowserSource.EnableHardwareAcceleration="Aktivér Browser Source-hardwareacceleration"
+
diff --git a/UI/data/locale/de-DE.ini b/UI/data/locale/de-DE.ini
index d0f64dc..5051ea5 100644
--- a/UI/data/locale/de-DE.ini
+++ b/UI/data/locale/de-DE.ini
@@ -78,6 +78,8 @@ None="Keine"
StudioMode.Preview="Vorschau"
StudioMode.Program="Programm"
ShowInMultiview="In Multiview anzeigen"
+VerticalLayout="Vertikales Layout"
+Group="Gruppe"
AlreadyRunning.Title="OBS wird bereits ausgeführt"
AlreadyRunning.Text="OBS wird bereits ausgeführt! Bitte beenden Sie alle vorhandenen OBS Instanzen bevor Sie eine neue Instanz starten, es sei denn, Sie tun dies absichtlich. Wenn Sie OBS so eingestellt haben das es sich zum Infobereich minimiert, überprüfen Sie bitte, ob es dort läuft."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Stoppe Replaypuffer..."
Basic.Main.StopStreaming="Streaming stoppen"
Basic.Main.StoppingStreaming="Stoppe Stream..."
Basic.Main.ForceStopStreaming="Streaming stoppen (Verzögerung verwerfen)"
+Basic.Main.Group="Gruppe %1"
+Basic.Main.GroupItems="Ausgewählte Elemente gruppieren"
+Basic.Main.Ungroup="Gruppierung aufheben"
Basic.MainMenu.File="Datei (&F)"
Basic.MainMenu.File.Export="&Exportieren"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="Werkzeuge (&T)"
Basic.MainMenu.Help="&Hilfe"
Basic.MainMenu.Help.HelpPortal="Hilfe&portal"
Basic.MainMenu.Help.Website="&Webseite besuchen"
+Basic.MainMenu.Help.Discord="Tritt unserem &Discordserver bei"
Basic.MainMenu.Help.Logs="&Logdateien"
Basic.MainMenu.Help.Logs.ShowLogs="Logdateien anzeigen (&S)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Upload aktuelle Logdatei (&C)"
Basic.MainMenu.Help.Logs.UploadLastLog="Upload &letzte Logdatei"
Basic.MainMenu.Help.Logs.ViewCurrentLog="Aktuelles Protokoll anzeigen (&V)"
Basic.MainMenu.Help.CheckForUpdates="Auf Updates prüfen"
+Basic.MainMenu.Help.CrashLogs="Abstu&rzberichte"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Ab&sturzberichte anzeigen"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Upload &letzten Absturzbericht"
Basic.Settings.ProgramRestart="Das Programm muss neu gestartet werden, damit die Änderungen wirksam werden."
Basic.Settings.ConfirmTitle="Änderungen bestätigen"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Immer zum Infobereich. statt zur
Basic.Settings.General.SaveProjectors="Projektoren beim Beenden speichern"
Basic.Settings.General.SwitchOnDoubleClick="Übergang zur Szene beim Doppelklicken"
Basic.Settings.General.StudioPortraitLayout="Porträt/vertikales Layout aktivieren"
+Basic.Settings.General.Multiview="Multiview"
+Basic.Settings.General.Multiview.MouseSwitch="Klicken, um zwischen den Szenen umzuschalten"
+Basic.Settings.General.Multiview.DrawSourceNames="Szenennamen anzeigen"
+Basic.Settings.General.Multiview.DrawSafeAreas="Zeige sichere Bereiche (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Multiview Layout"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, oben"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, unten"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertikal, links"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertikal, rechts"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, oben (8 Szenen)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, unten (8 Szenen)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertikal, links (8 Szenen)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertikal, rechts (8 Szenen)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontal, oben (24 Szenen)"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Stream Typ"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Aussteuerungsmesserverfallsrate"
Basic.Settings.Audio.MeterDecayRate.Fast="Schnell"
Basic.Settings.Audio.MeterDecayRate.Medium="Mittel (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Langsam (Type II PPM)"
+Basic.Settings.Audio.PeakMeterType="Peakmeter Typ"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Sample Peak"
+Basic.Settings.Audio.PeakMeterType.TruePeak="True Peak (höhere CPU-Auslastung)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="WARNUNG: Surround-Sound-Audio ist aktiviert."
Basic.Settings.Audio.MultichannelWarning="Überprüfen Sie beim Streaming, ob Ihr Streaming-Dienst sowohl die Einspeisung von Surround-Sound als auch die Surround-Sound-Wiedergabe unterstützt. Twitch, Facebook 360 Live, Mixer RTMP und Smashcast sind Beispiele, bei denen Surround Sound voll unterstützt wird. Obwohl Facebook Live und YouTube Live beide die Surround-Einspeisung akzeptieren, wird Facebook Live auf Stereo heruntergemischt und YouTube Live spielt nur zwei Kanäle ab.\n\nOBS-Audiofilter sind mit Surround-Sound kompatibel, obwohl die VST-Plugin-Unterstützung nicht garantiert ist."
Basic.Settings.Audio.MultichannelWarning.Title="Surround-Sound-Audio aktivieren?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Netzwerk"
Basic.Settings.Advanced.Network.BindToIP="Interface"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Neuen Netzwerkcode aktivieren"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Niedriger Latenzmodus"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Hotkeys deaktivieren, wenn das Hauptfenster im Fokus ist"
Basic.AdvAudio="Erweiterte Audioeigenschaften"
Basic.AdvAudio.Name="Name"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Warnung: Aufnahmen, die in MP4 gespeichert werden,
FinalScene.Title="Szene löschen"
FinalScene.Text="Es muss mindestens eine Szene vorhanden sein."
+NoSources.Title="Keine Quellen"
+NoSources.Text="Offenbar haben Sie noch keine Videoquellen hinzugefügt, so dass Sie nur einen leeren Bildschirm ausgeben werden. Sind Sie sicher, dass Sie das wollen?"
+NoSources.Text.AddSource="Sie können zu jeder Zeit Quellen hinzufügen, indem Sie auf das + Symbol unter dem Quellenfeld im Hauptfenster klicken."
+
+ChangeBG="Farbe auswählen"
+CustomColor="Benutzerdefinierte Farbe"
+
+BrowserSource.EnableHardwareAcceleration="Browser Hardwarebeschleunigung aktivieren"
+
diff --git a/UI/data/locale/el-GR.ini b/UI/data/locale/el-GR.ini
index 17fe7ff..5c9954e 100644
--- a/UI/data/locale/el-GR.ini
+++ b/UI/data/locale/el-GR.ini
@@ -28,12 +28,16 @@ Browse="Αναζήτηση"
Mono="Μονοφωνικό"
Stereo="Στερεοφωνικό"
DroppedFrames="Διακεκομμένα καρέ %1 (%2%)"
+StudioProgramProjector="Προβολέας Πλήρους Οθόνης (Πηγή)"
PreviewProjector="Προβολέας Πλήρους Οθόνης (Προεπισκόπηση)"
SceneProjector="Προβολέας Πλήρους Οθόνης (Σκηνή)"
SourceProjector="Προβολέας Πλήρους Οθόνης (Πηγή)"
+StudioProgramWindow="Παραθύρου προβολής (πρόγραμμα)"
PreviewWindow="Σε παράθυρο στον Προβολέα (Προεπισκόπηση)"
SceneWindow="Σε παράθυρο στον Προβολέα (Σκηνή)"
SourceWindow="Σε παράθυρο στον Προβολέα (Πηγή)"
+MultiviewProjector="MultiView (πλήρης οθόνη)"
+MultiviewWindowed="MultiView (παραθύρου)"
Clear="Καθαρισμός"
Revert="Επαναφορά"
Show="Εμφάνιση"
@@ -69,6 +73,11 @@ Next="Επόμενο"
Back="Προηγούμενο"
Defaults="Προεπιλογές"
HideMixer="Απόκρυψη στον Μίκτη"
+TransitionOverride="Παράκαμψη της μετάβασης"
+None="Καμία"
+StudioMode.Preview="Προεπισκόπηση"
+StudioMode.Program="Πρόγραμμα"
+ShowInMultiview="Εμφάνιση σε Multiview"
AlreadyRunning.Title="Το OBS εκτελείται ήδη"
AlreadyRunning.Text="Το OBS εκτελείται ήδη! Εκτός αν θέλατε να το κάνετε αυτό, παρακαλούμε τερματίστε τις τρέχουσες διεργασίες OBS πριν προσπαθήσετε να εκκινήσετε μια καινούρια. Εάν έχετε ρυθμίσει το OBS να ελαχιστοποιείται στην γραμμή εργαλείων, παρακαλούμε να ελένξετε αν τρέχει ήδη εκεί."
@@ -221,6 +230,7 @@ Output.RecordNoSpace.Msg="Δεν υπάρχει επαρκής χώρος στο
Output.RecordError.Title="Σφάλμα εγγραφής"
Output.RecordError.Msg="Παρουσιάστηκε ένα αδιευκρίνιστο σφάλμα κατά την εγγραφή."
Output.ReplayBuffer.NoHotkey.Title="Δεν έχει επιλεχθεί hotkey!"
+Output.ReplayBuffer.NoHotkey.Msg="Καμμία αποθήκευση συντόμευσης για επανάληψη buffer. Παρακαλώ ορίστε την συντόμευση για «Αποθήκευση» καί χρήση για την αποθήκευση επανάληψης ηχογραφήσεων."
Output.BadPath.Title="Λάθος Διαδρομή Αρχείου"
Output.BadPath.Text="Η προκαθορισμένη διαδρομή αρχείου δεν ειναι έγκυρη. Παρακαλώ ελέγξτε τις ρυθμίσεις σας για να επιβεβαιώσετε ότι έχει οριστεί μια έγκυρη διαδρομή αρχείου."
@@ -302,6 +312,8 @@ AddProfile.Text="Παρακαλώ εισάγετε το όνομα του προ
RenameProfile.Title="Μετονομασία Προφίλ"
+Basic.Main.MixerRename.Title="Μετονομασία της πηγής ήχου"
+Basic.Main.MixerRename.Text="Παρακαλώ εισάγετε το όνομα της πηγής ήχου"
Basic.Main.PreviewDisabled="Η προεπισκόπηση είναι απενεργοποιημένη"
@@ -457,6 +469,7 @@ Basic.MainMenu.SceneCollection.Exists="Η συλλογή σκήνων υπάρχ
Basic.MainMenu.Tools="&Εργαλεία"
Basic.MainMenu.Help="Βοήθεια(&H)"
+Basic.MainMenu.Help.HelpPortal="Πύλη βοήθειας"
Basic.MainMenu.Help.Website="Μετάβαση στην &Ιστοσελίδα"
Basic.MainMenu.Help.Logs="Αρχεία(&) Καταγραφής"
Basic.MainMenu.Help.Logs.ShowLogs="Εμφάνιση(&S) Αρχείων Καταγραφής"
@@ -476,6 +489,25 @@ Basic.Settings.General.EnableAutoUpdates="Αυτόματος έλεγχος εν
Basic.Settings.General.OpenStatsOnStartup="Άνοιγμα παραθύρου στατιστικών κατά την εκκίνηση"
Basic.Settings.General.WarnBeforeStartingStream="Εμφάνιση παραθύρου επιβεβαίωσης κατά την εκκίνηση των streams"
Basic.Settings.General.WarnBeforeStoppingStream="Εμφάνιση παραθύρου επιβεβαίωσης κατά τη διακοπή των streams"
+Basic.Settings.General.Projectors="Προβολείς"
+Basic.Settings.General.HideProjectorCursor="Απόκρυψη τού δρομέα πάνω από τούς προβολείς"
+Basic.Settings.General.ProjectorAlwaysOnTop="Προβολείς πάντα στην κορυφή"
+Basic.Settings.General.Snapping="Ευθυγράμμιση πηγής"
+Basic.Settings.General.ScreenSnapping="Προσκόλληση πηγών στην άκρη της οθόνης"
+Basic.Settings.General.CenterSnapping="Προσκόλληση πηγών οριζόντια και κάθετα στο κέντρο"
+Basic.Settings.General.SourceSnapping="Προσκόλληση πηγών σε άλλες πηγές"
+Basic.Settings.General.SnapDistance="Ευαισθησία προσκόλλησης"
+Basic.Settings.General.RecordWhenStreaming="Αυτόματη καταγραφή κατά την ροή"
+Basic.Settings.General.KeepRecordingWhenStreamStops="Διατηρήσετε καταγραφών όταν σταματά η ροή"
+Basic.Settings.General.ReplayBufferWhileStreaming="Αυτόματη εκκίνηση replay buffer κατά τη ροή"
+Basic.Settings.General.KeepReplayBufferStreamStops="Διατήρηση επανάληψης buffer όταν σταματά η ροή"
+Basic.Settings.General.SysTray="System Tray"
+Basic.Settings.General.SysTrayWhenStarted="Ελαχιστοποίηση στο System Tray κατά την εκκίνηση"
+Basic.Settings.General.SystemTrayHideMinimize="Συνεχής ελαχιστοποίηση στο System Tray αντί στην γραμμή εργασιών"
+Basic.Settings.General.SaveProjectors="Αποθήκευση προβολής στην έξοδο"
+Basic.Settings.General.SwitchOnDoubleClick="Μετάβαση στην σκηνή με διπλό κλικ"
+Basic.Settings.General.StudioPortraitLayout="Ενεργοποίηση πορτρέτου/κατακόρυφης διάταξης"
+Basic.Settings.General.MultiviewLayout="MultiView διάταξη"
Basic.Settings.Stream="Μετάδοση"
Basic.Settings.Stream.StreamType="Τύπος Μετάδοσης"
@@ -485,24 +517,37 @@ Basic.Settings.Output.Format="Μορφή Καταγραφής"
Basic.Settings.Output.Encoder="Κωδικοποιητής"
Basic.Settings.Output.SelectDirectory="Επιλέξτε κατάλογο καταγραφής"
Basic.Settings.Output.SelectFile="Επιλέξτε αρχείο καταγραφής"
+Basic.Settings.Output.EnforceBitrate="Επιβολή ροής υπηρεσία bitrate ορίων"
Basic.Settings.Output.Mode="Λειτουργία Εξόδου"
Basic.Settings.Output.Mode.Simple="Απλό"
Basic.Settings.Output.Mode.Adv="Σύνθετες επιλογές"
Basic.Settings.Output.Mode.FFmpeg="Έξοδος FFmpeg"
+Basic.Settings.Output.UseReplayBuffer="Ενεργοποίηση επανάληψης Buffer"
Basic.Settings.Output.ReplayBuffer.SecondsMax="Μέγιστος Χρόνος Επανάληψης (Δευτερόλεπτα)"
Basic.Settings.Output.ReplayBuffer.MegabytesMax="Μέγιστη Μνήμη (Megabytes)"
Basic.Settings.Output.ReplayBuffer.Estimate="Εκτιμώμενη χρήση μνήμης: %1 MB"
Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Δεν είναι δυνατή η εκτίμηση της χρήσης μνήμης. Ορίστε μέγιστο όριο μνήμης."
+Basic.Settings.Output.ReplayBuffer.HotkeyMessage="(Σημείωση: Βεβαιωθείτε ότι ορίσατε μία συντόμευση για το buffer στην ενότητα συντομέυσεων)"
+Basic.Settings.Output.ReplayBuffer.Prefix="Πρόθεμα ονόματος αρχείου Buffer Replay"
Basic.Settings.Output.ReplayBuffer.Suffix="Επίθεμα"
Basic.Settings.Output.Simple.SavePath="Διαδρομή Καταγραφής"
Basic.Settings.Output.Simple.RecordingQuality="Ποιότητα Εγγραφής"
+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="Προειδοποίηση: Το streaming βίντεο bitrate θα οριστεί %1, που είναι το ανώτερο όριο για την τρέχουσα υπηρεσία συνεχούς ροής. Εάν είστε βέβαιοι ότι θέλετε να πάτε πάνω από %1, Ενεργοποίηση επιλογών προηγμένο κωδικοποιητή και uncheck «Επιβολή streaming υπηρεσία bitrate όρια»."
+Basic.Settings.Output.Simple.Warn.AudioBitrate="Προειδοποίηση: Η ροή ήχου bitrate θα οριστεί %1, που είναι το ανώτερο όριο για την τρέχουσα υπηρεσία συνεχούς ροής. Εάν είστε βέβαιοι ότι θέλετε να πάτε πάνω από %1, Ενεργοποίηση επιλογών προηγμένο κωδικοποιητή και uncheck «Επιβολή streaming υπηρεσία bitrate όρια»."
+Basic.Settings.Output.Simple.Warn.Encoder="Προσοχή: Η εγγραφή με έναν κωδικοποιητή λογισμικού σε διαφορετική ποιότητα από την ροή θα απαιτήσει πρόσθετη χρήση της CPU, αν μπορείτε να πραγματοποιήσετε την ροή και την εγγραφή την ίδια στιγμή."
+Basic.Settings.Output.Simple.Warn.Lossless="Προειδοποίηση: Η μη απωλεστική ποιότητα δημιουργεί τρομερά μεγάλο μέγεθος των αρχείων! Η ποιότητα χωρίς απώλειες ποιότητας θα καταλάβει 7 gigabyte χώρο στον σκληρό δίσκο ανά λεπτό, σε υψηλές αναλύσεις και framerates. Χωρίς απώλειες δεν συνιστάται για μεγάλες ηχογραφήσεις, εκτός αν έχετε πολύ χώρο στον σκληρό δίσκο."
+Basic.Settings.Output.Simple.Warn.Lossless.Msg="Είστε σίγουρος ότι θέλετε να το χρησιμοποιήσετε χωρίς απώλειες ποιότητας;"
+Basic.Settings.Output.Simple.Warn.Lossless.Title="Χωρίς απώλειες ποιότητας προειδοποίηση!"
+Basic.Settings.Output.Simple.Warn.MultipleQSV="Προειδοποίηση: Δεν μπορείτε να χρησιμοποιήσετε πολλαπλούς ξεχωριστούς QSV κωδικοποιητές κατά την ροή και την εγγραφή την ίδια στιγμή. Αν θέλετε κάνετε stream και να καταγράψετε ταυτόχρονα, παρακαλείστε να αλλάξετε είτε την εγγραφή η την ροή τού κωδικοποιητή."
Basic.Settings.Output.Simple.Encoder.Software="Λογισμικό (x264)"
Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Υλικού (QSV)"
Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Υλικού (AMD)"
Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Υλικού (NVENC)"
+Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Λογισμικό (x264 χαμηλή χρήση CPU preset, αυξάνει το μέγεθος τού αρχείου)"
Basic.Settings.Output.VideoBitrate="Ρυθμός Bit του Βίντεο"
Basic.Settings.Output.AudioBitrate="Ρυθμός Bit του Ήχου"
Basic.Settings.Output.Reconnect="Αυτόματη Επανασύνδεση"
@@ -522,12 +567,15 @@ Basic.Settings.Output.Adv.Audio.Track1="Κομμάτι 1"
Basic.Settings.Output.Adv.Audio.Track2="Κομμάτι 2"
Basic.Settings.Output.Adv.Audio.Track3="Κομμάτι 3"
Basic.Settings.Output.Adv.Audio.Track4="Κομμάτι 4"
+Basic.Settings.Output.Adv.Audio.Track5="Κομμάτι 5"
+Basic.Settings.Output.Adv.Audio.Track6="Κομμάτι 6"
Basic.Settings.Output.Adv.Recording="Εγγραφή"
Basic.Settings.Output.Adv.Recording.Type="Τύπος"
Basic.Settings.Output.Adv.Recording.Type.Standard="Κανονικός"
Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Προσαρμοσμένη Έξοδος (FFmpeg)"
Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Χρήση κωδικοποιητή ροής)"
+Basic.Settings.Output.Adv.Recording.Filename="Μορφοποίηση ονόματος αρχείου"
Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Αντικατάσταση εάν το αρχείο υπάρχει"
Basic.Settings.Output.Adv.FFmpeg.Type="Τύπος εξόδου FFmpeg"
Basic.Settings.Output.Adv.FFmpeg.Type.URL="Έξοδος σε διεύθυνση URL"
@@ -553,9 +601,12 @@ Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Προβολή όλων τω
FilenameFormatting.completer="%AAXX-%MM-%ΗΗ %ωω-%λλ-%δδ\n%ΧΧ-%ΜΜ-%ΗΗ %ωω-%λλ-%δδ\n%Χ-%μ-%η %Ω-%Λ-%Δ\n%χ-%μ-%η %Ω-%Λ-%Δ\n%α %Χ-%μ-%η %Ω-%Λ-%Δ\n%Α %Χ-%μ-%η %Ω-%Λ-%Δ\n%Χ-%μ-%η %Ω-%Λ-%Δ\n%Χ-%Μ-%η %Ω-%Λ-%Δ\n%Χ-%μ-%η %Ω-%Λ-%Δ-%p\n%Χ-%μ-%μ %Ω-%Λ-%Δ-%ζ\n%Χ-%μ-%η %Ω-%Λ-%Δ-%Ζ"
+FilenameFormatting.TT="%CCYY έτους, τέσσερις digits\n%YY έτος, τα δύο τελευταία ψηφία (00-99) \n%MM μήνα ως ένα δεκαδικό αριθμό (01-12) \n%DD ημέρα του μήνα, μηδέν επένδυση (01-31)\n%hh ώρα στο 24h μορφή (00-23) \n%mm λεπτό (00-59) \n%ss δεύτερος (00-61) \n%% μια%a sign\n % συντετμημένη καθημερινές name\n%A πλήρη ημέρα της εβδομάδας name\n%b συντετμημένη μήνα name\n%B πλήρη μήνα name\n%d ημέρα του μήνα, μηδέν-γεμισμένος (01-31) \n%H ώρα στο 24h μορφή (00-23) \n%I ώρα σε μορφή 12h (01-12)\n%m μήνας ως δεκαδικός αριθμός (01-12)\n%M λεπτά (00-59) \n%p π. μ. ή μ. μ. designation\n%S δεύτερο (00-61) \n%y έτος, τελευταία δύο ψηφία (00-99)\n%Y Year\n%z ISO 8601 μετατόπιση από UTC ή timezone\n όνομα ή όνομα ζώνης ώρας%Z abbreviation\n ή abbreviation\n"
Basic.Settings.Video="Βίντεο"
Basic.Settings.Video.Adapter="Προσαρμογέας Βίντεο:"
+Basic.Settings.Video.BaseResolution="Βάση (Καμβάς) Ανάλυση"
+Basic.Settings.Video.ScaledResolution="Ανάλυση εξόδου (κλίμακα)"
Basic.Settings.Video.DownscaleFilter="Φίλτρο Σμίκρυνσης:"
Basic.Settings.Video.DisableAeroWindows="Απενεργοποίηση Aero (Windows μόνο)"
Basic.Settings.Video.FPS="Καρέ ανά δευτερόλεπτο (FPS):"
@@ -576,23 +627,48 @@ Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Οξυμμένη κλιμ
Basic.Settings.Audio="Ήχος"
Basic.Settings.Audio.SampleRate="Ρυθμός Δειγματοληψίας"
Basic.Settings.Audio.Channels="Κανάλια"
+Basic.Settings.Audio.MeterDecayRate="Συντελεστής απόσβεσης ήχου μέτρησης"
+Basic.Settings.Audio.MeterDecayRate.Fast="Γρήγορος"
+Basic.Settings.Audio.MeterDecayRate.Medium="Μεσαίος (τύπος Ι PPM)"
+Basic.Settings.Audio.MeterDecayRate.Slow="Αργός (Τύπος ΙΙ PPM)"
+Basic.Settings.Audio.MultiChannelWarning.Enabled="Προειδοποίηση: Ο ήχος Surround είναι ενεργοποιημένος."
+Basic.Settings.Audio.MultichannelWarning="Κατά το streaming, ελέγξτε αν η υπηρεσία streaming υποστηρίζει ήχο surround δύο ηχείων και ήχο surround αναπαραγωγής. Το Twitch, το Facebook 360 Live, το μίξερ RTMP καί το Smashcast αποτελούν παραδείγματα όπου ο surround ήχος υποστηρίζεται πλήρως. Αν και το Facebook Live και το YouTube Live αποδέχεστε τον ήχο surround, το Facebook Live κατεβάζει τον σε stereo, και το YouTube Live υποστηρίζει μόνο δύο φίλτρα ήχου.\n\nΤο OBS είναι συμβατό με ήχο surround, αν καί δεν είναι εγγυημένη η υποστήριξη για plugins VST."
+Basic.Settings.Audio.MultichannelWarning.Title="Ενεργοποίηση ήχου surround;"
+Basic.Settings.Audio.MultichannelWarning.Confirm="Είναι βέβαιοι ότι θέλετε να ενεργοποιήσετε τον ήχο surround;"
Basic.Settings.Audio.DesktopDevice="Συσκευή Ήχου Επιφάνειας"
Basic.Settings.Audio.DesktopDevice2="Συσκευή Ήχου Επιφάνειας 2"
Basic.Settings.Audio.AuxDevice="Μικρόφωνο/Αuxillary Συσκευή Ήχου"
Basic.Settings.Audio.AuxDevice2="Μικρόφωνο/Αuxillary Συσκευή Ήχου 2"
Basic.Settings.Audio.AuxDevice3="Μικρόφωνο/Αuxillary Συσκευή Ήχου 3"
+Basic.Settings.Audio.EnablePushToMute="Ενεργοποίηση της ώθησης-γιά-σίγαση"
+Basic.Settings.Audio.PushToMuteDelay="Καθυστέρηση ώθησης-γιά-σίγαση"
+Basic.Settings.Audio.EnablePushToTalk="Ενεργοποίηση Push-to-talk"
+Basic.Settings.Audio.PushToTalkDelay="Push-to-talk καθυστέρηση"
+Basic.Settings.Audio.UnknownAudioDevice="[Η συσκευή δεν είναι συνδεδεμένη η δεν είναι διαθέσιμη]"
Basic.Settings.Advanced="Σύνθετες επιλογές"
+Basic.Settings.Advanced.General.ProcessPriority="Προτεραιότητα διαδικασίας"
+Basic.Settings.Advanced.General.ProcessPriority.High="Υψηλή"
+Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Πάνω από το φυσιολογικό"
+Basic.Settings.Advanced.General.ProcessPriority.Normal="Κανονική"
+Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Κάτω από την κανονική"
+Basic.Settings.Advanced.General.ProcessPriority.Idle="Σε αδράνεια"
Basic.Settings.Advanced.FormatWarning="Προσοχή: Μορφές χρώματος εκτός του NV12 προορίζονται κυρίως για καταγραφή, και δεν συνιστώνται κατά τη μετάδοση. Ενδέχεται να υπάρξει αυξημένη χρήση της CPU λόγω μετατροπής μορφής χρώματος."
Basic.Settings.Advanced.Audio.BufferingTime="Χρόνος buffering ήχου"
Basic.Settings.Advanced.Video.ColorFormat="Μορφή Χρώματος"
+Basic.Settings.Advanced.Video.ColorSpace="Χώρος χρωμάτων YUV"
+Basic.Settings.Advanced.Video.ColorRange="Ποικιλία χρωμάτων YUV"
Basic.Settings.Advanced.Video.ColorRange.Partial="Μερικό"
Basic.Settings.Advanced.Video.ColorRange.Full="Πλήρες"
+Basic.Settings.Advanced.Audio.MonitoringDevice="Συσκευή παρακολούθησης ήχου"
Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Προεπιλεγμένη"
+Basic.Settings.Advanced.Audio.DisableAudioDucking="Απενεργοποίηση σίγασης ήχου"
+Basic.Settings.Advanced.StreamDelay="Καθυστέρηση ροής"
Basic.Settings.Advanced.StreamDelay.Duration="Διάρκεια (δευτερόλεπτα)"
Basic.Settings.Advanced.StreamDelay.Preserve="Διατήρηση σημείου αποκοπής (αύξηση καθυστέρησης) κατά την επανασύνδεση"
Basic.Settings.Advanced.StreamDelay.MemoryUsage="Εκτιμώμενη Χρήση Μνήμης: %1 MB"
Basic.Settings.Advanced.Network="Δίκτυο"
+Basic.Settings.Advanced.Network.BindToIP="Σύνδεση με IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Ενεργοποίηση νέου κώδικα δικτύωσης"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Λειτουργία χαμηλής καθυστέρησης"
@@ -602,9 +678,14 @@ Basic.AdvAudio.Volume="Ένταση (%)"
Basic.AdvAudio.Mono="Αποκωδικοποίηση σε Mono"
Basic.AdvAudio.Panning="Πανοραμικό"
Basic.AdvAudio.SyncOffset="Μετατόπιση Συγχρονισμού (ms)"
+Basic.AdvAudio.Monitoring="Ηχητική παρακολούθηση"
+Basic.AdvAudio.Monitoring.None="Monitor Off"
+Basic.AdvAudio.Monitoring.MonitorOnly="Μόνο η οθόνη (σίγαση εξόδου)"
+Basic.AdvAudio.Monitoring.Both="Παρακολούθηση και έξοδος"
Basic.AdvAudio.AudioTracks="Κομμάτια"
Basic.Settings.Hotkeys="Πλήκτρα συντόμευσης"
+Basic.Settings.Hotkeys.Pair="Συνδυασμοί πλήκτρων που μοιράζεται με το «%1» που ενεργούν ως εναλλαγή"
Basic.Hotkeys.SelectScene="Μετάβαση σε σκηνή"
@@ -644,12 +725,26 @@ Hotkeys.AppleKeypadNum="%1 (Keypad)"
Hotkeys.AppleKeypadMultiply="* (Keypad)"
Hotkeys.AppleKeypadDivide="/ (Keypad)"
Hotkeys.AppleKeypadAdd="+ (Keypad)"
+Hotkeys.AppleKeypadSubtract="- (Αριθμητικό πληκτρολόγιο)"
+Hotkeys.AppleKeypadDecimal=". (Πληκτρολόγιο)"
+Hotkeys.AppleKeypadEqual="= (Αριθμητικό πληκτρολόγιο)"
+Hotkeys.MouseButton="%1 ποντίκι"
Mute="Σίγαση"
Unmute="Κατάργηση σίγασης"
+Push-to-mute="Ώθηση-για-σίγαση"
+Push-to-talk="Πίεση και ομιλία"
SceneItemShow="Εμφάνιση '%1'"
SceneItemHide="Απόκρυψη '%1'"
+OutputWarnings.NoTracksSelected="Πρέπει να επιλέξετε τουλάχιστον ένα κομμάτι"
+OutputWarnings.MultiTrackRecording="Προειδοποίηση: Ορισμένες μορφές (όπως FLV) δεν υποστηρίζουν πολλαπλά κομμάτια ανά εγγραφή"
+OutputWarnings.MP4Recording="Προειδοποίηση: Οι ηχογραφήσεις που έχουν αποθηκευτεί σε MP4 θα είναι αδιόρθωτες, αν το αρχείο δεν είναι δυνατόν να ολοκληρωθεί (π.χ. λόγω BSODs, απώλεια ισχύος, κλπ.). Αν θέλετε να καταγράψετε πολλαπλά κομμάτια ήχου χρησιμοποιήστε το MKV και remux καταγραφής για mp4, αφού τελειώσει (αρχείο-> Remux ηχογραφήσεις)"
+
+FinalScene.Title="Διαγραφή σκηνής"
+FinalScene.Text="Πρέπει να υπάρχει τουλάχιστον μία σκηνή."
+
+
diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
index 89bd0b1..ca6d6a7 100644
--- a/UI/data/locale/en-US.ini
+++ b/UI/data/locale/en-US.ini
@@ -83,6 +83,8 @@ None="None"
StudioMode.Preview="Preview"
StudioMode.Program="Program"
ShowInMultiview="Show in Multiview"
+VerticalLayout="Vertical Layout"
+Group="Group"
# warning if program already open
AlreadyRunning.Title="OBS is already running"
@@ -457,6 +459,9 @@ Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..."
Basic.Main.StopStreaming="Stop Streaming"
Basic.Main.StoppingStreaming="Stopping Stream..."
Basic.Main.ForceStopStreaming="Stop Streaming (discard delay)"
+Basic.Main.Group="Group %1"
+Basic.Main.GroupItems="Group Selected Items"
+Basic.Main.Ungroup="Ungroup"
# basic mode file menu
Basic.MainMenu.File="&File"
@@ -529,6 +534,7 @@ Basic.MainMenu.Tools="&Tools"
Basic.MainMenu.Help="&Help"
Basic.MainMenu.Help.HelpPortal="Help &Portal"
Basic.MainMenu.Help.Website="Visit &Website"
+Basic.MainMenu.Help.Discord="Join &Discord Server"
Basic.MainMenu.Help.Logs="&Log Files"
Basic.MainMenu.Help.Logs.ShowLogs="&Show Log Files"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Upload &Current Log File"
@@ -570,11 +576,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Always minimize to system tray in
Basic.Settings.General.SaveProjectors="Save projectors on exit"
Basic.Settings.General.SwitchOnDoubleClick="Transition to scene when double-clicked"
Basic.Settings.General.StudioPortraitLayout="Enable portrait/vertical layout"
+Basic.Settings.General.Multiview="Multiview"
+Basic.Settings.General.Multiview.MouseSwitch="Click to switch between scenes"
+Basic.Settings.General.Multiview.DrawSourceNames="Show scene names"
+Basic.Settings.General.Multiview.DrawSafeAreas="Draw safe areas (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Multiview Layout"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, Top"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, Bottom"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, Left"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, Right"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, Top (8 Scenes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, Bottom (8 Scenes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, Left (8 Scenes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, Right (8 Scenes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontal, Top (24 Scenes)"
# basic mode 'stream' settings
Basic.Settings.Stream="Stream"
@@ -611,7 +622,6 @@ Basic.Settings.Output.Simple.Warn.Encoder="Warning: Recording with a software en
Basic.Settings.Output.Simple.Warn.Lossless="Warning: Lossless quality generates tremendously large file sizes! Lossless quality can use upward of 7 gigabytes of disk space per minute at high resolutions and framerates. Lossless is not recommended for long recordings unless you have a very large amount of disk space available."
Basic.Settings.Output.Simple.Warn.Lossless.Msg="Are you sure you want to use lossless quality?"
Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless quality warning!"
-Basic.Settings.Output.Simple.Warn.MultipleQSV="Warning: You cannot use multiple separate QSV encoders when streaming and recording at the same time. If you want to stream and record at the same time, please change either the recording encoder or the stream encoder."
Basic.Settings.Output.Simple.Encoder.Software="Software (x264)"
Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)"
Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardware (AMD)"
@@ -707,6 +717,9 @@ Basic.Settings.Audio.MeterDecayRate="Audio Meter Decay Rate"
Basic.Settings.Audio.MeterDecayRate.Fast="Fast"
Basic.Settings.Audio.MeterDecayRate.Medium="Medium (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Slow (Type II PPM)"
+Basic.Settings.Audio.PeakMeterType="Peak Meter Type"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Sample Peak"
+Basic.Settings.Audio.PeakMeterType.TruePeak="True Peak (Higher CPU usage)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="WARNING: Surround sound audio is enabled."
Basic.Settings.Audio.MultichannelWarning="If streaming, check to see if your streaming service supports both surround sound ingest and surround sound playback. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast are examples where surround sound is fully supported. Although Facebook Live and YouTube Live both accept surround ingest, Facebook Live downmixes to stereo, and YouTube Live plays only two channels.\n\nOBS audio filters are compatible with surround sound, though VST plugin support isn't guaranteed."
Basic.Settings.Audio.MultichannelWarning.Title="Enable surround sound audio?"
@@ -748,6 +761,7 @@ Basic.Settings.Advanced.Network="Network"
Basic.Settings.Advanced.Network.BindToIP="Bind to IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Enable new networking code"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Low latency mode"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Disable hotkeys when main window is in focus"
# advanced audio properties
Basic.AdvAudio="Advanced Audio Properties"
@@ -831,3 +845,15 @@ OutputWarnings.MP4Recording="Warning: Recordings saved to MP4 will be unrecovera
# deleting final scene
FinalScene.Title="Delete Scene"
FinalScene.Text="There needs to be at least one scene."
+
+# no sources
+NoSources.Title="No Sources"
+NoSources.Text="It looks like you haven't added any video sources yet, so you will only be outputting a blank screen. Are you sure you want to do this?"
+NoSources.Text.AddSource="You can add sources by clicking the + icon under the Sources box in the main window at any time."
+
+# Scene item color selection
+ChangeBG="Set Color"
+CustomColor="Custom Color"
+
+# Global settings for the browser source
+BrowserSource.EnableHardwareAcceleration="Enable Browser Source Hardware Acceleration"
diff --git a/UI/data/locale/es-ES.ini b/UI/data/locale/es-ES.ini
index ac279b0..df65678 100644
--- a/UI/data/locale/es-ES.ini
+++ b/UI/data/locale/es-ES.ini
@@ -78,6 +78,8 @@ None="Ninguno"
StudioMode.Preview="Vista previa"
StudioMode.Program="Programa"
ShowInMultiview="Mostrar en vista múltiple"
+VerticalLayout="Interfaz Vertical"
+Group="Grupo"
AlreadyRunning.Title="OBS ya se está ejecutando"
AlreadyRunning.Text="¡OBS ya se está ejecutando! A no ser que quieras hacer esto, por favor, cierra todas las ventanas de OBS antes de intentar iniciar una nueva. Si tienes configurado OBS para que se minimize a la barra de tareas, prueba a ver si sigue ejecutándose ahí."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Deteniendo la reproducción del búfer..."
Basic.Main.StopStreaming="Detener Transmisión"
Basic.Main.StoppingStreaming="Deteniendo la trasmisión..."
Basic.Main.ForceStopStreaming="Parar Transmisión (descartar retardo)"
+Basic.Main.Group="Grupo %1"
+Basic.Main.GroupItems="Agrupar los elementos seleccionados"
+Basic.Main.Ungroup="Desagrupar"
Basic.MainMenu.File="&Archivo"
Basic.MainMenu.File.Export="&Exportar"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Herramientas"
Basic.MainMenu.Help="&Ayuda"
Basic.MainMenu.Help.HelpPortal="Ayuda (&P)"
Basic.MainMenu.Help.Website="Visitar Sitio &Web"
+Basic.MainMenu.Help.Discord="Unirse a un servidor &Discord"
Basic.MainMenu.Help.Logs="&Archivos de registro"
Basic.MainMenu.Help.Logs.ShowLogs="Mostrar archivo&s de registro"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Carga &de archivo de registro actual"
Basic.MainMenu.Help.Logs.UploadLastLog="Carga del &último archivo de registro"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Ver registro actual"
Basic.MainMenu.Help.CheckForUpdates="Comprobar Actualizaciones"
+Basic.MainMenu.Help.CrashLogs="Informes de &error"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="&Mostrar informes de error"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Subir el &último informe de error"
Basic.Settings.ProgramRestart="El programa debe reiniciarse para que esta configuración surta efecto."
Basic.Settings.ConfirmTitle="Confirmar cambios"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Minimizar siempre en la bandeja d
Basic.Settings.General.SaveProjectors="Guardar los proyectores al salir"
Basic.Settings.General.SwitchOnDoubleClick="Transición a la escena cuando se hace doble clic"
Basic.Settings.General.StudioPortraitLayout="Habilitar la disposición horizontal/vertical"
+Basic.Settings.General.Multiview="Vista Múltiple"
+Basic.Settings.General.Multiview.MouseSwitch="Click para cambiar entre escenas"
+Basic.Settings.General.Multiview.DrawSourceNames="Mostrar nombres de las escenas"
+Basic.Settings.General.Multiview.DrawSafeAreas="Dibujar áreas seguras (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Prueba de multivisión"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, parte superior"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, parte inferior"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, izquierda"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, derecha"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, superior (8 Escenas)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, inferior (8 Escenas)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, izquierda (8 Escenas)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, derecha (8 Escenas)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontal, superior (24 Escenas)"
Basic.Settings.Stream="Emision"
Basic.Settings.Stream.StreamType="Tipo de Emision"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Tasa de decaimiento del medidor de audio"
Basic.Settings.Audio.MeterDecayRate.Fast="Rápida"
Basic.Settings.Audio.MeterDecayRate.Medium="Media (PPM de tipo I)"
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="Si se está transmitiendo, compruebe si su servicio de transmisión admite la ingesta de sonido envolvente y la reproducción de sonido envolvente. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast son ejemplos en los que el sonido envolvente es totalmente compatible. Aunque Facebook Live y YouTube Live aceptan la ingesta surround, Facebook Live mezcla a estéreo y YouTube Live solo reproduce dos canales.\n\nLos filtros de audio OBS son compatibles con sonido envolvente, aunque no se garantiza el soporte de complementos VST."
Basic.Settings.Audio.MultichannelWarning.Title="¿Habilitar el audio de sonido envolvente?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Red"
Basic.Settings.Advanced.Network.BindToIP="Enlazar con IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Habilitar el nuevo código de red"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Modo de baja latencia"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Deshabilitar teclas de acceso rápido cuando la ventana principal se encuentre activa"
Basic.AdvAudio="Propiedades de Audio avanzadas"
Basic.AdvAudio.Name="Nombre"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="ADVERTENCIA: Las grabaciones guardadas en MP4 será
FinalScene.Title="Eliminar escena"
FinalScene.Text="Debe haber al menos una escena."
+NoSources.Title="Sin recursos"
+NoSources.Text="Parece que no has añadido ningún recurso de vídeo aún, así que estarás emitiendo una pantalla en blanco. ¿Estás seguro de que quieres hacer esto?"
+NoSources.Text.AddSource="Puedes añadir recursos haciendo click en el icono de \"+\" debajo del menú Recursos en la ventana principal en cualquier momento."
+
+ChangeBG="Establecer color"
+CustomColor="Color personalizado"
+
+BrowserSource.EnableHardwareAcceleration="Habilitar Aceleración de Hardware de Recurso en el Navegador"
+
diff --git a/UI/data/locale/et-EE.ini b/UI/data/locale/et-EE.ini
index 75a3c84..694f6ad 100644
--- a/UI/data/locale/et-EE.ini
+++ b/UI/data/locale/et-EE.ini
@@ -31,6 +31,7 @@ DroppedFrames="Vahele jäetud kaadreid %1 (%2%)"
PreviewProjector="Projektor täisekraanil (eelvaade)"
SceneProjector="Projektor täisekraanil (stseen)"
SourceProjector="Projektor täisekraanil (allikas)"
+MultiviewProjector="Mitmikvaade (täisekraanil)"
Clear="Eemalda"
Revert="Tühista"
Show="Näita"
@@ -59,20 +60,30 @@ Export="Ekspordi"
Copy="Kopeeri"
Paste="Kleebi"
PasteReference="Kleebi (Viide)"
+PasteDuplicate="Kleebi (Koopia)"
Next="Edasi"
Back="Tagasi"
+StudioMode.Preview="Eelvaade"
AlreadyRunning.Title="OBS juba töötab"
+BandwidthTest.Region.US="Ameerika Ühendriigid"
+BandwidthTest.Region.EU="Euroopa"
+BandwidthTest.Region.Asia="Aasia"
+BandwidthTest.Region.Other="Muu"
+Basic.AutoConfig.ApplySettings="Rakenda muutused"
Basic.AutoConfig.StreamPage.Service="Teenus"
Basic.AutoConfig.StreamPage.Server="Server"
+Basic.AutoConfig.StreamPage.StreamKey="Voogedastuse võti"
Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)"
+Basic.Stats="Statistika"
Basic.Stats.CPUUsage="CPU kasutus"
Basic.Stats.Status.Inactive="Inaktiivne"
+Basic.Stats.Bitrate="Bitikiirus"
Updater.Title="Uus värskendus saadaval"
Updater.Text="Uus värskendus on saadaval:"
@@ -465,3 +476,6 @@ Mute="Vaigista"
+
+
+
diff --git a/UI/data/locale/eu-ES.ini b/UI/data/locale/eu-ES.ini
index 4e80c29..51e163d 100644
--- a/UI/data/locale/eu-ES.ini
+++ b/UI/data/locale/eu-ES.ini
@@ -78,6 +78,8 @@ None="Gabe"
StudioMode.Preview="Aurreikusi"
StudioMode.Program="Programa"
ShowInMultiview="Erakutsi ikuspegi anitzean"
+VerticalLayout="Diseinu bertikala"
+Group="Taldea"
AlreadyRunning.Title="OBS dagoeneko martxan dago"
AlreadyRunning.Text="OBS dagoeneko martxan dago! Bestelakorik nahi ez baduzu Itxi irekita dagoen saioa beste saio bat ireki baino lehen. Ezarri baduzu OBS agertzea minimizatua sistemaren erretiluan begiratu eta oraindik exekutatzen ari den bertan."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Erreprodukzio bufferra gelditzen..."
Basic.Main.StopStreaming="Gelditu transmisioa"
Basic.Main.StoppingStreaming="Transmisioa gelditzen..."
Basic.Main.ForceStopStreaming="Gelditu transmisioa (baztertu atzerapena)"
+Basic.Main.Group="%1 taldea"
+Basic.Main.GroupItems="Batu hautatutako elementuak"
+Basic.Main.Ungroup="Banatu"
Basic.MainMenu.File="&Fitxategia"
Basic.MainMenu.File.Export="&Esportatu"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Tresnak"
Basic.MainMenu.Help="&Laguntza"
Basic.MainMenu.Help.HelpPortal="Laguntza ataria"
Basic.MainMenu.Help.Website="Ikusi &webgunea"
+Basic.MainMenu.Help.Discord="Bat egin Discord zerbitzariarekin"
Basic.MainMenu.Help.Logs="&Egunkari-fitxategiak"
Basic.MainMenu.Help.Logs.ShowLogs="&Erakutsi egunkari-fitxategiak"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Kargatu &uneko egunkari-fitxategiak"
Basic.MainMenu.Help.Logs.UploadLastLog="Kargatu &azken egunkari-fitxategia"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Ikusi uneko egunkari-fitxategia"
Basic.MainMenu.Help.CheckForUpdates="Begiratu eguneraketak"
+Basic.MainMenu.Help.CrashLogs="Matxuren jakinarazpenak"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Erakutsi matxuren jakinarazpenak"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Kargatu azken matxura jakinarazpena"
Basic.Settings.ProgramRestart="Programa berrabiarazi egin behar da ezarpen hauek eragina izateko."
Basic.Settings.ConfirmTitle="Baieztatu aldaketak"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Minimizatu beti sistemaren erreti
Basic.Settings.General.SaveProjectors="Gorde proiekzioak irtetean"
Basic.Settings.General.SwitchOnDoubleClick="Aldatu eszena klik bikoitza egitean"
Basic.Settings.General.StudioPortraitLayout="Gaitu diseinu horizontala/bertikala"
+Basic.Settings.General.Multiview="Ikuspegi anitza"
+Basic.Settings.General.Multiview.MouseSwitch="Klikatu eszena batetik bestera pasatzeko"
+Basic.Settings.General.Multiview.DrawSourceNames="Erakutsi eszenen izenak"
+Basic.Settings.General.Multiview.DrawSafeAreas="Markatu area seguruak (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Ikuspegi anitzeko diseinua"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontala, goian"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontala, behean"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Bertikala, ezkerrean"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Bertikala, eskuinean"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontala, goian (8 eszena)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontala, behean (8 eszena)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Bertikala, ezkerrean (8 eszena)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Bertikala, eskuinean (8 eszena)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontala, goian (24 eszena)"
Basic.Settings.Stream="Transmisioa"
Basic.Settings.Stream.StreamType="Transmisio-mota"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Audio neurtzailearen gutxiagotze-tasa"
Basic.Settings.Audio.MeterDecayRate.Fast="Azkarra"
Basic.Settings.Audio.MeterDecayRate.Medium="Tartekoa (I motako PPMa)"
Basic.Settings.Audio.MeterDecayRate.Slow="Geldoa (II motako PPMa)"
+Basic.Settings.Audio.PeakMeterType="Gailurren neurgailu mota"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Lagin-gailurra"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Benetako gailurra (CPUaren erabilera handiagoa)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="Kontu: soinu inguratzailea aktibatuta dago."
Basic.Settings.Audio.MultichannelWarning="Transmititzen ari bazara, begiratu ea zure transmisio zerbitzuak onartzen duen soinu inguratzailea sarrerako soinuan zein irteerakoan. Twitch, Facebook 360 LIve, Mixer RTMP, Samashcast esate baterako guztiz onartzen dute soinu inguratzailea. Facebook Live eta Youtube Live sarrerako soinu inguratzailea onartzen badute ere, Facebook Livek estereo bihurtzen du, eta Youtube Livek bakarrik bi kanal erreproduzitzen ditu.\n\nOBS audio iragazkiak soinu inguratzailearekin bateragarriak badira ere, ezin da bermatu VST pluginaren bateragarritasuna."
Basic.Settings.Audio.MultichannelWarning.Title="Nahi duzu soinu inguratzailea aktibatzea?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Sarea"
Basic.Settings.Advanced.Network.BindToIP="IP bidez lotu"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Gaitu sare kode berria"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Latentzia txikiko modua"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Desgaitu laster-teklak leiho nagusia fokuan dagoenean"
Basic.AdvAudio="Audio propietate aurreratuak"
Basic.AdvAudio.Name="Izena"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Kontuz: MP4 formatuz gordetako grabazioak izan dait
FinalScene.Title="Ezabatu eszena"
FinalScene.Text="Gutxienez eszena bat egon behar du."
+NoSources.Title="Iturbururik ez dago"
+NoSources.Text="Badirudi ez duzula gehitu bideo iturbururik oraindik, beraz emaitza pantaila huts bat izango da. Ziur zaude hau egin nahi duzula?"
+NoSources.Text.AddSource="Gehitzen ahal duzu iturburuak Iturburuak kutxako azpiko aldeko + ikonoa klikatuz edozein unetan."
+
+ChangeBG="Ezarri kolorea"
+CustomColor="Kolore pertsonalizatua"
+
+BrowserSource.EnableHardwareAcceleration="Gaitu nabigatzailearen iturburuko hardware azelerazioa"
+
diff --git a/UI/data/locale/fa-IR.ini b/UI/data/locale/fa-IR.ini
new file mode 100644
index 0000000..8593961
--- /dev/null
+++ b/UI/data/locale/fa-IR.ini
@@ -0,0 +1,148 @@
+
+
+OK="باشه"
+Apply="اعمال تغییرات"
+Cancel="لغو"
+Close="ببند"
+Save="ذخیره"
+Discard="دور انداختن"
+Disable="غیرفعال کردن"
+Yes="بله"
+No="خير"
+Add="اضافه کردن"
+Remove="حذف"
+Rename="تغییر نام"
+Interact="تعامل"
+Filters="فیلتر ها"
+Properties="تنظیمات"
+MoveUp="انتقال به بالا"
+MoveDown="انتقال به پایین"
+Settings="تنظیمات"
+Display="صفحه نمایش"
+Name="نام"
+Exit="خروج"
+Mixer="میکسر"
+Browse="تغییر"
+Mono="مونو"
+Stereo="استریو"
+DroppedFrames="فریم های از دست رفته %1 (%2٪)"
+StudioProgramProjector="پروژکتور تمام صفحه (برنامه)"
+PreviewProjector="پروژکتور تمام صفحه (پیش نمایش)"
+SceneProjector="پروژکتور تمام صفحه (صحنه)"
+SourceProjector="پروژکتور تمام صفحه (منبع)"
+StudioProgramWindow="پروژکتور پنجره ای (برنامه)"
+PreviewWindow="پروژکتور پنجره ای (پیش نمایش)"
+SceneWindow="پروژکتور پنجره ای (صحنه)"
+SourceWindow="پروژکتور پنجره ای (منبع)"
+MultiviewProjector="حالت چند نمایی (تمام صفحه)"
+MultiviewWindowed="حالت چند نمایی (پنجره ای)"
+Clear="پاک کردن"
+Revert="برگرداندن"
+Show="نمایش"
+Hide="پنهان کردن"
+UnhideAll="نمایش تمام مخفی ها"
+Untitled="بدون عنوان"
+New="ایجاد"
+Duplicate="ایجاد مشابه"
+Enable="فعال کردن"
+DisableOSXVSync="غیرفعال کردن OSX V-Sync"
+ResetOSXVSyncOnExit="تنظیم مجدد OSX V-Sync هنگام خروج"
+HighResourceUsage="کد گذاری بیش از حد ! توجه کنید که تنظیمات ویدئویی را تغییر بدهید یا از یک پریست کد گذاری سریع تر استفاده کنید ."
+
+
+
+
+
+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="یا ۶۰ یا ۳۰ ، اما ۶۰ را ترجیح می دهم"
+Basic.AutoConfig.VideoPage.FPS.PreferHighRes="یا ۶۰ یا ۳۰ ، اما وضوح بالا را ترجیح می دهم"
+Basic.AutoConfig.VideoPage.CanvasExplanation="نکته : اندازه محیط (پایه) لزوما همان اندازه ای نیست که با آن پخش زنده یا ضبط می کنید . اندازه واقعی پخش زنده/ضبط شما ممکن است برای کاهش استفاده از منابع و یا میزان درخواست بیت بر ثانیه (بیت ریت) کمتر باشد ."
+Basic.AutoConfig.StreamPage="اطلاعات پخش زنده"
+Basic.AutoConfig.StreamPage.SubTitle="لطفا اطلاعات پخش زنده خود را وارد کنید"
+Basic.AutoConfig.StreamPage.Service="سرویس"
+Basic.AutoConfig.StreamPage.Service.ShowAll="نمایش همه..."
+Basic.AutoConfig.StreamPage.Server="سرور"
+Basic.AutoConfig.StreamPage.StreamKey="کلید پخش زنده"
+Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(لینک)"
+Basic.AutoConfig.StreamPage.PerformBandwidthTest="برآورد میزان بیت بر ثانیه (بیت ریت) با تست پهنای باند (ممکن است چند دقیقه طول بکشد)"
+Basic.AutoConfig.StreamPage.PreferHardwareEncoding="کد گذاری سخت افزاری را ترجیح می دهم"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/data/locale/fi-FI.ini b/UI/data/locale/fi-FI.ini
index d9b37b4..8e72cbc 100644
--- a/UI/data/locale/fi-FI.ini
+++ b/UI/data/locale/fi-FI.ini
@@ -78,6 +78,8 @@ None="Ei mitään"
StudioMode.Preview="Esikatselu"
StudioMode.Program="Ohjelma"
ShowInMultiview="Näytä moninäkymässä"
+VerticalLayout="Pystynäkymä"
+Group="Ryhmitä"
AlreadyRunning.Title="OBS on jo käynnissä"
AlreadyRunning.Text="OBS on jo käynnissä! Ellet tarkoittanut tehdä näin, ole hyvä ja sulje aikaisemmat OBS-prosessit ennen uuden käynnistämistä. Jos olet asettanut OBS:n pienentymään ilmaisinalueelle, varmista ettei se ole siellä yhä päällä."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Pysäytetään toistopuskuri..."
Basic.Main.StopStreaming="Pysäytä lähetys"
Basic.Main.StoppingStreaming="Pysäytetään lähetystä..."
Basic.Main.ForceStopStreaming="Lopeta lähetys (ohita viive)"
+Basic.Main.Group="Ryhmitä %1"
+Basic.Main.GroupItems="Ryhmitä valitut lähteet"
+Basic.Main.Ungroup="Poista ryhmästä"
Basic.MainMenu.File="&Tiedosto"
Basic.MainMenu.File.Export="&Vie"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="T&yökalut"
Basic.MainMenu.Help="&Apua"
Basic.MainMenu.Help.HelpPortal="&Apukeskus"
Basic.MainMenu.Help.Website="Käy &verkkosivulla"
+Basic.MainMenu.Help.Discord="Liity &Discord-palvelimelle"
Basic.MainMenu.Help.Logs="&Lokitiedostot"
Basic.MainMenu.Help.Logs.ShowLogs="&Näytä lokitiedostot"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Lähetä n&ykyinen lokitiedosto"
Basic.MainMenu.Help.Logs.UploadLastLog="Lähetä edellinen lokitiedosto"
Basic.MainMenu.Help.Logs.ViewCurrentLog="Näytä ny&kyinen loki"
Basic.MainMenu.Help.CheckForUpdates="Tarkista päivitykset"
+Basic.MainMenu.Help.CrashLogs="&Kaatumisraportit"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="&Näytä kaatumisraportit"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="&Lähetä kaatumisraportti"
Basic.Settings.ProgramRestart="Ohjelma on käynnistettävä uudelleen, jotta asetukset tulevat voimaan."
Basic.Settings.ConfirmTitle="Vahvista muutokset"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Pienennä aina tilapalkkiin teht
Basic.Settings.General.SaveProjectors="Tallenna peilaus poistuessa"
Basic.Settings.General.SwitchOnDoubleClick="Siirtymä skeneen tuplaklikattaessa"
Basic.Settings.General.StudioPortraitLayout="Ota pystyasettelu käyttöön"
+Basic.Settings.General.Multiview="Moninäkymä"
+Basic.Settings.General.Multiview.MouseSwitch="Klikkaa vaihtaaksesi skenejen välillä"
+Basic.Settings.General.Multiview.DrawSourceNames="Näytä skenejen nimet"
+Basic.Settings.General.Multiview.DrawSafeAreas="Piirrä turva-alueet (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Moninäkymän asettelu"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Vaaka, ylhäällä"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Vaaka, alhaalla"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Pysty, vasemmalla"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Pysty, oikealla"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Vaakasuunta, ylhäällä (8 skeneä)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Vaakasuunta, alhaalla (8 skeneä)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Pystysuunta, vasemmalla (8 skeneä)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Pystysuunta, oikealla (8 skeneä)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Vaakasuunta, ylhäällä (24 skeneä)"
Basic.Settings.Stream="Lähetys"
Basic.Settings.Stream.StreamType="Lähetystyyppi"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Äänimittarin putoamisnopeus"
Basic.Settings.Audio.MeterDecayRate.Fast="Nopea"
Basic.Settings.Audio.MeterDecayRate.Medium="Keskinopea (Tyyppi I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Hidas (Tyyppi II PPM)"
+Basic.Settings.Audio.PeakMeterType="Huippuarvo-mittarin tyyppi"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Huippuarvon näyte"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Todellinen huippuarvo (Korkeampi CPU:n käyttö)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="VAROITUS: Monikanavaääni on käytössä."
Basic.Settings.Audio.MultichannelWarning="Varmista lähettäessä että palvelu tukee sekä monikanavaäänen lähettämistä, että toistamista. Twitch, Facebook 360 Live, Mixer RTMP ja Smashcast ovat esimerkkejä palveluista joissa monikanavaääni on täysin tuettu. Vaikka Facebook Live ja YouTube Live hyväksyvät monikanavaäänen lähettämisen, Facebook Live miksaa äänen stereoksi ja YouTube Live toistaa vain kaksi kanavaa.\n\nOBS:n äänisuodattimet tukevat monikanavaääntä, mutta VST-liitännäiset eivät välttämättä tue."
Basic.Settings.Audio.MultichannelWarning.Title="Käytä monikanava-ääntä?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Verkko"
Basic.Settings.Advanced.Network.BindToIP="Liitä IP:seen"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Käytä uutta verkkokoodia"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Alhaisen latenssin tila"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Poista pikanäppäimet käytöstä, kun pääikkuna on aktiivisena"
Basic.AdvAudio="Äänen lisäominaisuudet"
Basic.AdvAudio.Name="Nimi"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Varoitus: MP4-muotoon tallentaessa tiedostoista tul
FinalScene.Title="Poista skene"
FinalScene.Text="Ainakin yksi skene pitää olla olemassa."
+NoSources.Title="Ei lähteitä"
+NoSources.Text="Näyttää siltä ettet ole vielä lisännyt yhtään kuvalähdettä, joten kuva on musta. Haluatko varmasti tehdä näin?"
+NoSources.Text.AddSource="Voit lisätä lähteitä klikkaamalla \"+\"-kuvaketta \"Lähteet\"-alueen alapuolella."
+
+ChangeBG="Aseta väri"
+CustomColor="Mukautettu väri"
+
+BrowserSource.EnableHardwareAcceleration="Käytä laitteistokiihdytystä \"Selain\"-lähteessä"
+
diff --git a/UI/data/locale/fil-PH.ini b/UI/data/locale/fil-PH.ini
index f39fa56..80ec529 100644
--- a/UI/data/locale/fil-PH.ini
+++ b/UI/data/locale/fil-PH.ini
@@ -76,6 +76,8 @@ None="Wala"
StudioMode.Preview="Balikan"
StudioMode.Program="Programa"
ShowInMultiview="Ipakita sa Multiview"
+VerticalLayout="Patayong Layout"
+Group="Grupo"
AlreadyRunning.Title="Tumatakbo na ngayon ang OBS"
AlreadyRunning.Text="Tumatakbo na ang OBS! Maliban na lamang kung gusto mong gawin ito, pakiusap patayin ang anomang nabubuhay na mga mungkahi ng OBS bago subukang magpatakbo ng panibagong mungkahi. Kung meron kang OBS set para mabawasan ang sistemang tray, pakiusap magsiyasat para makita kung ito ay tumatakbo parin."
@@ -402,6 +404,9 @@ Basic.Main.StoppingReplayBuffer="Pagtigil sa Pagre-Replay Buffer..."
Basic.Main.StopStreaming="Itiigil ang Pag-stream"
Basic.Main.StoppingStreaming="Pagtigil sa Pag-stream..."
Basic.Main.ForceStopStreaming="Itigil ang Pag-stream (Iwaksi ang Pagkaantala)"
+Basic.Main.Group="Grupo %1"
+Basic.Main.GroupItems="I-grupo ang napiling mga aytem"
+Basic.Main.Ungroup="Alisin sa Grupo"
Basic.MainMenu.File="&Talaksan"
Basic.MainMenu.File.Export="&I-export"
@@ -468,6 +473,7 @@ Basic.MainMenu.Tools="&Mga Kasangkapan"
Basic.MainMenu.Help="&Tulong"
Basic.MainMenu.Help.HelpPortal="Tulong &lagusan"
Basic.MainMenu.Help.Website="Pagbisita &website"
+Basic.MainMenu.Help.Discord="Sumali sa &Discord Server"
Basic.MainMenu.Help.Logs="Mag-&log ng mga File"
Basic.MainMenu.Help.Logs.ShowLogs="&ipakita ang Pag-log ng mga File"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Mag-upload &Kasalukuyang Mag-log ng File"
@@ -505,10 +511,6 @@ Basic.Settings.General.SaveProjectors="I-save ang mga prodyektor sa labasan"
Basic.Settings.General.SwitchOnDoubleClick="Paglipat sa eksena kahit makadalawang-pindot"
Basic.Settings.General.StudioPortraitLayout="Paganahin ang larawan/vertical layout"
Basic.Settings.General.MultiviewLayout="Multiview Layout"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Pahalang, Itaas"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Pahalang, Pababa"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, Sa kaliwa"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, sa kanan"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Mga uri ng Stream"
@@ -746,3 +748,6 @@ OutputWarnings.MP4Recording="Babala: Ang Recording na naka-save sa MP4 ay hindi
FinalScene.Title="Tanggaling ang Eksena"
FinalScene.Text="Doon kailangan ng kahit isang eksena."
+
+
+
diff --git a/UI/data/locale/fr-FR.ini b/UI/data/locale/fr-FR.ini
index 17e52e0..0e12f98 100644
--- a/UI/data/locale/fr-FR.ini
+++ b/UI/data/locale/fr-FR.ini
@@ -78,6 +78,8 @@ None="Aucune"
StudioMode.Preview="Aperçu"
StudioMode.Program="Programme"
ShowInMultiview="Montrer en Multivues"
+VerticalLayout="Disposition Verticale"
+Group="Groupe"
AlreadyRunning.Title="OBS est déjà en cours d'exécution"
AlreadyRunning.Text="OBS est déjà en cours d'exécution, merci de bien fermer toute autre instances existantes d'OBS avant d'en exécuter une nouvelle. Vérifiez dans votre barre d'état s'il n'est pas réduit et en cours d’exécution."
@@ -92,7 +94,7 @@ BandwidthTest.Region.EU="Europe"
BandwidthTest.Region.Asia="Asie"
BandwidthTest.Region.Other="Autre"
-Basic.FirstStartup.RunWizard="Exécutez l'Assistant de configuration ? Vous pouvez configurer manuellement vos paramètre en cliquant sur le bouton des Paramètres situer dans la fenêtre principale."
+Basic.FirstStartup.RunWizard="Exécuter l'Assistant de configuration ? Vous pouvez configurer manuellement vos paramètres en cliquant sur le bouton des Paramètres situé dans la fenêtre principale."
Basic.FirstStartup.RunWizard.BetaWarning="(Remarque : l’Assistant de configuration automatique est actuellement en version bêta)"
Basic.FirstStartup.RunWizard.NoClicked="Si vous changez d’avis, vous pouvez réexécuter l’Assistant de configuration automatique n’importe quel moment dans le menu outils."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Arrêt du tampon de relecture..."
Basic.Main.StopStreaming="Arrêter le streaming"
Basic.Main.StoppingStreaming="Arrêt du stream..."
Basic.Main.ForceStopStreaming="Arrêter le streaming (annule le retard)"
+Basic.Main.Group="Groupe %1"
+Basic.Main.GroupItems="Grouper les éléments sélectionnés"
+Basic.Main.Ungroup="Dissocier"
Basic.MainMenu.File="&Fichier"
Basic.MainMenu.File.Export="&Exporter"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="Outils"
Basic.MainMenu.Help="&Aide"
Basic.MainMenu.Help.HelpPortal="&Portail d'aide"
Basic.MainMenu.Help.Website="Consulter le site &Web"
+Basic.MainMenu.Help.Discord="Rejoindre le serveur &Discord"
Basic.MainMenu.Help.Logs="&Fichiers journaux"
Basic.MainMenu.Help.Logs.ShowLogs="Afficher les &fichiers de log"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Mettre en ligne le fichier journal &actuel"
Basic.MainMenu.Help.Logs.UploadLastLog="Mettre en ligne le &dernier fichier journal"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Voir le journal actuel"
Basic.MainMenu.Help.CheckForUpdates="Rechercher des mises à jour"
+Basic.MainMenu.Help.CrashLogs="Rapports d'e&rreurs"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Montrer les rapport&s d'erreur"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Envoyer &le Dernier Rapport d'Erreur"
Basic.Settings.ProgramRestart="Le programme doit être redémarré pour que les paramètres prennent effet."
Basic.Settings.ConfirmTitle="Valider les modifications"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Toujours réduire dans la zone de
Basic.Settings.General.SaveProjectors="Enregistrer les projecteurs en quittant"
Basic.Settings.General.SwitchOnDoubleClick="Effectuer la transition vers la scène en cas de double clic"
Basic.Settings.General.StudioPortraitLayout="Activer la mise en page portrait/verticale"
+Basic.Settings.General.Multiview="Multivues"
+Basic.Settings.General.Multiview.MouseSwitch="Cliquez pour changer de scène"
+Basic.Settings.General.Multiview.DrawSourceNames="Montrer les noms des scènes"
+Basic.Settings.General.Multiview.DrawSafeAreas="Afficher les zones sûres (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Disposition de la multivue"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, haut"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, bas"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, gauche"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, droit"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, Haut (8 Scènes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, Bas (8 Scènes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, Gauche (8 Scènes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, Droite (8 Scènes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontal, Haut (24 Scènes)"
Basic.Settings.Stream="Flux"
Basic.Settings.Stream.StreamType="Type de diffusion"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Vitesse de dégradation audiométrique"
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"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Crête vraie (plus grande utilisation du CPU)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="ATTENTION : le son multicanal est activé."
Basic.Settings.Audio.MultichannelWarning="Pour de besoins de diffusion, vérifiez que votre service de diffusion supporte l'intégration et la lecture du son multicanal. Twitch, Facebook 360 Live, Mixer RTMP ou Smashcast sont des exemples de services où le son multicanal est entièrement supporté. Bien que Facebook Live et YouTube Live acceptent l'intégration de son multicanal, Facebook Live transcode en stéréo, et YouTube Live ne lit que deux canaux.\n\nLes filtres audio d'OBS sont compatibles avec le son multicanal, toutefois le support du plugin VST n'est pas garanti."
Basic.Settings.Audio.MultichannelWarning.Title="Activer le son multicanal ?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Carte réseau (adresse IP source du flux)"
Basic.Settings.Advanced.Network.BindToIP="Lier à :"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Activer le nouveau code réseau"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Mode faible latence"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Désactiver les raccourcis clavier lorsque la fenêtre principale est au premier plan"
Basic.AdvAudio="Propriétés audio avancées"
Basic.AdvAudio.Name="Nom"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Avertissement : les enregistrements sauvegardés en
FinalScene.Title="Supprimer la scène"
FinalScene.Text="Il doit y avoir au moins une scène."
+NoSources.Title="Aucune source"
+NoSources.Text="Il semble que vous n'ayez pas ajouté de source vidéo pour le moment, vous aurez donc un écran noir. Êtes-vous sûr de vouloir continuer ?"
+NoSources.Text.AddSource="Vous pouvez à tout moment ajouter des sources en cliquant sur l'icone + sous le bloc Sources de la fenêtre principale."
+
+ChangeBG="Définir la couleur"
+CustomColor="Couleur personnalisée"
+
+BrowserSource.EnableHardwareAcceleration="Activer l’accélération matériel de la source navigateur"
+
diff --git a/UI/data/locale/gd-GB.ini b/UI/data/locale/gd-GB.ini
new file mode 100644
index 0000000..0ea4d86
--- /dev/null
+++ b/UI/data/locale/gd-GB.ini
@@ -0,0 +1,770 @@
+
+Language="Gàidhlig"
+Region="Alba"
+
+OK="Ceart ma-thà"
+Apply="Cuir an sàs"
+Cancel="Sguir dheth"
+Close="Dùin"
+Save="Sàbhail"
+Discard="Tilg air falbh"
+Disable="Cuir à comas"
+Yes="Tha"
+No="Chan eil"
+Add="Cuir ris"
+Remove="Thoir air falbh"
+Rename="Thoir ainm ùr air"
+Interact="Eadar-ghnìomhach"
+Filters="Criathragan"
+Properties="Roghainnean"
+MoveUp="Gluais suas"
+MoveDown="Gluais sìos"
+Settings="Roghainnean"
+Display="Uidheam-taisbeanaidh"
+Name="Ainm"
+Exit="Fàg an-seo"
+Mixer="Measgadair"
+Browse="Rùraich"
+Mono="Mono"
+Stereo="Stereo"
+DroppedFrames="Frèamaichean a thuit: %1 (%2%)"
+StudioProgramProjector="Proiseactar làn-sgrìn (prògram)"
+PreviewProjector="Proiseactar làn-sgrìn (ro-shealladh)"
+SceneProjector="Proiseactar làn-sgrìn (sealladh)"
+SourceProjector="Proiseactar làn-sgrìn (tùs)"
+StudioProgramWindow="Proiseactar uinneagaichte (prògram)"
+PreviewWindow="Proiseactar uinneagaichte (ro-shealladh)"
+SceneWindow="Proiseactar uinneagaichte (sealladh)"
+SourceWindow="Proiseactar uinneagaichte (tùs)"
+MultiviewProjector="Ioma-shealladh (làn-sgrìn)"
+MultiviewWindowed="Ioma-shealladh (uinneagaichte)"
+Clear="Falamhaich"
+Revert="Till"
+Show="Seall"
+Hide="Falaich"
+UnhideAll="Seall na h-uile"
+Untitled="Gun tiotal"
+New="Ùr"
+Duplicate="Dùblaich"
+Enable="Cuir an comas"
+DisableOSXVSync="Cuir à comas sioncronachadh-V air OSX"
+ResetOSXVSyncOnExit="Ath-shuidhich sioncronachadh-V air OSX nuar a dh’fhàgar"
+HighResourceUsage="Tha an còdachadh ro thrang! Feuch an tagh thu roghainnean video nas ìsle no an cleachd thu ro-sheata còdachaidh nas luaithe."
+Transition="Tar-mhùthadh"
+QuickTransitions="Tar-mhùthaidhean luatha"
+Left="Clì"
+Right="Deas"
+Top="Barr"
+Bottom="Bonn"
+Reset="Ath-shuidhich"
+Hours="Uair"
+Minutes="Mionaid"
+Seconds="Diog"
+Deprecated="Cha mholar seo tuilleadh"
+Import="Ion-phortaich"
+Export="Às-phortaich"
+Copy="Dèan lethbhreac"
+Paste="Cuir ann"
+PasteReference="Cuir ann (iomradh)"
+PasteDuplicate="Cuir ann (dùblachadh)"
+RemuxRecordings="Iompaich clàraidhean"
+Next="Air adhart"
+Back="Air ais"
+Defaults="Bun-roghainnean"
+HideMixer="Falaich sa mheasgadair"
+TransitionOverride="Tar-àithneadh an tar-mhùthaidh"
+None="Chan eil gin"
+StudioMode.Preview="Ro-shealladh"
+StudioMode.Program="Prògram"
+ShowInMultiview="Seall san ioma-shealladh"
+VerticalLayout="Co-dhealbhachd inghearach"
+Group="Buidhnich"
+
+AlreadyRunning.Title="Tha OBS ’ga ruith mar-thà"
+AlreadyRunning.Text="Tha OBS ’ga ruith mar-thà! Mur ann gun robh thu airson seo a dhèanamh, dùin sìos gach ionstans de dh’OBS mus fheuch thu ri ionstans eile dheth a ruith. Ma shuidhich thu OBS ach an dèid fhìor-lùghdachadh gu treidhe an t-siostaim thoir sùil a bheil e ’ga ruith an-siud fhathast."
+AlreadyRunning.LaunchAnyway="Cuir gu dol e co-dhiù"
+
+Copy.Filters="Dèan lethbhreac dhe na criathragan"
+Paste.Filters="Cuir ann criathragan"
+
+BandwidthTest.Region="Roinn-dùthcha"
+BandwidthTest.Region.US="Na Stàitean Aonaichte"
+BandwidthTest.Region.EU="An Roinn-Eòrpa"
+BandwidthTest.Region.Asia="Àisia"
+BandwidthTest.Region.Other="Roinn-dùthcha eile"
+
+Basic.FirstStartup.RunWizard="A bheil thu airson draoidh an fhèin-rèiteachaidh a ruith? ’S urrainn dhut na roghainnean agad a rèiteachadh a làimh cuideachd ’s tu a’ briogadh air a’ phutan “Roghainnean” sa phrìomh-uinneag."
+Basic.FirstStartup.RunWizard.BetaWarning="(An aire: Tha draoidh an fhèin-rèiteachaidh ’na thionndadh beta fhathast)"
+Basic.FirstStartup.RunWizard.NoClicked="Ma chuireas tu an caochladh romhad, ’s urrainn dhut draoidh an fhèin-rèiteachaidh a ruith a-rithist on chlàr-taice “Innealan” uair sam bith."
+
+Basic.AutoConfig="Draoidh an fhèin-rèiteachaidh"
+Basic.AutoConfig.Beta="Draoidh an fhèin-rèiteachaidh (beta)"
+Basic.AutoConfig.ApplySettings="Cuir na roghainnean an sàs"
+Basic.AutoConfig.StartPage="Fiosrachadh a’ chleachdaidh"
+Basic.AutoConfig.StartPage.SubTitle="Sònraich na h-adhbharan air an cleachd thu am prògram"
+Basic.AutoConfig.StartPage.PrioritizeStreaming="Gleus airson sruthadh is chan eil clàradh cho cudromach sin"
+Basic.AutoConfig.StartPage.PrioritizeRecording="Gleus airson clàradh a-mhàin, cha dèan mi sruthadh"
+Basic.AutoConfig.VideoPage="Roghainnean video"
+Basic.AutoConfig.VideoPage.SubTitle="Sònraich na roghainnean video a bu toigh leat cleachdadh"
+Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="An roghainn làithreach (%1x%2)"
+Basic.AutoConfig.VideoPage.BaseResolution.Display="Uidheam-taisbeanaidh %1 (%2x%3)"
+Basic.AutoConfig.VideoPage.FPS.UseCurrent="An roghainn làithreach (%1)"
+Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="An dà chuid 60 no 30 ach b’ fhearr leam 60"
+Basic.AutoConfig.VideoPage.FPS.PreferHighRes="An dà chuid 60 no 30 ach b’ fhearr leam dùmhlachd-bhreacaidh àrd"
+Basic.AutoConfig.VideoPage.CanvasExplanation="An aire: Cha bhi dùmhlachd-bhreacaidh bhunasach (a’ chanabhais) co-ionnan ris an dùmhlachd-bhreacaidh a thèid a chlàradh no a shruthadh an-còmhnaidh. Dh’fhaoidte gun dèid an dùbhlachd-bhreacaidh air sruthadh no clàradh ìsleachadh o dhùmhlachd-bhreacaidh a’ chanabhais airson freagairt ri feumalachdan cleachdaidh no reat bhiotaichean."
+Basic.AutoConfig.StreamPage="Fiosrachadh an t-sruthaidh"
+Basic.AutoConfig.StreamPage.SubTitle="Cuid a-steach fiosrachadh an t-sruthaidh agad"
+Basic.AutoConfig.StreamPage.Service="Seirbheis"
+Basic.AutoConfig.StreamPage.Service.ShowAll="Seall na h-uile…"
+Basic.AutoConfig.StreamPage.Server="Frithealaiche"
+Basic.AutoConfig.StreamPage.StreamKey="Iuchair an t-sruthaidh"
+Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Ceangal)"
+Basic.AutoConfig.StreamPage.PerformBandwidthTest="Dèan tuairmse air reat bhiotaichean le deuchainn air an leud-bhanna (dh’fhaoidte gun doir seo mionaid no dhà)"
+Basic.AutoConfig.StreamPage.PreferHardwareEncoding="B’ fhearr leam còdachadh bathair-chruthaidh"
+Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Le còdachadh bathair-chruaidh, cha dèid ach glè bheag dhen CPU a chleachdadh ach dh’fhaoidte gum bi feum air reat bhiotaichean nas àirde airson ann aon ìre a chàileachd fhaighinn."
+Basic.AutoConfig.StreamPage.StreamWarning.Title="Rabhadh sruthaidh"
+Basic.AutoConfig.StreamPage.StreamWarning.Text="Tha an deuchainn air an leud-bhanna gu bhith dàta video tuaireamach gun fhuaim a shruthadh dhan t-seanail agad. Ma ghabhas seo a dhèanamh, mholamaid gun cuir thu dheth sàbhaladh sruthaidh video agus gun dèan thu an sruth agad prìobhaideach gus am bi an deuchainn deiseil. A bheil thu airson leantainn air adhart?"
+Basic.AutoConfig.TestPage="Na toraidhean deireannach"
+Basic.AutoConfig.TestPage.SubTitle.Testing="Tha am prògram a’ ruith deuchainnean airson tuairmse a dhèanamh air na roghainnean as fhearr"
+Basic.AutoConfig.TestPage.SubTitle.Complete="Tha an deuchainn deiseil"
+Basic.AutoConfig.TestPage.TestingBandwidth="A’ cur an leud-bhanna fo dheuchainn, dh’fhaoidte gun doir seo mionaid no dhà…"
+Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="A’ ceangal ri: %1…"
+Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Cha deach leinn ceangal a dhèanamh ri frithealaiche sam bith, thoir sùil air a’ cheangal agad dhan eadar-lìon is feuch ris a-rithist an uairsin."
+Basic.AutoConfig.TestPage.TestingBandwidth.Server="A’ cur an leud-bhanna fo dheuchainn airson: %1"
+Basic.AutoConfig.TestPage.TestingStreamEncoder="A’ cur inneal-còdachaidh an t-sruthaidh fo dheuchainn, dh’fhaoidte gun doir seo mionaid no dhà…"
+Basic.AutoConfig.TestPage.TestingRecordingEncoder="A’ cur inneal-còdachaidh a’ chlàraidh fo dheuchainn, dh’fhaoidte gun doir seo mionaid no dhà…"
+Basic.AutoConfig.TestPage.TestingRes="A’ cur nan dùmhlachdan-breacaidh fo dheuchainn, dh’fhaoidte gun doir seo mionaid no dhà…"
+Basic.AutoConfig.TestPage.TestingRes.Fail="Cha deach leinn an t-inneal-còdachaidh a chur gu dol"
+Basic.AutoConfig.TestPage.TestingRes.Resolution="A’ feuchainn %1x%2 %3 FPS…"
+Basic.AutoConfig.TestPage.Result.StreamingEncoder="Inneal-còdachaidh an t-sruthaidh"
+Basic.AutoConfig.TestPage.Result.RecordingEncoder="Inneal-còdachaidh a’ chlàraidh"
+Basic.AutoConfig.TestPage.Result.Header="Rinn am prògram tuairmse gur e seo na roghainnean as fhearr dhut:"
+Basic.AutoConfig.TestPage.Result.Footer="Airson na roghainnean seo a chleachdadh, briog air “Cuir na roghainnean an sàs”. Airson an draoidh a rèiteachadh às ùr is fheuchainn is a-rithist, briog air “Air ais”. Airson na roghainnean a rèiteachadh a làimh, briog air “Sguir dheth” is fosgail na “Roghainnean”."
+
+Basic.Stats="Stadastaireachd"
+Basic.Stats.CPUUsage="Cleachdadh a’ CPU"
+Basic.Stats.HDDSpaceAvailable="Àite ri fhaighinn air a’ chlàr-chruaidh"
+Basic.Stats.MemoryUsage="Cleachdadh a’ chuimhne"
+Basic.Stats.AverageTimeToRender="Ùine cuibheasach air reandaradh frèama"
+Basic.Stats.SkippedFrames="Na frèamaichean air an leigeil seachad ri linn dàil còdachaidh"
+Basic.Stats.MissedFrames="Na frèamaichean a chaidh a dhìth ri linn dàil còdachaidh"
+Basic.Stats.Output.Stream="Sruthadh"
+Basic.Stats.Output.Recording="Clàradh"
+Basic.Stats.Status="Staid"
+Basic.Stats.Status.Recording="Clàradh"
+Basic.Stats.Status.Live="BEÒ"
+Basic.Stats.Status.Reconnecting="’Ga ath-cheangal"
+Basic.Stats.Status.Inactive="Neo-ghnìomhach"
+Basic.Stats.DroppedFrames="Frèamaichean a thuit: (lìonra)"
+Basic.Stats.MegabytesSent="Às-chur dàta iomlan"
+Basic.Stats.Bitrate="Reat bhiotaichean"
+
+Updater.Title="Tha ùrachadh ri fhaighinn"
+Updater.Text="Tha ùrachadh ri fhaighinn:"
+Updater.UpdateNow="Ùraich an-dràsta"
+Updater.RemindMeLater="Cuir nam chuimhne uaireigin eile"
+Updater.Skip="Leig seachad an tionndadh"
+Updater.Running.Title="Tha am prògram gnìomhach"
+Updater.Running.Text="Tha às-chur gnìomhach ann, cuir stad air gach às-chur mus fheuch thu ris an ùrachadh"
+Updater.NoUpdatesAvailable.Title="Chan eil ùrachadh ri fhaighinn"
+Updater.NoUpdatesAvailable.Text="Chan eil ùrachadh ri fhaighinn an-dràsta"
+Updater.FailedToLaunch="Cha deach leinn an t-inneal-ùrachaidh a chur gu dol"
+Updater.GameCaptureActive.Title="Tha glacadh geama gnìomhach"
+Updater.GameCaptureActive.Text="Tha leabharlann glacaidh geama ’ga chleachdadh. Dùin gad geama no prògram a tha ’ga ghlacadh (no ath-thòisich Windows) is feuch ris a-rithist."
+
+QuickTransitions.SwapScenes="Dèan iomlaid air seallaidhean an ro-sheallaidh ’s an às-chuir às dèid an tar-mhùthaidh"
+QuickTransitions.SwapScenesTT="Nì seo iomlaid air seallaidhean an ro-sheallaidh ’s an às-chuir às dèidh an tar-mhùthaidh (ma tha sealladh tùsail an às-chuir ann fhathast).\nCha neo-dhèan seo atharrachadh sam bith a chaidh a dhèanamh air sealladh tùsail an às-chuir."
+QuickTransitions.DuplicateScene="Dùblaich an t-sealladh"
+QuickTransitions.DuplicateSceneTT="Nuair a nì thu deasachadh air an aon shealladh, leigidh seo leat cruth-atharrachadh no faicsinneachd nan tùsan a dheasachadh gun atharrachadh air an às-chur.\nAirson roghainnean nan tùsan atharrachadh gun a bhith ag atharrachadh an às-chuir, cuir an comas “Dùblaich na tùsan”.\nNuair a dh’atharraicheas tu an luach seo, thèid sealladh an às-chuir làithrich atharrachadh (ma tha e ann fhathast)."
+QuickTransitions.EditProperties="Dùblaich na tùsan"
+QuickTransitions.EditPropertiesTT="Nuair a nì thu deasachadh air an aon shealladh, leigidh seo leat roghainnean nan tùsan atharrachadh gun atharrachadh air an às-chur.\nCha ghabh seo a chleachdadh ach ma tha “Dùblaich an t-sealladh” an comas.\nTha tùsan ann (can tùsan glacaidh no meadhain) nach cuir taic ri seo agus cha ghabh an deasachadh fa leth.\nNuair a dh’atharraicheas tu an luach seo, thèid sealladh an às-chuir làithrich ath-shuidheachadh (ma tha e ann fhathast).\n\nRabhadh: On a thèid na tùsan a dhùblachadh, bi feum air barrachd ghoireasan siostaim no video."
+QuickTransitions.HotkeyName="Tar-mhùthadh luath: %1"
+
+Basic.AddTransition="Cuir ris tar-mhùthadh a ghabhas rèiteachadh"
+Basic.RemoveTransition="Thoir air falbh tar-mhùthadh a ghabhas rèiteachadh"
+Basic.TransitionProperties="Roghainnean an tar-mhùthaidh"
+Basic.SceneTransitions="Tar-mhùthaidhean an t-seallaidh"
+Basic.TransitionDuration="Faide"
+Basic.TogglePreviewProgramMode="Modh stiùideo"
+
+TransitionNameDlg.Text="Cuir a-steach ainm an tar-mhùthaidh"
+TransitionNameDlg.Title="Ainm an tar-mhùthaidh"
+
+TitleBar.Profile="Pròifil"
+TitleBar.Scenes="Seallaidhean"
+
+NameExists.Title="Tha an t-ainm ann mu thràth"
+NameExists.Text="Tha an t-ainm seo ’ga chleachdadh mu thràth."
+
+NoNameEntered.Title="Cuir a-steach ainm dligheach"
+NoNameEntered.Text="Chan urrainn dhut ainm falamh a chleachdadh."
+
+ConfirmStart.Title="A bheil thu airson tòiseachadh air sruthadh?"
+ConfirmStart.Text="A bheil thu cinnteach gu bheil thu airson tòiseachadh air an t-sruthadh?"
+
+ConfirmStop.Title="A bheil thu airson stad a chur air an t-sruthadh?"
+ConfirmStop.Text="A bheil thu cinnteach gu bheil thu airson stad a chur air an t-sruthadh?"
+
+ConfirmExit.Title="A bheil thu airson OBS fhàgail?"
+ConfirmExit.Text="Tha OBS gnìomhach an-dràsta. Thèid gach sruthadh no clàradh a chur gu crìch. A bheil thu cinnteach gu bheil thu airson fhàgail?"
+
+ConfirmRemove.Title="Dearbh an toirt air falbh"
+ConfirmRemove.Text="A bheil thu cinnteach gu bheil thu airson “$1” a thoirt air falbh?"
+ConfirmRemove.TextMultiple="A bheil thu cinnteach gu bheil thu airson %1 nithean a thoirt air falbh?"
+
+Output.StartStreamFailed="Cha deach leinn tòiseachadh air an t-sruthadh"
+Output.StartRecordingFailed="Cha deach leinn tòiseachadh air a’ chlàradh"
+Output.StartReplayFailed="Cha deach leinn tòiseachadh air bufair na h-ath-chluiche"
+Output.StartFailedGeneric="Cha deach leinn tòiseachadh air an às-chur. Thoir sùil air an loga airson barrachd fiosrachaidh.\n\nAn aire: Ma tha thu a’ cleachdadh inneal-còdachaidh NVENC no AMD, dèan cinneach gu bheil na draibhearan video agad cho ùr ’s a ghabhas."
+
+Output.ConnectFail.Title="Cha deach leinn ceangal a dhèanamh"
+Output.ConnectFail.BadPath="Tha slighe no URL a’ cheangail mì-dhligheach. Thoir sùil air na roghainnean agad feuch a bheil iad mar bu chòir."
+Output.ConnectFail.ConnectFailed="Dh’fhàillig an ceangal ris an fhrithealaiche"
+Output.ConnectFail.InvalidStream="Bha b’ urrainn dhuinn an t-seanail no an iuchair sruthaidh a shònraich thu inntrigeadh, dearbhaidh an iuchair sruthaidh agad. Ma tha i ceart, dh’fhaoidte gu bheil duilgheadas ann le ceangal ris an fhrithealaiche."
+Output.ConnectFail.Error="Thachair mearachd ris nach robh dùil nuair a dh’fheuch sinn ri ceangal ris an fhrithealaiche. Tha barrachd fiosrachaidh ann am faidhle an loga."
+Output.ConnectFail.Disconnected="Chaidh an ceangal dhan fhrithealaiche a bhriseadh."
+
+Output.RecordFail.Title="Cha deach leinn tòiseachadh air a’ chlàradh"
+Output.RecordFail.Unsupported="Cha chuirear taic ri fòrmat an às-chuir no cha chuir e taic ri corr is aon traca fuaime. Thoir sùil air na roghainnean agad is feuch ris a-rithist."
+Output.RecordNoSpace.Title="Chan eil àite gu leòr air an diosga"
+Output.RecordNoSpace.Msg="Chan eil àite gu leòr air an diosga airson leantainn air adhart leis a’ chlàradh."
+Output.RecordError.Title="Mearachd clàraidh"
+Output.RecordError.Msg="Thachair mearachd nach deach a shònrachadh rè a’ chlàraidh."
+Output.ReplayBuffer.NoHotkey.Title="Cha deach grad-iuchair a shuidheachadh!"
+Output.ReplayBuffer.NoHotkey.Msg="Cha deach grad-iuchair sàbhalaidh a shuidheachadh airson bufair na h-ath-chluiche. Suidhich an grad-iuchair “Sàbhail” a thèid a chleachdadh airson clàraidhean air ath-chluichean a shàbhaladh."
+
+Output.BadPath.Title="Droch-shlighe faidhle"
+Output.BadPath.Text="Chan eil an t-slighe às-chuir a chaidh a rèiteachadh dligheach. Thoir sùil air na roghainnean agad airson dearbhadh gun deach slighe faidhle dhligheach a shuidheachadh."
+
+LogReturnDialog="Chaidh an loga a luchdadh suas"
+LogReturnDialog.CopyURL="Dèan lethbhreac dhen URL"
+LogReturnDialog.ErrorUploadingLog="Mearachd le luchdadh suas an loga"
+
+LicenseAgreement="Aonta ceadachais"
+LicenseAgreement.PleaseReview="Thoir sùil air teirmichean a’ cheadachais mus cleachd thu OBS. Le cleachdadh a’ phrògraim seo, aidichidh tu gun do leugh thu is gun aontaich thu ri teirmichean ceadachas GNU General Public License v2.0. Sgrolaich sìos airson an corr dhen aonta fhaicinn."
+LicenseAgreement.ClickIAgreeToContinue="Ma ghabhas tu ri teirmichean an aonta, briog air “Gabhaidh mi ris” airson leantainn air adhart. Feumaidh tu gabhail ris an aonta airson OBS a chleachdadh."
+LicenseAgreement.IAgree="Gabhaidh mi ris"
+LicenseAgreement.Exit="Fàg an-seo"
+
+Remux.SourceFile="Clàradh OBS"
+Remux.TargetFile="Faidhle amais"
+Remux.Remux="Iompaich"
+Remux.OBSRecording="Clàradh OBS"
+Remux.FinishedTitle="Tha an t-iompachadh deiseil"
+Remux.Finished="Chaidh an clàradh iompachadh"
+Remux.FinishedError="Chaidh an clàradh iompachadh ach ’s ma dh’fhaoidte nach eil am faidhle coileanta"
+Remux.SelectRecording="Tagh clàradh OBS…"
+Remux.SelectTarget="Tagh faidhle amais…"
+Remux.FileExistsTitle="Tha am faidhle amais ann"
+Remux.FileExists="Tha am faidhle amais ann mu thràth, a bheil thu airson seo a chur ’na àite?"
+Remux.ExitUnfinishedTitle="’Ga iompachadh"
+Remux.ExitUnfinished="Chan eil an t-iompachadh deiseil agus dh’fhaoidte nach gabh am faidhle amais a cleachdadh ma chuireas tu stad air an-dràsta.\nA bheil thu cinnteach gu bheil thu airson stad a chur air an iompachadh?"
+
+UpdateAvailable="Tha ùrachadh ri fhaighinn"
+UpdateAvailable.Text="Tha tionndadh %1.%2.%3 ri fhaighinn a-nis. Briog an-seo gus a luchdadh a-nuas"
+
+Basic.DesktopDevice1="Fuaim an deasg"
+Basic.DesktopDevice2="Fuaim an deasg 2"
+Basic.AuxDevice1="Micreofon/Taic"
+Basic.AuxDevice2="Micreofon/Taic 2"
+Basic.AuxDevice3="Micreofon/Taic 3"
+Basic.AuxDevice4="Micreofon/Taic 4"
+
+Basic.Scene="Sealladh"
+Basic.DisplayCapture="Glacadh an uidheim-taisbeanaidh"
+
+Basic.Main.PreviewConextMenu.Enable="Cuir an ro-shealladh an comas"
+
+ScaleFiltering="Criathradh sgèilidh"
+ScaleFiltering.Point="Puing"
+ScaleFiltering.Bilinear="Dà-loidhneach"
+ScaleFiltering.Bicubic="Dà-chiùbach"
+ScaleFiltering.Lanczos="Lanczos"
+
+Deinterlacing="Dì-fhilleadh"
+Deinterlacing.Discard="Tilg air falbh"
+Deinterlacing.Retro="Retro"
+Deinterlacing.Blend="Co-mheasgachadh"
+Deinterlacing.Blend2x="Co-mheasgachadh 2x"
+Deinterlacing.Linear="Loidhneach"
+Deinterlacing.Linear2x="Loidhneach 2x"
+Deinterlacing.Yadif="Yadif"
+Deinterlacing.Yadif2x="Yadif 2x"
+Deinterlacing.TopFieldFirst="An raon air a’ bharr an toiseach"
+Deinterlacing.BottomFieldFirst="An raon aig a’ bhonn an toiseach"
+
+VolControl.SliderUnmuted="Sleamhnachan àirde airson “%1”: %2"
+VolControl.SliderMuted="Sleamhnachan àirde airson “%1”: %2 (mùchte an-dràsta)"
+VolControl.Mute="Mùch “%1”"
+VolControl.Properties="Roghainnean airson “%1”"
+
+Basic.Main.AddSceneDlg.Title="Cuir sealladh ris"
+Basic.Main.AddSceneDlg.Text="Cuir a-steach ainm an t-seallaidh"
+
+Basic.Main.DefaultSceneName.Text="Sealladh %1"
+
+Basic.Main.AddSceneCollection.Title="Cuir cruinneachadh sheallaidhean ris"
+Basic.Main.AddSceneCollection.Text="Cuir a-steach ainm a’ chruinneachaidh sheallaidhean"
+
+Basic.Main.RenameSceneCollection.Title="Thoir ainm ùr air a’ chruinneachadh sheallaidhean"
+
+AddProfile.Title="Cuir pròifil ris"
+AddProfile.Text="Cuir a-steach ainm na pròifil"
+
+RenameProfile.Title="Thoir ainm ùr air a’ phròifil"
+
+Basic.Main.MixerRename.Title="Thoir ainm ùr air an tùs fuaime"
+Basic.Main.MixerRename.Text="Cuir a-steach ainm an tùis fuaime"
+
+
+Basic.Main.PreviewDisabled="Tha an ro-shealladh às comas an-dràsta"
+
+Basic.SourceSelect="Cruthaich/Tagh tùs"
+Basic.SourceSelect.CreateNew="Cruthaich tùs ùr"
+Basic.SourceSelect.AddExisting="Cuir ris tùs a tha ann"
+Basic.SourceSelect.AddVisible="Seall an tùs"
+
+Basic.PropertiesWindow="Roghainnean airson “%1”"
+Basic.PropertiesWindow.AutoSelectFormat="%1 (taghadh fèin-obrachail: %2)"
+Basic.PropertiesWindow.SelectColor="Tagh dath"
+Basic.PropertiesWindow.SelectFont="Tagh cruth-clò"
+Basic.PropertiesWindow.ConfirmTitle="Chaidh na roghainnean atharrachadh"
+Basic.PropertiesWindow.Confirm="Tha atharraichean gun sàbhaladh ann. A bheil thu airson an cumail?"
+Basic.PropertiesWindow.NoProperties="Chan eil roghainn ann"
+Basic.PropertiesWindow.AddFiles="Cuir faidhlichean ris"
+Basic.PropertiesWindow.AddDir="Cuir pasgan ris"
+Basic.PropertiesWindow.AddURL="Cuir slighe/URL ris"
+Basic.PropertiesWindow.AddEditableListDir="Cuir pasgan ri “%1”"
+Basic.PropertiesWindow.AddEditableListFiles="Cuir faidhlichean ri “%1”"
+Basic.PropertiesWindow.AddEditableListEntry="Cuir innteart ri “%1”"
+Basic.PropertiesWindow.EditEditableListEntry="Deasaich innteart o “%1”"
+
+Basic.PropertiesView.FPS.Simple="Luachan FPS simplidh"
+Basic.PropertiesView.FPS.Rational="Luachan FPS reusanta"
+Basic.PropertiesView.FPS.ValidFPSRanges="Rainsean FPS dligheach:"
+
+Basic.InteractionWindow="Conaltradh le “%1”"
+
+Basic.StatusBar.Reconnecting="Gun cheangal, ’ga ath-cheangal an ceann %2 diog(an) (oidhirp %1)"
+Basic.StatusBar.AttemptingReconnect="A’ feuchainn ri ath-cheangal… (oidhirp %1)"
+Basic.StatusBar.ReconnectSuccessful="Chaidh ath-cheangal"
+Basic.StatusBar.Delay="Dàil (%1d)"
+Basic.StatusBar.DelayStartingIn="Dàil (a’ tòiseachadh an ceann %1d)"
+Basic.StatusBar.DelayStoppingIn="Dàil (a’ stad an ceann %1d)"
+Basic.StatusBar.DelayStartingStoppingIn="Dàil (a’ stad an ceann %1d, a’ tòiseachadh an ceann %1d)"
+
+Basic.Filters="Criathragan"
+Basic.Filters.AsyncFilters="Criathragan fuaime/video"
+Basic.Filters.AudioFilters="Criathragan fuaime"
+Basic.Filters.EffectFilters="Criathragan èifeachd"
+Basic.Filters.Title="Crathragan airson “%1”"
+Basic.Filters.AddFilter.Title="Ainm na criathraige"
+Basic.Filters.AddFilter.Text="Cuir a-steach ainm na criathraige"
+
+Basic.TransformWindow="Tar-mhùthadh air nì an t-seallaidh"
+Basic.TransformWindow.Position="Ionad"
+Basic.TransformWindow.Rotation="Cuairteachadh"
+Basic.TransformWindow.Size="Meud"
+Basic.TransformWindow.Alignment="Co-thaobhadh ionaid"
+Basic.TransformWindow.BoundsType="Seòrsa a’ bhogsa-iadhaidh"
+Basic.TransformWindow.BoundsAlignment="Co-thaobhadh sa bhogsa-iadhaidh"
+Basic.TransformWindow.Bounds="Meud a’ bhogsa-iadhaidh"
+Basic.TransformWindow.Crop="Bearr"
+
+Basic.TransformWindow.Alignment.TopLeft="Taobh clì aig a’ bharr"
+Basic.TransformWindow.Alignment.TopCenter="Sa mheadhan aig a’ bharr"
+Basic.TransformWindow.Alignment.TopRight="Taobh deas aig a’ bharr"
+Basic.TransformWindow.Alignment.CenterLeft="Sa mheadhan air an taobh chlì"
+Basic.TransformWindow.Alignment.Center="Sa mheadhan"
+Basic.TransformWindow.Alignment.CenterRight="Sa mheadhan air an taobh deas"
+Basic.TransformWindow.Alignment.BottomLeft="Taobh clì aig a’ bhonn"
+Basic.TransformWindow.Alignment.BottomCenter="Sa mheadhan aig a’ bhonn"
+Basic.TransformWindow.Alignment.BottomRight="Taobh deas aig a’ bhonn"
+
+Basic.TransformWindow.BoundsType.None="Gun iadhadh"
+Basic.TransformWindow.BoundsType.MaxOnly="Am meud as motha a-mhàin"
+Basic.TransformWindow.BoundsType.ScaleInner="Sgèilich ri iadhadh a-staigh"
+Basic.TransformWindow.BoundsType.ScaleOuter="Sgèilich ri iadhadh a-muigh"
+Basic.TransformWindow.BoundsType.ScaleToWidth="Sgèilich ri leud an iadhaidh"
+Basic.TransformWindow.BoundsType.ScaleToHeight="Sgèilich ri àirde an iadhaidh"
+Basic.TransformWindow.BoundsType.Stretch="Sìn gun iadhadh"
+
+Basic.Main.AddSourceHelp.Title="Chan urrainn dhuinn an tùs a chur ris"
+Basic.Main.AddSourceHelp.Text="Feumaidh do shealladh a bhith agad mus cuir thu tùs ris."
+
+Basic.Main.Scenes="Seallaidhean"
+Basic.Main.Sources="Tùsan"
+Basic.Main.Controls="Uidheaman-smachd"
+Basic.Main.Connecting="’Ga cheangal…"
+Basic.Main.StartRecording="Tòisich air clàradh"
+Basic.Main.StartReplayBuffer="Tòisich air bufair ath-chluiche"
+Basic.Main.StartStreaming="Tòisich air sruthadh"
+Basic.Main.StopRecording="Cuir stad air a’ chlàradh"
+Basic.Main.StoppingRecording="A’ cur stad air a’ chlàradh…"
+Basic.Main.StopReplayBuffer="Cuir stad air bufair na h-ath-chluiche"
+Basic.Main.StoppingReplayBuffer="A’ cur stad air bufair na h-ath-chluiche…"
+Basic.Main.StopStreaming="Cuir stad air an t-sruthadh"
+Basic.Main.StoppingStreaming="A’ cur stad air an t-sruthadh…"
+Basic.Main.ForceStopStreaming="Cuir stad air an t-sruthadh (leig seachad an dàil)"
+Basic.Main.Group="Buidheann %1"
+Basic.Main.GroupItems="Buidhnich na thagh thu"
+Basic.Main.Ungroup="Sgaoil am buidheann"
+
+Basic.MainMenu.File="&Faidhle"
+Basic.MainMenu.File.Export="Às-phor&taich"
+Basic.MainMenu.File.Import="&Ion-phortaich"
+Basic.MainMenu.File.ShowRecordings="Seall na &clàraidhean"
+Basic.MainMenu.File.Remux="Io&mpaich na clàraidhean"
+Basic.MainMenu.File.Settings="&Roghainnean"
+Basic.MainMenu.File.ShowSettingsFolder="Seall pasgan nan roghainnean"
+Basic.MainMenu.File.ShowProfileFolder="Seall pasgan na pròifil"
+Basic.MainMenu.AlwaysOnTop="&Air uachdar an-còmhnaidh"
+Basic.MainMenu.File.Exit="&Fàg an-seo"
+
+Basic.MainMenu.Edit="D&easaich"
+Basic.MainMenu.Edit.Undo="&Neo-dhèan"
+Basic.MainMenu.Edit.Redo="Ath-&dhèan"
+Basic.MainMenu.Edit.UndoAction="&Neo-dhèan $1"
+Basic.MainMenu.Edit.RedoAction="Ath-&dhèan $1"
+Basic.MainMenu.Edit.LockPreview="G&lais an ro-shealladh"
+Basic.MainMenu.Edit.Scale="&Sgèileadh an ro-sheallaidh"
+Basic.MainMenu.Edit.Scale.Window="Sgèilich ris an uinneag"
+Basic.MainMenu.Edit.Scale.Canvas="Canabhas (%1x%2)"
+Basic.MainMenu.Edit.Scale.Output="Às-chur (%1x%2)"
+Basic.MainMenu.Edit.Transform="&Tar-mhùth"
+Basic.MainMenu.Edit.Transform.EditTransform="D&easaich an tar-mhùthadh…"
+Basic.MainMenu.Edit.Transform.CopyTransform="Dèan lethbhreac dhen tar-mhùthadh"
+Basic.MainMenu.Edit.Transform.PasteTransform="Cuir ann tar-mhùthadh"
+Basic.MainMenu.Edit.Transform.ResetTransform="Ath-&shuidhich an tar-mhùthadh"
+Basic.MainMenu.Edit.Transform.Rotate90CW="Cuairtich gu deiseil le 90 ceum"
+Basic.MainMenu.Edit.Transform.Rotate90CCW="Cuairtich gu tuathail le 90 ceum"
+Basic.MainMenu.Edit.Transform.Rotate180="Cuairtich le 180 ceum"
+Basic.MainMenu.Edit.Transform.FlipHorizontal="T&hoir flip air a’ chòmhnard"
+Basic.MainMenu.Edit.Transform.FlipVertical="&Thoir flip gu h-inghearach"
+Basic.MainMenu.Edit.Transform.FitToScreen="Co-&fhreagair ri meud na sgrìn"
+Basic.MainMenu.Edit.Transform.StretchToScreen="&Sìn gu meud na sgrìn"
+Basic.MainMenu.Edit.Transform.CenterToScreen="Cuir air &meadhan na sgrìn"
+Basic.MainMenu.Edit.Order="Òrdu&gh"
+Basic.MainMenu.Edit.Order.MoveUp="Gluais s&uas"
+Basic.MainMenu.Edit.Order.MoveDown="Gluais &sìos"
+Basic.MainMenu.Edit.Order.MoveToTop="Gluais gun bh&arr"
+Basic.MainMenu.Edit.Order.MoveToBottom="Gluais gun &bhonn"
+Basic.MainMenu.Edit.AdvAudio="Roghainnean &adhartach na fuaime"
+
+Basic.MainMenu.View="&Seall"
+Basic.MainMenu.View.Toolbars="&Bàraichean-inneal"
+Basic.MainMenu.View.Docks="Docaichean"
+Basic.MainMenu.View.Docks.ResetUI="Ath-shuidhich an eadar-aghaidh"
+Basic.MainMenu.View.Docks.LockUI="Glais an eadar-aghaidh"
+Basic.MainMenu.View.Toolbars.Listboxes="Bogsaichean-&liosta"
+Basic.MainMenu.View.SceneTransitions="Tar-&mhùthaidhean an t-seallaidh"
+Basic.MainMenu.View.StatusBar="Bàr-s&taide"
+Basic.MainMenu.View.Fullscreen.Interface="Eadar-aghaidh làn-sgrìn"
+
+Basic.MainMenu.SceneCollection="Cruinneachadh &sheallaidhean"
+Basic.MainMenu.Profile="&Pròifil"
+Basic.MainMenu.Profile.Import="Ion-phortaich pròifil"
+Basic.MainMenu.Profile.Export="Às-phortaich a’ phròifil"
+Basic.MainMenu.SceneCollection.Import="Ion-phortaich cruinneachadh sheallaidhean"
+Basic.MainMenu.SceneCollection.Export="Às-phortaich cruinneachadh sheallaidhean"
+Basic.MainMenu.Profile.Exists="Tha a’ phròifil ann mu thràth"
+Basic.MainMenu.SceneCollection.Exists="Tha an cruinneachadh sheallaidhean ann mu thràth"
+
+Basic.MainMenu.Tools="Innea&lan"
+
+Basic.MainMenu.Help="Cob&hair"
+Basic.MainMenu.Help.HelpPortal="&Portal na cobharach"
+Basic.MainMenu.Help.Website="&Tadhail air an làrach-lìn"
+Basic.MainMenu.Help.Logs="Faidhlichean an &loga"
+Basic.MainMenu.Help.Logs.ShowLogs="&Seall faidhlichean an loga"
+Basic.MainMenu.Help.Logs.UploadCurrentLog="Luchdai&ch suas faidhle an loga làithrich"
+Basic.MainMenu.Help.Logs.UploadLastLog="&Luchdaich suas faidhle an loga mu dheireadh"
+Basic.MainMenu.Help.Logs.ViewCurrentLog="&Seall an loga làithreach"
+Basic.MainMenu.Help.CheckForUpdates="Thoir sùil airson ùrachaidhean"
+Basic.MainMenu.Help.CrashLogs="Aithis&gean tuislidh"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="&Seall na h-aithisgean tuislidh"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="&Luchdaich suas an aithisg tuislidh mu dheireadh"
+
+Basic.Settings.ProgramRestart="Feumaidh tu am prògram ath-thòiseachadh gus na roghainnean seo a chur an sàs."
+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"
+Basic.Settings.General.WarnBeforeStartingStream="Seall còmhradh dearbhaidh mus dèid sruthadh a thòiseachadh"
+Basic.Settings.General.WarnBeforeStoppingStream="Seall còmhradh dearbhaidh mus dèid stad a chur air sruthadh"
+Basic.Settings.General.Projectors="Proiseactaran"
+Basic.Settings.General.HideProjectorCursor="Falaich an cùrsair os cionn proiseactaran"
+Basic.Settings.General.ProjectorAlwaysOnTop="Cuir na proiseactaran air uachdar an-còmhnaidh"
+Basic.Settings.General.Snapping="Greimeachadh co-thaobhadh nan tùsan"
+Basic.Settings.General.ScreenSnapping="Greimich na tùsan ri oir na sgrìn"
+Basic.Settings.General.CenterSnapping="Greimich na tùsan ris a’ mheadhan"
+Basic.Settings.General.SourceSnapping="Greimich na tùsan ri tùsan eile"
+Basic.Settings.General.SnapDistance="Mothalachd a’ ghreimeachaidh"
+Basic.Settings.General.RecordWhenStreaming="Clàraich gu fèil-obrachail nuair a thèid rud a shruthadh"
+Basic.Settings.General.KeepRecordingWhenStreamStops="Cum an clàradh nuair a thèid stad a chur air an t-sruthadh"
+Basic.Settings.General.ReplayBufferWhileStreaming="Tòisich bufair na h-ath-chluiche gu fèin-obrachail nuair a bhios rud ’ga shruthadh"
+Basic.Settings.General.KeepReplayBufferStreamStops="Cum bufair na h-ath-chluiche gnìomhach nuair a thèid stad a chur air sruthadh"
+Basic.Settings.General.SysTray="Treidhe an t-siostaim"
+Basic.Settings.General.SysTrayWhenStarted="Fìor-lùghdaich gu treidhe an t-siostaim aig an toiseach"
+Basic.Settings.General.SystemTrayHideMinimize="Fìor-lùghdaich gu treidhe an t-siostaim seach bàr nan saothair an-còmhnaidh"
+Basic.Settings.General.SaveProjectors="Sàbhail na proiseactaran nuair a thèid fàgail an-seo"
+Basic.Settings.General.SwitchOnDoubleClick="Tar-mhùth gu sealladh le briogadh dùbailte"
+Basic.Settings.General.StudioPortraitLayout="Cuir an comas co-dhealbhachd portraid/inghearach"
+Basic.Settings.General.Multiview="Ioma-shealladh"
+Basic.Settings.General.Multiview.MouseSwitch="Briog airson leum a ghearradh eadar seallaidhean"
+Basic.Settings.General.Multiview.DrawSourceNames="Seall ainmean nan seallaidhean"
+Basic.Settings.General.Multiview.DrawSafeAreas="Tarraing raointean sàbhailte (EBU R 95)"
+Basic.Settings.General.MultiviewLayout="Co-dhealbhachd an ioma-sheallaidh"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Còmhnard, barr (8 seallaidhean)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Còmhnard, bonn (8 seallaidhean)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Inghearach, clì (8 seallaidhean)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Inghearach, deas (8 seallaidhean)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Còmhnard, barr (24 sealladh)"
+
+Basic.Settings.Stream="Sruthadh"
+Basic.Settings.Stream.StreamType="Seòrsa an t-sruthaidh"
+
+Basic.Settings.Output="Às-chur"
+Basic.Settings.Output.Format="Fòrmat a’ chlàraidh"
+Basic.Settings.Output.Encoder="Inneal-còdachaidh"
+Basic.Settings.Output.SelectDirectory="Tagh pasgan a’ chlàraidh"
+Basic.Settings.Output.SelectFile="Tagh faidhle clàraidh"
+Basic.Settings.Output.EnforceBitrate="Èignich cuingeachaidhean reat bhiotaichean aig an t-seirbheis sruthaidh"
+Basic.Settings.Output.Mode="Modh an às-chuir"
+Basic.Settings.Output.Mode.Simple="Simplidh"
+Basic.Settings.Output.Mode.Adv="Adhartach"
+Basic.Settings.Output.Mode.FFmpeg="Às-chur FFmpeg"
+Basic.Settings.Output.UseReplayBuffer="Cuir an comas bufair ath-chluiche"
+Basic.Settings.Output.ReplayBuffer.SecondsMax="Ùine as motha nan ath-chluichean (diog)"
+Basic.Settings.Output.ReplayBuffer.MegabytesMax="A’ chuimhne as motha (meaga-baidht)"
+Basic.Settings.Output.ReplayBuffer.Estimate="Tuairmse air cleachdadh na cuimhne: %1 MB"
+Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Chan urrainn dhuinn tuairmse a dhèanamh air cleachdadh na cuimhne. Suidhich crìoch as motha na cuimhne."
+Basic.Settings.Output.ReplayBuffer.HotkeyMessage="(An aire: Dèan cinnteach gun suidhich thu grad-iuchair airson bufair na h-ath-chluiche ann an earrann nan grad-iuchraichean)"
+Basic.Settings.Output.ReplayBuffer.Prefix="Ro-leasachan air ainmean faidhle aig bufair na h-ath-chluiche"
+Basic.Settings.Output.ReplayBuffer.Suffix="Iar-leasachan"
+Basic.Settings.Output.Simple.SavePath="Slighe a’ chlàraidh"
+Basic.Settings.Output.Simple.RecordingQuality="Càileachd a’ chlàraidh"
+Basic.Settings.Output.Simple.RecordingQuality.Stream="Co-ionnann ri tè an t-sruthaidh"
+Basic.Settings.Output.Simple.RecordingQuality.Small="Càileachd àrd, faidhle meadhanach mòr"
+Basic.Settings.Output.Simple.RecordingQuality.HQ="Cha ghabh diofar aithneachadh, faidhle mòr"
+Basic.Settings.Output.Simple.RecordingQuality.Lossless="Càileachd gun chall, faidhle uabhasach mòr"
+Basic.Settings.Output.Simple.Warn.VideoBitrate="Rabhadh: Thèid reat bhiotaichean video an t-sruthaidh a shuidheachadh air %1, seo a’ chrìoch as àirde aig an t-seirbheis sruthaidh làithreach. Ma tha thu cinnteach bu bheil thu airson corr is %1 a chleachdadh, cuir an comas roghainnean adhartach an inneil-chòdachaidh agus thoir a’ chromag far “Èignich cuingeachaidhean reat bhiotaichean aig an t-seirbheis sruthaidh”."
+Basic.Settings.Output.Simple.Warn.AudioBitrate="Rabhadh: Thèid reat bhiotaichean fuaime an t-sruthaidh a shuidheachadh air %1, seo a’ chrìoch as àirde aig an t-seirbheis sruthaidh làithreach. Ma tha thu cinnteach bu bheil thu airson corr is %1 a chleachdadh, cuir an comas roghainnean adhartach an inneil-chòdachaidh agus thoir a’ chromag far “Èignich cuingeachaidhean reat bhiotaichean aig an t-seirbheis sruthaidh”."
+Basic.Settings.Output.Simple.Warn.Encoder="Rabhadh: Ma nì thu clàradh le inneal-còdachaidh bathair-bhog air nach eil an aon chàileachd ’s a th’ air an t-sruthadh, bi feum air barrachd cleachdadh a’ CPU nuair a bhios tu a’ sruthadh ’s a’ clàradh aig an aon àm."
+Basic.Settings.Output.Simple.Warn.Lossless="Rabhadh: Cruthaichidh càileachd gun chall faidhlichean uabhasach mòr! Faodaidh càileachd gun chall corr is 7 giga-baidht a dh’àite a chleachdadh air an diosg gach mionaid ma tha an dùmhlachd-bhreacaidh agus an reat fhrèamaichean àrd. Cha mholamaid càileachd gun chall airson clàraidhean fada ach ma tha torr àite agad air an diosg."
+Basic.Settings.Output.Simple.Warn.Lossless.Msg="A bheil thu cinnteach gu bheil thu airson càileachd gun chall a chleachdadh?"
+Basic.Settings.Output.Simple.Warn.Lossless.Title="Rabhadh a thaobh càileachd gun chall!"
+Basic.Settings.Output.Simple.Warn.MultipleQSV="Rabhadh: Chan urrainn dhut iomadh inneal-còdachaidh QSV fa leth a chleachdadh nuair a bhios tu a’ sruthadh ’s a clàradh aig an aon àm. Nam bu toigh leat sruthadh is clàradh aig an aon àm, feuch an atharraich thu inneal-còdachaidh a’ chlàraidh no an t-sruthaidh."
+Basic.Settings.Output.Simple.Encoder.Software="Bathar-bog (x264)"
+Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Bathar-cruaidh (QSV)"
+Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Bathar-cruaidh (AMD)"
+Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Bathar-cruaidh (NVENC)"
+Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Bathar-bog (ro-shuidheachadh air x264 le cleachdadh a’ CPU ìosal, meudaichidh seo na faidhlichean)"
+Basic.Settings.Output.VideoBitrate="Reat bhiotaichean a’ video"
+Basic.Settings.Output.AudioBitrate="Reat bhiotaichean na fuaime"
+Basic.Settings.Output.Reconnect="Ath-cheangail gu fèin-obrachail"
+Basic.Settings.Output.RetryDelay="Dàil na feuchainn a-rithist (diog)"
+Basic.Settings.Output.MaxRetries="Oidhirpean as motha"
+Basic.Settings.Output.Advanced="Cuir an comas roghainnean adhartach an inneil-chòdachaidh"
+Basic.Settings.Output.EncoderPreset="Ro-shuidheachadh an inneil-chòdachaidh (nas àirde = nas lugha dhen CPU)"
+Basic.Settings.Output.CustomEncoderSettings="Roghainnean gnàthaichte an inneil-chòdachaidh"
+Basic.Settings.Output.CustomMuxerSettings="Roghainnean gnàthaichte an iompaicheir"
+Basic.Settings.Output.NoSpaceFileName="Gin ainm faidhle gun spàs"
+
+Basic.Settings.Output.Adv.Rescale="Ath-sgèilich an t-às-chur"
+Basic.Settings.Output.Adv.AudioTrack="Traca fuaime"
+Basic.Settings.Output.Adv.Streaming="Sruthadh"
+Basic.Settings.Output.Adv.ApplyServiceSettings="Èignich na roghainnean inneil-chòdachaidh aig an t-seirbheis sruthaidh"
+Basic.Settings.Output.Adv.Audio.Track1="Traca 1"
+Basic.Settings.Output.Adv.Audio.Track2="Traca 2"
+Basic.Settings.Output.Adv.Audio.Track3="Traca 3"
+Basic.Settings.Output.Adv.Audio.Track4="Traca 4"
+Basic.Settings.Output.Adv.Audio.Track5="Traca 5"
+Basic.Settings.Output.Adv.Audio.Track6="Traca 6"
+
+Basic.Settings.Output.Adv.Recording="Clàradh"
+Basic.Settings.Output.Adv.Recording.Type="Seòrsa"
+Basic.Settings.Output.Adv.Recording.Type.Standard="Stannardach"
+Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Às-chur gnàthaichte (FFmpeg)"
+Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Cleachd inneal-còdachaidh sruthaidh)"
+Basic.Settings.Output.Adv.Recording.Filename="Fòrmatadh nan ainmean faidhle"
+Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Sgrìobh thairis air faidhle ma tha e ann"
+Basic.Settings.Output.Adv.FFmpeg.Type="Seòrsa às-chur FFmpeg"
+Basic.Settings.Output.Adv.FFmpeg.Type.URL="Às-chuir gu URL"
+Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Às-chuir gu faidhle"
+Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="Fòrmatan clàraidh cumanta"
+Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Na h-uile faidhle"
+Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Slighe faidhle no URL"
+Basic.Settings.Output.Adv.FFmpeg.Format="Fòrmat an t-soithich"
+Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Fuaim"
+Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Video"
+Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Am fòrmat tùsail"
+Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Tuairisgeul air fòrmat an t-soithich"
+Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Chaidh codec fuaime/video a thuairmeas o shlighe an fhaidhle no URL"
+Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="An t-inneal-còdachaidh tùsail"
+Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Cuir an t-inneal-còdachaidh à comas"
+Basic.Settings.Output.Adv.FFmpeg.VEncoder="Inneal-còdachaidh video"
+Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Roghainnean an inneil-chòdachaidh video (ma tha gin ann)"
+Basic.Settings.Output.Adv.FFmpeg.AEncoder="Inneal-còdachaidh fuaime"
+Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Roghainnean an inneil-chòdachaidh fuaime (ma tha gin ann)"
+Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Roghainnean an iompaicheir (ma tha gin ann)"
+Basic.Settings.Output.Adv.FFmpeg.GOPSize="Eadaramh nam frèamaichean-iuchrach (frèam)"
+Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Seall na h-uile codec (fiù an fheadhainn nach eil co-chòrdail ma dh’fhaoidte)"
+
+FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z"
+
+FilenameFormatting.TT="%CCYY Am bliadhna, ceithir àireamhan\n%YY Am bliadhna, an dà àireamh mu dheireadh (00-99)\n%MM Am mìos ’na àireamh dheicheach (01-12)\n%DD Latha a’ mhìosa, le pada neoni (01-31)\n%hh An uair san fhòrmat 24u (00-23)\n%mm Am mionaid (00-59)\n%ss An diog (00-61)\n%% Samhla %\n%a Gearr-ainm air latha na seachdaine\n%A Ainm slàn air latha na seachdaine\n%b Gearr-ainm a’ mhìosa\n%B Ainm slàn a’ mhìosa\n%d Latha a’ mhìosa le pada neoni (01-31)\n%H An uair san fhòrmat 24u (00-23)\n%I An uair san fhòrmat 12u (01-12)\n%m Am mìos ’na àireamh dheicheach (01-12)\n%M A’ mhionaid (00-59)\n%p Sònrachadh m no f\n%S An diog (00-61)\n%y am bliadhna, an dà àireamh mu dheireadh (00-99)\n%Y Am bliadhna\n%z Frìth-àireamh ISO 8601 o UTC no\n ainm no giorrachadh na roinn-tìde\n%Z Ainm no giorrachadh na roinn-tìde\n"
+
+Basic.Settings.Video="Video"
+Basic.Settings.Video.Adapter="Adaptar video"
+Basic.Settings.Video.BaseResolution="Dùmhlachd-bhreacaidh bhunasach (a’ chanabhais)"
+Basic.Settings.Video.ScaledResolution="Dùmhlachd-bhreacaidh (sgèilichte) an às-chuir"
+Basic.Settings.Video.DownscaleFilter="Criathrag sgèileachaidh"
+Basic.Settings.Video.DisableAeroWindows="Cuir à comas Aero (Windows a-mhàin)"
+Basic.Settings.Video.FPS="FPS"
+Basic.Settings.Video.FPSCommon="Luachan FPS cumanta"
+Basic.Settings.Video.FPSInteger="Luach FPS slàn"
+Basic.Settings.Video.FPSFraction="Luachan FPS bloigheach"
+Basic.Settings.Video.Numerator="Àireamhaiche"
+Basic.Settings.Video.Denominator="Seòrsaiche"
+Basic.Settings.Video.Renderer="Inneal-reandaraidh"
+Basic.Settings.Video.InvalidResolution="Chan eil luach na dùmhlachd-breacaidh dligheach. Feumaidh e a bhith ’na [leud]x[àirde] (can 1920x1080)"
+Basic.Settings.Video.CurrentlyActive="Tha às-chur video gnìomhach an-dràsta. cuir dheth gach às-chur airson roghainnean a’ video atharrachadh."
+Basic.Settings.Video.DisableAero="Cuir à comas Aero"
+
+Basic.Settings.Video.DownscaleFilter.Bilinear="Dà-loidhneach (as luaithe ach sgleò air le sgèilachadh)"
+Basic.Settings.Video.DownscaleFilter.Bicubic="Dà-chiùbach (sgèileachadh geuraichte, 16 sampallan)"
+Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (sgèileachadh geuraichte, 32 sampall)"
+
+Basic.Settings.Audio="Fuaim"
+Basic.Settings.Audio.SampleRate="Reat shampallan"
+Basic.Settings.Audio.Channels="Seanailean"
+Basic.Settings.Audio.MeterDecayRate="Reat crìonaidh a’ mheidheadair-fhuaime"
+Basic.Settings.Audio.MeterDecayRate.Fast="Luath"
+Basic.Settings.Audio.MeterDecayRate.Medium="Meadhanach (PPM seòrsa I)"
+Basic.Settings.Audio.MeterDecayRate.Slow="Slaodach (PPM seòrsa II)"
+Basic.Settings.Audio.PeakMeterType="Seòrsa a’ mheidheadair-bharran"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Ball-sampaill de bharr"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Barr fìrinneach (cleachdadh nas àirde a’ CPU)"
+Basic.Settings.Audio.MultiChannelWarning.Enabled="RABHADH: Tha fuaim cuairteachaidh an comas."
+Basic.Settings.Audio.MultichannelWarning="Ma tha thu a’ dèanamh sruthadh, dearbh gun doir an t-seirbheis sruthaidh agad taic an dà chuid ri ion-chur is cluich fuaime cuairteachaidh. Mar eisimpleir, cuiridh Twitch, Facebook 360 Live, Mixer RTMP is Smashcast làn-taic ri fuaim cuairteachaidh. Ged a ghabhas Facebook Live is youTube Live ri sruthan fuaime cuairteachaidh, nì Facebook Live measgachadh sìos stereo dheth agus cha chluich YouTube live ach dà sheanail.\n\nTha criathragan fuaime OBS co-chòrdail ri fuaim cuairteachaidh ged nach doir sinn barantas gun obraich plugain VST."
+Basic.Settings.Audio.MultichannelWarning.Title="A bheil thu airson fuaim cuairteachaidh a chur an comas?"
+Basic.Settings.Audio.MultichannelWarning.Confirm="A bheil thu cinnteach gu bheil thu airson fuaim cuairteachaidh a chur an comas?"
+Basic.Settings.Audio.DesktopDevice="Uidheam fuaime an deasg"
+Basic.Settings.Audio.DesktopDevice2="Uidheam fuaime an deasg 2"
+Basic.Settings.Audio.AuxDevice="Uidheam fuaime micreofoin/taice"
+Basic.Settings.Audio.AuxDevice2="Uidheam fuaime micreofoin/taice 2"
+Basic.Settings.Audio.AuxDevice3="Uidheam fuaime micreofoin/taice 3"
+Basic.Settings.Audio.EnablePushToMute="Cuir an comas brùth-airson-mùchadh"
+Basic.Settings.Audio.PushToMuteDelay="Dàil air brùth-airson-mùchadh"
+Basic.Settings.Audio.EnablePushToTalk="Cuir an comas brùth-airson-bruidhinn"
+Basic.Settings.Audio.PushToTalkDelay="Dàil air brùth-airson-bruidhinn"
+Basic.Settings.Audio.UnknownAudioDevice="[Chan eil uidheam ceangailte no ri fhaighinn]"
+
+Basic.Settings.Advanced="Adhartach"
+Basic.Settings.Advanced.General.ProcessPriority="Prìomhachas a’ phròiseis"
+Basic.Settings.Advanced.General.ProcessPriority.High="Àrd"
+Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Nas àirde na àbhaisteach"
+Basic.Settings.Advanced.General.ProcessPriority.Normal="Àbhaisteach"
+Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Nas ìsle na àbhaisteach"
+Basic.Settings.Advanced.General.ProcessPriority.Idle="’Na tàmh"
+Basic.Settings.Advanced.FormatWarning="Rabhadh: Chaidh fòrmatan datha seach NV12 a dhealbhachadh a chum clàraidh agus cha molamaid airson sruthadh iad. Cleachdaidh an sruthadh barrachd dhen CPU ri linn iompachadh air fòrmat nan dathan."
+Basic.Settings.Advanced.Audio.BufferingTime="Ùine bufair na fuaime"
+Basic.Settings.Advanced.Video.ColorFormat="Fòrmat nan dathan"
+Basic.Settings.Advanced.Video.ColorSpace="Spàs dhathan YUV"
+Basic.Settings.Advanced.Video.ColorRange="Rainse dhathan YUV"
+Basic.Settings.Advanced.Video.ColorRange.Partial="Leth-phàirteach"
+Basic.Settings.Advanced.Video.ColorRange.Full="Làn"
+Basic.Settings.Advanced.Audio.MonitoringDevice="Uidheam sgrùdadh fuaime"
+Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Tùsail"
+Basic.Settings.Advanced.Audio.DisableAudioDucking="Cuir à comas tumadh fuaime Windows"
+Basic.Settings.Advanced.StreamDelay="Dàil an t-sruthaidh"
+Basic.Settings.Advanced.StreamDelay.Duration="Faide (diog)"
+Basic.Settings.Advanced.StreamDelay.Preserve="Glèidh puing a’ ghearraidh (meudaich an dàil) nuair a nithear ath-cheangal"
+Basic.Settings.Advanced.StreamDelay.MemoryUsage="Tuairmse air cleachdadh na cuimhne: %1 MB"
+Basic.Settings.Advanced.Network="Lìonra"
+Basic.Settings.Advanced.Network.BindToIP="Nasg ri IP"
+Basic.Settings.Advanced.Network.EnableNewSocketLoop="Cuir an comas an còd lìonraidh ùr"
+Basic.Settings.Advanced.Network.EnableLowLatencyMode="Modh foillidheachd ìosail"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Cuir à comas na grad-iuchraichean nuair a bhios am fòcas air a’ phrìomh-uinneag"
+
+Basic.AdvAudio="Roghainnean adhartach na fuaime"
+Basic.AdvAudio.Name="Ainm"
+Basic.AdvAudio.Volume="Àirde (%)"
+Basic.AdvAudio.Mono="Measgaich sìos gu mono"
+Basic.AdvAudio.Panning="Panachadh"
+Basic.AdvAudio.SyncOffset="Frìth-àireamh an t-sioncronachaidh (ms)"
+Basic.AdvAudio.Monitoring="Sgrùdadh fuaime"
+Basic.AdvAudio.Monitoring.None="Gun sgrùdadh"
+Basic.AdvAudio.Monitoring.MonitorOnly="Sgrùdadh a-mhàin (mùch an t-às-chur)"
+Basic.AdvAudio.Monitoring.Both="Sgrùdadh is às-chur"
+Basic.AdvAudio.AudioTracks="Tracaichean"
+
+Basic.Settings.Hotkeys="Grad-iuchraichean"
+Basic.Settings.Hotkeys.Pair="Nì na co-iuchraichean a tha ’gan co-roinneadh le “%1” toglachadh"
+
+Basic.Hotkeys.SelectScene="Gearr leum dhan t-sealladh"
+
+Basic.SystemTray.Show="Seall"
+Basic.SystemTray.Hide="Falaich"
+
+Basic.SystemTray.Message.Reconnecting="Gun cheangal. ’Ga ath-cheangal…"
+
+Hotkeys.Insert="Cuir a-steach"
+Hotkeys.Delete="Sguab às"
+Hotkeys.Home="Home"
+Hotkeys.End="End"
+Hotkeys.PageUp="Page Up"
+Hotkeys.PageDown="Page Down"
+Hotkeys.NumLock="Num Lock"
+Hotkeys.ScrollLock="Scroll Lock"
+Hotkeys.CapsLock="Caps Lock"
+Hotkeys.Backspace="Backspace"
+Hotkeys.Tab="Tab"
+Hotkeys.Print="Print"
+Hotkeys.Pause="Pause"
+Hotkeys.Left="Gu clì"
+Hotkeys.Right="Gu deas"
+Hotkeys.Up="Suas"
+Hotkeys.Down="Sìos"
+Hotkeys.Windows="Windows"
+Hotkeys.Super="Super"
+Hotkeys.Menu="Menu"
+Hotkeys.Space="Space"
+Hotkeys.NumpadNum="%1 air pada nan àireamh"
+Hotkeys.NumpadMultiply="Iomadachadh air pada nan àireamh"
+Hotkeys.NumpadDivide="Roinneadh air pada nan àireamh"
+Hotkeys.NumpadAdd="Cur ris air pada nan àireamh"
+Hotkeys.NumpadSubtract="Toir air falbh air pada nan àireamh"
+Hotkeys.NumpadDecimal="Puing air pada nan àireamh"
+Hotkeys.AppleKeypadNum="%1 (pada nan àireamh)"
+Hotkeys.AppleKeypadMultiply="* (pada nan àireamh)"
+Hotkeys.AppleKeypadDivide="/ (pada nan àireamh)"
+Hotkeys.AppleKeypadAdd="+ (pada nan àireamh)"
+Hotkeys.AppleKeypadSubtract="- (pada nan àireamh)"
+Hotkeys.AppleKeypadDecimal=". (pada nan àireamh)"
+Hotkeys.AppleKeypadEqual="= (pada nan àireamh)"
+Hotkeys.MouseButton="%1 na luchaige"
+
+Mute="Mùch"
+Unmute="Dì-mhùch"
+Push-to-mute="Brùth-airson-mùchadh"
+Push-to-talk="Brùth-airson-bruidhinn"
+
+SceneItemShow="Seall “%1”"
+SceneItemHide="Falaich “%1”"
+
+OutputWarnings.NoTracksSelected="Feumaidh tu traca no dhà a thaghadh"
+OutputWarnings.MultiTrackRecording="Rabhadh: Tha fòrmatan ann (can FLV) nach cuir taic ri iomadh traca sa chlàradh"
+OutputWarnings.MP4Recording="Rabhadh: Cha ghabh clàraidhean a thèid a shàbhaladh gu MP4 aiseag mura gabh am faidhle a thoirt gu crìch (can ri linn tuisleachaidh, call cumhachd is msaa.). Nam bu toigh leat iomadh traca fuaime a chlàradh, mholamaid gun cleachd thu MKV agus gun iompaich thu an clàradh gu mp4 nuair a bhios e deiseil (Faidhle->Iompaich clàraidhean)"
+
+FinalScene.Title="Sguab às an sealladh"
+FinalScene.Text="Feumaidh do shealladh a bhith ann."
+
+
+
+
diff --git a/UI/data/locale/gl-ES.ini b/UI/data/locale/gl-ES.ini
index 4411f70..c90dfc7 100644
--- a/UI/data/locale/gl-ES.ini
+++ b/UI/data/locale/gl-ES.ini
@@ -413,3 +413,6 @@ OutputWarnings.NoTracksSelected="Debes seleccionar, cando menos, unha pista"
OutputWarnings.MultiTrackRecording="Aviso: certos formatos (caso de FLV) non admiten múltiples pistas para gravar"
+
+
+
diff --git a/UI/data/locale/he-IL.ini b/UI/data/locale/he-IL.ini
index eec2909..0f8e6a0 100644
--- a/UI/data/locale/he-IL.ini
+++ b/UI/data/locale/he-IL.ini
@@ -508,10 +508,6 @@ Basic.Settings.General.SaveProjectors="שמור את המקרנים ביציאה
Basic.Settings.General.SwitchOnDoubleClick="מעבר לסצנה על ידי הקלקה כפולה"
Basic.Settings.General.StudioPortraitLayout="אפשר פריסה אנכית/דיוקן"
Basic.Settings.General.MultiviewLayout="פריסת תצוגה מרובה"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="אופקי, עליון"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="אופקי, תחתון"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="אנכי, שמאל"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="אנכי, ימין"
Basic.Settings.Stream="זרם נתונים"
Basic.Settings.Stream.StreamType="סוג זרם נתונים"
@@ -749,3 +745,6 @@ OutputWarnings.MP4Recording="אזהרה: הקלטות שנשמרו MP4 תהיה
FinalScene.Title="מחק סצינה"
FinalScene.Text="נדרשת סצנה אחת לפחות."
+
+
+
diff --git a/UI/data/locale/hi-IN.ini b/UI/data/locale/hi-IN.ini
new file mode 100644
index 0000000..16c8600
--- /dev/null
+++ b/UI/data/locale/hi-IN.ini
@@ -0,0 +1,124 @@
+
+Language="हिन्दी"
+Region="इंडिया"
+
+OK="ठीक है"
+Apply="लागू करें"
+Cancel="रद्द करें"
+Close="बंद करें"
+Save="सहेजें"
+Discard="छोड़ें"
+Yes="हां"
+No="नहीं"
+Add="जोड़ें"
+Remove="निकालें"
+Rename="नाम बदलें"
+Interact="बातचीत"
+MoveUp="ऊपर ले जाएँ"
+MoveDown="नीचे ले जाएँ"
+Settings="सेटिंग्स"
+Display="प्रदर्शन"
+Name="नाम"
+Exit="निकास"
+Clear="साफ़ करें"
+Revert="पहले जैसा करें"
+Show="दिखाएँ"
+Hide="छुपायें"
+UnhideAll="कुछ ना छिपाएं"
+New="नया"
+Duplicate="दोहरा"
+Left="बाएं"
+Right="दाएँ"
+Top="शीर्ष"
+Bottom="नीचे"
+Hours="घंटे"
+Minutes="मिनट"
+Seconds="सेकंड"
+Copy="प्रतिलिपि"
+Paste="चिपकाएँ"
+Next="अगले"
+Back="वापस"
+
+
+
+BandwidthTest.Region.Asia="एशिया"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/data/locale/hr-HR.ini b/UI/data/locale/hr-HR.ini
index c7ca15a..744d7c6 100644
--- a/UI/data/locale/hr-HR.ini
+++ b/UI/data/locale/hr-HR.ini
@@ -604,3 +604,6 @@ OutputWarnings.NoTracksSelected="Morate odabrati makar jednu traku"
OutputWarnings.MultiTrackRecording="Upozorenje: Određeni formati (kao što je FLV) ne podržavaju više traka po snimku"
+
+
+
diff --git a/UI/data/locale/hu-HU.ini b/UI/data/locale/hu-HU.ini
index d8b91f0..6413a55 100644
--- a/UI/data/locale/hu-HU.ini
+++ b/UI/data/locale/hu-HU.ini
@@ -14,7 +14,7 @@ No="Nem"
Add="Hozzáadás"
Remove="Eltávolítás"
Rename="Átnevezés"
-Interact="Kölcsönhatás"
+Interact="Ráhatás"
Filters="Szűrők"
Properties="Tulajdonságok"
MoveUp="Mozgatás Fel"
@@ -78,6 +78,8 @@ None="Nincs"
StudioMode.Preview="Előnézet"
StudioMode.Program="Program"
ShowInMultiview="Mutatás Multiviewban"
+VerticalLayout="Függőleges elrendezés"
+Group="Csoport"
AlreadyRunning.Title="Az OBS már fut"
AlreadyRunning.Text="Az OBS már fut! Ha nem teljesen biztos benne mit tesz, akkor állítsa le az összes már futó OBS programot. Ha a programot úgy állította be, hogy rendszertálcára minimalizálódjon, akkor ellenőrizze, hogy ott megtalálható e."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Visszajátszás puffer leáll..."
Basic.Main.StopStreaming="Stream leállítása"
Basic.Main.StoppingStreaming="Stream leállítása..."
Basic.Main.ForceStopStreaming="Stream leállítása (Késleltetés elvetése)"
+Basic.Main.Group="Csoport %1"
+Basic.Main.GroupItems="Kijelölt elemek csoportosítása"
+Basic.Main.Ungroup="Csoport megszüntetése"
Basic.MainMenu.File="&Fájl"
Basic.MainMenu.File.Export="&Exportálás"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Eszközök"
Basic.MainMenu.Help="&Segítség"
Basic.MainMenu.Help.HelpPortal="Segítség &Portál"
Basic.MainMenu.Help.Website="Weboldal megtekintése"
+Basic.MainMenu.Help.Discord="Csatlakozás &Discord szerverre"
Basic.MainMenu.Help.Logs="&Naplófájlok"
Basic.MainMenu.Help.Logs.ShowLogs="&Naplófájlok megjelenítése"
Basic.MainMenu.Help.Logs.UploadCurrentLog="&Aktuális Naplófájl feltöltése"
Basic.MainMenu.Help.Logs.UploadLastLog="&Utolsó Naplófájl feltöltése"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Jelenlegi Naplófájl megtekintése"
Basic.MainMenu.Help.CheckForUpdates="Frissítések ellenőrzése"
+Basic.MainMenu.Help.CrashLogs="Hibajelentések (&R)"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Hibajelentések megjelenítése (&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Utolsó Hibajelentés feltöltése (&L)"
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"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Mindig a rendszertálcára minima
Basic.Settings.General.SaveProjectors="Projektorok mentése kilépéskor"
Basic.Settings.General.SwitchOnDoubleClick="Átmenet a jelenetre dupla kattintás esetén"
Basic.Settings.General.StudioPortraitLayout="Portré/függőleges elrendezés engedélyezése"
+Basic.Settings.General.Multiview="MultiView"
+Basic.Settings.General.Multiview.MouseSwitch="Kattintás a jelenetek közötti váltáshoz"
+Basic.Settings.General.Multiview.DrawSourceNames="Jelenetek neveinek megjelenítése"
+Basic.Settings.General.Multiview.DrawSafeAreas="Biztonságos területek kirajzolása (EBU R 95)"
Basic.Settings.General.MultiviewLayout="MultiView elrendezés"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Vízszintes, Felső"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Vízszintes, Alsó"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Függőleges, Bal"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Függőleges, Jobb"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Vízszintes, felső (8 jelenet)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Vízszintes, alsó (8 jelenet)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Függőleges, bal (8 jelenet)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Függőleges, jobb (8 jelenet)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Vízszintes, felső (24 jelenet)"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Stream típusa"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Hangmérő halkulási aránya"
Basic.Settings.Audio.MeterDecayRate.Fast="Gyors"
Basic.Settings.Audio.MeterDecayRate.Medium="Medium (Típus | PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Lassú (Típus II PPM)"
+Basic.Settings.Audio.PeakMeterType="Csúcsmérték Típus"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Csúcsminta"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Valós Csúcs (Magasabb CPU használat)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="Figyelmeztetés: Surround sound hang engedélyezve van."
Basic.Settings.Audio.MultichannelWarning="Ha közvetít, ellenőrizze, hogy a stream szolgáltatója támogatja mind a surround sound kezelést, mind pedig a surround sound lejátszást. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast a legjobb példa, hogy mely platformokon van teljes támogatás. Ellenben a Facebook Live és a YouTube Live minden elfogadja a térhangzású hangot, a Facebook Live lekeveri stereora és a YouTube Live csak két csatornát játszik le.\n\nOBS audio szűrők kompatibilisek a térhangzású hanggal, viszont a VST bővítmények támogatása nem garantált."
Basic.Settings.Audio.MultichannelWarning.Title="Engedélyezi a surround hangzást?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Hálózat"
Basic.Settings.Advanced.Network.BindToIP="IP-hez rendelés"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Új hálózatkezelő kód engedélyezése"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Alacsony késleltetésű mód"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Gyorsbillentyűk letiltása, ha a fő ablak fókuszban van"
Basic.AdvAudio="Speciális hangtulajdonságok"
Basic.AdvAudio.Name="Név"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Figyelem: Az MP4-be mentett állományok javíthata
FinalScene.Title="Jelenet törlése"
FinalScene.Text="Legalább egy jelenetnek lennie kell."
+NoSources.Title="Nincsenek Források"
+NoSources.Text="Úgy fest, hogy nem adott hozzá valamilyen videoforrást, úgyhogy fekete képet fog adni. Biztos benne, hogy ezt kívánja tenni?"
+NoSources.Text.AddSource="Hozzáadhat forrásokat bármikor a + ikonra kattintva a Források doboz alatt a fő ablakban."
+
+ChangeBG="Szín megadása"
+CustomColor="Egyedi szín"
+
+BrowserSource.EnableHardwareAcceleration="Böngészőforrás hardveres támogatásának engedélyezése"
+
diff --git a/UI/data/locale/it-IT.ini b/UI/data/locale/it-IT.ini
index 39d72e9..2a121b4 100644
--- a/UI/data/locale/it-IT.ini
+++ b/UI/data/locale/it-IT.ini
@@ -78,6 +78,8 @@ None="Nessuno"
StudioMode.Preview="Anteprima"
StudioMode.Program="Programma"
ShowInMultiview="Mostra in Vista-Multipla"
+VerticalLayout="Layout verticale"
+Group="Gruppo"
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."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Arresto del buffer di riproduzione in corso..."
Basic.Main.StopStreaming="Ferma trasmissione"
Basic.Main.StoppingStreaming="Arresto trasmissione..."
Basic.Main.ForceStopStreaming="Ferma trasmissione (annulla ritardo)"
+Basic.Main.Group="Gruppo %1"
+Basic.Main.GroupItems="Elementi selezionati"
+Basic.Main.Ungroup="Separa"
Basic.MainMenu.File="&File"
Basic.MainMenu.File.Export="&Esporta"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Strumenti"
Basic.MainMenu.Help="&Aiuto"
Basic.MainMenu.Help.HelpPortal="Portale Aiuto"
Basic.MainMenu.Help.Website="Visita il sito"
+Basic.MainMenu.Help.Discord="Join & Discord Server"
Basic.MainMenu.Help.Logs="File di &log"
Basic.MainMenu.Help.Logs.ShowLogs="&Visualizza i file di Log"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Carica file di log &corrente"
Basic.MainMenu.Help.Logs.UploadLastLog="Carica u<imo file di log"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Vedi attuale file di log"
Basic.MainMenu.Help.CheckForUpdates="Controlla aggiornamenti"
+Basic.MainMenu.Help.CrashLogs="Segnalazione c&rash "
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Vi&sualizza Segnalazione crash"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Carica l'u<imo Crash Report"
Basic.Settings.ProgramRestart="Il programma deve essere riavviato perché questi cambiamenti abbiano effetto."
Basic.Settings.ConfirmTitle="Conferma cambiamenti"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Minimizza sempre nel vassoio di s
Basic.Settings.General.SaveProjectors="Salva i proiettori all'uscita"
Basic.Settings.General.SwitchOnDoubleClick="Transizione alla scena al doppio-click"
Basic.Settings.General.StudioPortraitLayout="Attiva il layout Orizzontale/Verticale"
+Basic.Settings.General.Multiview="Visualizzazione Multipla"
+Basic.Settings.General.Multiview.MouseSwitch="Clicca per passare da una scena all'altra"
+Basic.Settings.General.Multiview.DrawSourceNames="Visualizza il nome della scena"
+Basic.Settings.General.Multiview.DrawSafeAreas="Evidenziare aree sicure (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Layout a viste multiple"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Orizzontale, Alto"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Orizzontale, Basso"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Orizzontale, Sinistra"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Orizzontale, Destra"
+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)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Verticale, A destra (8 scene)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Orizzontale, In alto (24 scene)"
Basic.Settings.Stream="Trasmissione"
Basic.Settings.Stream.StreamType="Tipo di stream"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Tasso di decadimento audio"
Basic.Settings.Audio.MeterDecayRate.Fast="Veloce"
Basic.Settings.Audio.MeterDecayRate.Medium="Medio (Tipo 1 PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Lento (Tipo 2 PPM)"
+Basic.Settings.Audio.PeakMeterType="Modalità Peak Meter"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Sample Peak"
+Basic.Settings.Audio.PeakMeterType.TruePeak="True Peak (alto utilizzo della CPU)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="ATTENZIONE: L'audio Surround è attivo."
Basic.Settings.Audio.MultichannelWarning="Per lo streaming, accertati che il servizio di streaming supporti sia integrazione che riproduzione di suono surround. Twitch, Facebook 360 Live, Mixer RTMP e Smashcast sono esempi di servizi in cui il suono surround è completamente supportato. Anche se sia Facebook Live che YouTube Live implementano il suono surround, Facebook Live lo converte in stereo, mentre YouTube Live ne riproduce solo due canali.\n\nI filtri di OBS Studio sono compatibili con il suono surround, anche se il supporto per il plugin VST non è garantito."
Basic.Settings.Audio.MultichannelWarning.Title="Abilitare l'audio surround?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Rete"
Basic.Settings.Advanced.Network.BindToIP="Associa a IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Attiva il nuovo codice di rete"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Modalità a bassa latenza"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Disabilitare i tasti di scelta rapida quando la finestra principale è a fuoco"
Basic.AdvAudio="Proprietà audio avanzate"
Basic.AdvAudio.Name="Nome"
@@ -749,3 +767,7 @@ OutputWarnings.MP4Recording="Avviso: le registrazioni salvate in MP4 non saranno
FinalScene.Title="Elimina scena"
FinalScene.Text="Deve esserci almeno una scena."
+NoSources.Title="Nessuna sorgente"
+
+
+
diff --git a/UI/data/locale/ja-JP.ini b/UI/data/locale/ja-JP.ini
index ff668b1..6dc4ddc 100644
--- a/UI/data/locale/ja-JP.ini
+++ b/UI/data/locale/ja-JP.ini
@@ -78,6 +78,8 @@ None="未設定"
StudioMode.Preview="プレビュー"
StudioMode.Program="番組"
ShowInMultiview="マルチビューで表示"
+VerticalLayout="垂直レイアウト"
+Group="グループ化"
AlreadyRunning.Title="OBSは既に実行中です"
AlreadyRunning.Text="OBSは既に実行されています! この操作を行うつもりがない限り、新しいインスタンスを実行する前に既存のOBSインスタンスを終了してください。OBSがシステムトレイに最小化されるように設定されている場合は、まだ実行中であるかどうかを確認してください。"
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="リプレイバッファー停止処理中..."
Basic.Main.StopStreaming="配信終了"
Basic.Main.StoppingStreaming="配信停止処理中..."
Basic.Main.ForceStopStreaming="配信停止 (遅延破棄)"
+Basic.Main.Group="グループ化 %1"
+Basic.Main.GroupItems="選択したアイテムのグループ化"
+Basic.Main.Ungroup="グループ化の解除"
Basic.MainMenu.File="ファイル(&F)"
Basic.MainMenu.File.Export="エクスポート(&E)"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="ツール(&T)"
Basic.MainMenu.Help="ヘルプ(&H)"
Basic.MainMenu.Help.HelpPortal="ヘルプポータル(&P)"
Basic.MainMenu.Help.Website="ウェブサイト(&W)"
+Basic.MainMenu.Help.Discord="Discordサーバーに参加(&D)"
Basic.MainMenu.Help.Logs="ログファイル(&L)"
Basic.MainMenu.Help.Logs.ShowLogs="ログファイルを表示(&S)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="現在のログファイルをアップロード(&C)"
Basic.MainMenu.Help.Logs.UploadLastLog="最新のログファイルをアップロード(&L)"
Basic.MainMenu.Help.Logs.ViewCurrentLog="現在のログを表示(&V)"
Basic.MainMenu.Help.CheckForUpdates="更新を確認"
+Basic.MainMenu.Help.CrashLogs="クラッシュレポート(&R)"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="クラッシュレポートを表示(&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="最新のクラッシュレポートをアップロード(&L)"
Basic.Settings.ProgramRestart="これらの設定を有効にするためにはプログラムの再起動が必要です。"
Basic.Settings.ConfirmTitle="変更確認"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="タスクバーの代わりにシ
Basic.Settings.General.SaveProjectors="終了時にプロジェクターを保存する"
Basic.Settings.General.SwitchOnDoubleClick="ダブルクリックしたときにシーンに遷移"
Basic.Settings.General.StudioPortraitLayout="縦長/垂直レイアウトを有効にする"
+Basic.Settings.General.Multiview="マルチビュー"
+Basic.Settings.General.Multiview.MouseSwitch="クリックするとシーンを切り替える"
+Basic.Settings.General.Multiview.DrawSourceNames="シーン名を表示"
+Basic.Settings.General.Multiview.DrawSafeAreas="安全領域を描画 (EBU R95)"
Basic.Settings.General.MultiviewLayout="マルチビューレイアウト"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="水平, 上"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="水平, 下"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="垂直, 左"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="垂直, 右"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="水平上側 (8 シーン)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="水平下側 (8 シーン)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="垂直左側 (8 シーン)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="垂直右側 (8 シーン)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="水平上側 (24 シーン)"
Basic.Settings.Stream="配信"
Basic.Settings.Stream.StreamType="配信種別"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="音声メーターの減衰率"
Basic.Settings.Audio.MeterDecayRate.Fast="速い"
Basic.Settings.Audio.MeterDecayRate.Medium="中 (タイプ I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="遅い (タイプ II PPM)"
+Basic.Settings.Audio.PeakMeterType="ピークメーターの種類"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="サンプル ピーク"
+Basic.Settings.Audio.PeakMeterType.TruePeak="真のピーク (CPU使用率高い)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="警告: サラウンド音声が有効です。"
Basic.Settings.Audio.MultichannelWarning="配信する場合、配信サービスがサラウンド音声の取り込みと再生の両方をサポートしているかどうかを確認してください。Twitch、Facebook 360 Live、Mixer RTMP、Smashcastは、サラウンド音声が完全にサポートされている例です。しかしFacebook LiveとYouTube Liveはどちらもサラウンド取り込みを受信しますが、Facebook Liveはステレオにダウンミックスし、YouTube Liveは2チャンネルのみしか再生できません。\n\nVSTプラグインのサポートは保証されていませんが、OBS音声フィルタはサラウンド音声と互換性があります。"
Basic.Settings.Audio.MultichannelWarning.Title="サラウンド音声を有効にしますか?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="ネットワーク"
Basic.Settings.Advanced.Network.BindToIP="IP選択"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="新しいネットワークコードを有効にする"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="低遅延モード"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="メインウィンドウにフォーカスがあるときはホットキーを無効にする"
Basic.AdvAudio="オーディオの詳細プロパティ"
Basic.AdvAudio.Name="名称"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="警告: ファイルをファイナライズ出来
FinalScene.Title="シーンを削除する"
FinalScene.Text="1つ以上のシーンが必要です。"
+NoSources.Title="ソース無し"
+NoSources.Text="映像ソースをまだ追加していないようなので、空白の画面だけが出力されます。よろしいですか?"
+NoSources.Text.AddSource="メインウィンドウのソースボックスの下にある + アイコンをクリックすると、いつでもソースを追加することができます。"
+
+ChangeBG="色の設定"
+CustomColor="カスタム色"
+
+BrowserSource.EnableHardwareAcceleration="ブラウザソースのハードウェアアクセラレーションを有効にする"
+
diff --git a/UI/data/locale/ka-GE.ini b/UI/data/locale/ka-GE.ini
index d446611..eb59e36 100644
--- a/UI/data/locale/ka-GE.ini
+++ b/UI/data/locale/ka-GE.ini
@@ -2,7 +2,7 @@
Language="ქართული"
Region="საქართველო"
-OK="კაი"
+OK="კარგი"
Apply="მიღება"
Cancel="გაუქმება"
Close="დახურვა"
@@ -28,6 +28,16 @@ Browse="მოძიება"
Mono="მონო"
Stereo="სტერეო"
DroppedFrames="კადრების ვარდნა %1 (%2%)"
+StudioProgramProjector="სრულეკრანიანი ჩვენება (შედეგი)"
+PreviewProjector="სრულეკრანიანი ჩვენება (შეთვალიერება)"
+SceneProjector="სრულეკრანიანი ჩვენება (სცენა)"
+SourceProjector="სრულეკრანიანი ჩვენება (წყარო)"
+StudioProgramWindow="ფანჯარაში ჩვენება (შედეგი)"
+PreviewWindow="ფანჯარაში ჩვენება (შეთვალიერება)"
+SceneWindow="ფანჯარაში ჩვენება (სცენა)"
+SourceWindow="ფანჯარაში ჩვენება (წყარო)"
+MultiviewProjector="მრავალხედიანი (სრულ ეკრანზე)"
+MultiviewWindowed="მრავალხედიანი (ფანჯარაში)"
Clear="გასუფთავება"
Revert="დაბრუნება"
Show="ჩვენება"
@@ -56,123 +66,713 @@ Import="შემოტანა"
Export="გატანა"
Copy="დაკოპირება"
Paste="ჩასმა"
+PasteReference="ჩასმა (ბმული)"
+PasteDuplicate="ჩასმა (ასლი)"
+RemuxRecordings="ჩანაწერების გარდაქმნა"
Next="შემდეგ"
Back="უკან"
Defaults="ნაგულისხმევი"
+HideMixer="ხმის მიქშერში დამალვა"
+TransitionOverride="გადასვლა გადაფარვით"
+None="არცერთი"
+StudioMode.Preview="შეთვალიერება"
+StudioMode.Program="შედეგი"
+ShowInMultiview="მრავალხედიანი ჩვენება"
+VerticalLayout="შვეული განლაგება"
+Group="დაჯგუფება"
AlreadyRunning.Title="OBS უკვე გაშვებულია"
AlreadyRunning.Text="OBS უკვე გაშვებულია! გთხოვთ, ჯერ დახუროთ OBS-ის ყველა გაშვებული პროცესი, სანამ ახლის გაშვებას შეეცდებით. თუ მითითებული გაქვთ, რომ დახურვის ნაცვლად, OBS სისტემურ არეში უნდა ჩაიკეცოს, გთხოვთ მანდაც გადაამოწმოთ, დარჩენილი ხომ არაა."
AlreadyRunning.LaunchAnyway="მაინც გაშვება"
Copy.Filters="ფილტრების დაკოპირება"
+Paste.Filters="ფილტრების ჩასმა"
+BandwidthTest.Region="რეგიონი"
+BandwidthTest.Region.US="შეერთებული შტატები"
+BandwidthTest.Region.EU="ევროპა"
+BandwidthTest.Region.Asia="აზია"
+BandwidthTest.Region.Other="სხვა"
+Basic.FirstStartup.RunWizard="გსურთ, გაეშვას თვითგამართვის მეგზური? ამასთან, შეგიძლიათ პარამეტრების ხელით გამართვა მთავარ ფანჯარაში, პარამეტრების ღილაკზე დაწკაპებით."
+Basic.FirstStartup.RunWizard.BetaWarning="(შენიშვნა: თვითგამართვის მეგზური ჯერჯერობით საცდელია)"
+Basic.FirstStartup.RunWizard.NoClicked="თუ გადაიფიქრებთ, თვითგამართვის გაშვება შეგეძლებათ ნებისმიერ დროს, ხელსაწყოების მენიუდან."
+Basic.AutoConfig="თვითგამართვის მეგზური"
+Basic.AutoConfig.Beta="თვითგამართვის მეგზური (Beta)"
+Basic.AutoConfig.ApplySettings="პარამეტრების მიღება"
+Basic.AutoConfig.StartPage="გამოყენების შესახებ"
+Basic.AutoConfig.StartPage.SubTitle="მიუთითეთ, თუ რა მიზნით გსურთ პროგრამის გამოყენება"
+Basic.AutoConfig.StartPage.PrioritizeStreaming="ნაკადების გაშვებისთვის მორგება, ვიდეოს ჩაწერა მეორეხარისხოვანია"
+Basic.AutoConfig.StartPage.PrioritizeRecording="ვიდეოების ჩაწერისთვის მორგება, ნაკადების გაშვებას არ ვაპირებ"
+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.FPS.UseCurrent="მიმდინარეს გამოყენება (%1)"
+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.Service="მომსახურება"
+Basic.AutoConfig.StreamPage.Service.ShowAll="ყველას ჩვენება..."
+Basic.AutoConfig.StreamPage.Server="სერვერი"
+Basic.AutoConfig.StreamPage.StreamKey="ნაკადის გასაღები"
+Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(ბმული)"
+Basic.AutoConfig.StreamPage.PerformBandwidthTest="მიახლოებითი ბიტური სიხშირე, გამოთვლილი ქსელის გამტარუნარიანობის შემოწმებით (რამდენიმე წუთს შესაძლოა გასტანოს)"
+Basic.AutoConfig.StreamPage.PreferHardwareEncoding="აპარატურული დაშიფვრის გამოყენება"
+Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="აპარატურული დაშიფვრა შეამცირებს პროცესორზე დატვირთვას, თუმცა შესაძლოა მეტი ბიტური სიხშირე დასჭირდეს, იმავე ხარისხის მისაღწევად."
+Basic.AutoConfig.StreamPage.StreamWarning.Title="გაფრთხილება ნაკადის გაშვებისას"
+Basic.AutoConfig.StreamPage.StreamWarning.Text="ქსელის გამტარუნარიანობის შემოწმება გულისხმობს, თქვენს არხზე შემთხვევითი ვიდეოფაილების ხმის გაშერე ნაკადად გაშვებას. სასურველია, თუ დროებით გათიშავთ ნაკადის შენახვის შესაძლებლობას და შემოწმების დასრულებამდე ამ ნაკადს გადაიყვანთ პირად რეჟიმში. გსურთ, განაგრძოთ?"
+Basic.AutoConfig.TestPage="საბოლოო შედეგები"
+Basic.AutoConfig.TestPage.SubTitle.Testing="პროგრამა ახლა უშვებს სხვადასხვა სახის შემოწმებებს, სასურველი პარამეტრების დადგენის მიზნით"
+Basic.AutoConfig.TestPage.SubTitle.Complete="შემოწმება დასრულებულია"
+Basic.AutoConfig.TestPage.TestingBandwidth="ქსელის გამტარუნარიანობის შემოწმება, შესაძლოა რამდენიმე წუთს გასტანოს..."
+Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="უკავშირდება სერვერს: %1..."
+Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="ვერცერთ სერვერთან დაკავშირება ვერ მოხერხდა. შეამოწმეთ თქვენი ინტერნეტთან კავშირი და სცადეთ ხელახლა."
+Basic.AutoConfig.TestPage.TestingBandwidth.Server="გამტარუნარიანობის შემოწმება სერვერისთვის: %1"
+Basic.AutoConfig.TestPage.TestingStreamEncoder="ნაკადის დამშიფრავის შემოწმება, შესაძლოა ერთ წუთამდე გასტანოს..."
+Basic.AutoConfig.TestPage.TestingRecordingEncoder="ჩანაწერის დამშიფრავის შემოწმება, შესაძლოა ერთ წუთამდე გასტანოს..."
+Basic.AutoConfig.TestPage.TestingRes="გაფართოებების შემოწმება, შესაძლოა რამდენიმე წუთს გასტანოს..."
+Basic.AutoConfig.TestPage.TestingRes.Fail="დამშიფრავის ჩართვა ვერ მოხერხდა"
+Basic.AutoConfig.TestPage.TestingRes.Resolution="მოწმდება %1x%2 %3 FPS (კადრ/წმ)..."
+Basic.AutoConfig.TestPage.Result.StreamingEncoder="ნაკადის დამშიფრავი"
+Basic.AutoConfig.TestPage.Result.RecordingEncoder="ჩანაწერის დამშიფრავი"
+Basic.AutoConfig.TestPage.Result.Header="პროგრამის მიერ დადგენილი მიახლოებითი პარამეტრები, რომელიც მეტად გამოსადეგია თქვენთვის:"
+Basic.AutoConfig.TestPage.Result.Footer="თუ გსურთ ამ პარამეტრების გამოყენება, დააწკაპეთ „პარამეტრების მიღებას“. თუ გსურთ პარამეტრების ხელახლა დადგენა, დააწკაპეთ ღილაკს „უკან“. ხოლო, თუ პარამეტრების ხელით გამართვა გსურთ, დააწკაპეთ „გაუქმებას“ და გადადით პარამეტრებზე."
+Basic.Stats="სტატისტიკა"
+Basic.Stats.CPUUsage="CPU დატვირთვა"
+Basic.Stats.HDDSpaceAvailable="ხელმისაწვდომი ადგილი დისკზე"
+Basic.Stats.MemoryUsage="მეხსიერების დატვირთვა"
+Basic.Stats.AverageTimeToRender="კადრის დამუშავების საშუალო დრო"
+Basic.Stats.SkippedFrames="დაშიფვრის დაყოვნების გამო გამოტოვებული კადრები"
+Basic.Stats.MissedFrames="დამუშავების დაყოვნების გამო გამოტოვებული კადრები"
+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.DroppedFrames="გამოტოვებული კადრები (ქსელი)"
+Basic.Stats.MegabytesSent="საერთო მოცულობა"
+Basic.Stats.Bitrate="ბიტური სიხშირე"
+Updater.Title="ხელმისაწვდომია განახლება"
+Updater.Text="ხელმისაწვდომია განახლება:"
Updater.UpdateNow="განაახლე ახლა"
+Updater.RemindMeLater="მოგვიანებით შეხსენება"
+Updater.Skip="ვერსიის გამოტოვება"
+Updater.Running.Title="პროგრამა ამჟამად გაშვებულია"
+Updater.Running.Text="გაშვებულია გამომავალი სიგნალები, გთხოვთ, ჯერ გამორთოთ ყველა მოქმედი გამომავალი სიგნალი, სანამ განახლებას შეეცდებით"
+Updater.NoUpdatesAvailable.Title="განახლებები არაა ხელმისაწვდომი"
+Updater.NoUpdatesAvailable.Text="ამჟამად, განახლებები არაა ხელმისაწვდომი"
+Updater.FailedToLaunch="განახლების გაშვება ვერ მოხერხდა"
+Updater.GameCaptureActive.Title="მიმდინარეობს თამაშის ჩაწერა"
+Updater.GameCaptureActive.Text="ამჟამად გაშვებულია თამაშის ჩამწერი ბიბლიოთეკა. გთხოვთ, შეწყვიტოთ ყველა თამაშის/პროგრამის ჩაწერა (ან თავიდან ჩართოთ windows) და სცადოთ ხელახლა."
+QuickTransitions.SwapScenes="გადასვლის შემდეგ, შესათვალიერებელი და გამომავალი სცენებისთვის ადგილის გაცვლა"
+QuickTransitions.SwapScenesTT="გადასვლის შემდეგ, შესათვალიერებელი და გამომავალი სცენებისთვის ადგილის გაცვლა (თუ ჯერ კიდევ არსებობს გამომავალი სცენის თავდაპირველი ასლი).\nამის შედეგად, გამომავალი სცენის თავდაპირველ ვარიანტზე გაკეთებული ცვლილებები, არ გაუქმდება."
+QuickTransitions.DuplicateScene="სცენის გაორკეცება"
+QuickTransitions.DuplicateSceneTT="ერთი და იმავე სცენის შესწორებისას, საშუალებას იძლევა წყარო გარდაიქმნას, გამომავალი შედეგის შეცვლის გარეშე.\nწყაროს პარამეტრების ჩასასწორებლად, გამომავალი შედეგის შეცვლის გარეშე, ჩართეთ 'წყაროს გაორკეცება'\nმოცემული მნიშვნელობის ცვლილება გამოიწვევს მიმდინარე გამომავალი სცენის საწყისზე დაბრუნებას (თუ ჯერ კიდევ არსებობს)."
+QuickTransitions.EditProperties="წყაროს გაორკეცება"
+QuickTransitions.EditPropertiesTT="ერთი და იმავე სცენის შესწორებისას, საშუალებას იძლევა წყაროს პარამეტრები ჩასწორდეს, გამომავალი შედეგის შეცვლის გარეშე.\nმისი გამოყენება შესაძლებელია, მხოლოდ 'სცენის გაორკეცების' შემთხვევაში\nცალკეული წყაროები (მათ შორის გადაღებული ან არსებული მედიაფაილები) არ იძლევა ამის საშუალებას და მათი განცალკევებით ჩასწორება შეუძლებელია.\nმოცემული მნიშვნელობის ცვლილება გამოიწვევს მიმდინარე გამომავალი სცენის საწყისზე დაბრუნებას (თუ ჯერ კიდევ არსებობს).\n\nგაფრთხილება: წყაროს გაორკეცების შედეგად, შესაძლოა სისტემის და ვიდეოდაფის დატვირთვა გაიზარდოს."
+QuickTransitions.HotkeyName="სწრაფი გადასვლა: %1"
+Basic.AddTransition="გასამართი გადასვლის დამატება"
+Basic.RemoveTransition="გასამართი გადასვლის ამოშლა"
+Basic.TransitionProperties="გადასვლის პარამეტრები"
+Basic.SceneTransitions="სცენებს შორის გადასვლები"
Basic.TransitionDuration="ხანგრძლივობა"
+Basic.TogglePreviewProgramMode="სტუდიური რეჟიმი"
+TransitionNameDlg.Text="გთხოვთ, მიუთითოთ გადასვლის სახელი"
+TransitionNameDlg.Title="გადასვლის სახელი"
+TitleBar.Profile="პროფილი"
+TitleBar.Scenes="სცენები"
+NameExists.Title="სახელი უკვე არსებობს"
+NameExists.Text="სახელი უკვე გამოყენებულია."
+NoNameEntered.Title="გთხოვთ, შეიყვანოთ მართებული სახელი"
+NoNameEntered.Text="ცარიელი სახელი დაუშვებელია."
+ConfirmStart.Title="გაეშვას ნაკადი?"
+ConfirmStart.Text="ნამდვილად გსურთ პირდაპირი ეთერის გაშვება?"
+ConfirmStop.Title="შეწყდეს ნაკადი?"
+ConfirmStop.Text="ნამდვილად გსურთ პირდაპირი ეთერის შეწყვეტა?"
ConfirmExit.Title="OBS-დან გასვლა?"
+ConfirmExit.Text="OBS ამჟამად მოქმედია. ყველა გაშვებული ნაკადი/ჩაწერა შეწყდება. ნამდვილად გსურთ გამოსვლა?"
+ConfirmRemove.Title="წაშლის დადასტურება"
+ConfirmRemove.Text="ნამდვილად გსურთ, წაიშალოს '$1'?"
+ConfirmRemove.TextMultiple="ნამდვილად გსურთ, წაიშალოს %1 მათგანი?"
+Output.StartStreamFailed="ნაკადის გაშვება ვერ მოხერხდა"
+Output.StartRecordingFailed="ჩაწერის დაწყება ვერ მოხერხდა"
+Output.StartReplayFailed="გადახვევის ჩართვა ვერ მოხერხდა"
+Output.StartFailedGeneric="სიგნალის გაშვება ვერ მოხერხდა. გთხოვთ, შეამოწმეთ აღრიცხვის ფაილი, დამატებითი ინფორმაციისთვის.\n\nშენიშვნა: თუ იყენებთ NVENC ან AMD დამშიფრავებს, დარწმუნდით რომ თქვენი ვიდეოდაფის პროგრამა განახლებულია."
+Output.ConnectFail.Title="დაკავშირება ვერ მოხერხდა"
+Output.ConnectFail.BadPath="არამართებული მისამართი ან დასაკავშირებელი URL ბმული. გთხოვთ, გადაამოწმოთ თქვენი პარამეტრების სისწორე."
+Output.ConnectFail.ConnectFailed="სერვერთან დაკავშირება ვერ მოხერხდა"
+Output.ConnectFail.InvalidStream="ვერ ხერხდება მითითებულ არხთან ან ნაკადის გასაღებთან დაკავშირება, გთხოვთ, გადაამოწმოთ თქვენი ნაკადის გასაღები. თუ სწორია, მაშინ შესაძლოა ხარვეზი იყოს სერვერთან დაკავშირებისას."
+Output.ConnectFail.Error="მოულოდნელი შეცდომა წარმოიქმნა სერვერთან დაკავშირებისას. დამატებითი ინფორმაციისთვის იხილეთ აღრიცხვის ფაილი."
+Output.ConnectFail.Disconnected="სერვერთან კავშირი გაწყვეტილია."
+Output.RecordFail.Title="ჩაწერის დაწყება ვერ მოხერხდა"
+Output.RecordFail.Unsupported="ამ სახის გამომავალი სიგნალი ან არაა მხარდაჭერილი, ან არ იძლევა ერთზე მეტი ხმოვანი ფაილის გამოყენების საშუალებას. გთხოვთ, გადაამოწმოთ თქვენი პარამეტრები და სცადოთ ხელახლა."
+Output.RecordNoSpace.Title="არასაკმარისი ადგილი დისკზე"
+Output.RecordNoSpace.Msg="დისკზე აღარაა საკმარისი ადგილი ჩაწერის გასაგრძელებლად."
+Output.RecordError.Title="შეცდომა ჩაწერისას"
+Output.RecordError.Msg="ჩაწერის დროს დაუდგენელი სახის შეცდომა წარმოიშვა."
+Output.ReplayBuffer.NoHotkey.Title="სწრაფი ღილაკი არაა მითითებული!"
+Output.ReplayBuffer.NoHotkey.Msg="სწრაფი ღილაკი არაა მითითებული გადახვევისთვის. გთხოვთ, მიუთითოთ „შენახვის“ ღილაკი, გადასახვევი მასალის შესანახად."
+Output.BadPath.Title="ფაილის არამართებული მისამართი"
+Output.BadPath.Text="ფაილის მითითებული მდებარეობა არასწორია. გთხოვთ, გადაამოწმოთ თქვენი პარამეტრების სისწორე."
+LogReturnDialog="ჩანაწერი წარმატებით აიტვირთა"
LogReturnDialog.CopyURL="ბმულის კოპირება"
+LogReturnDialog.ErrorUploadingLog="შეცდომა აღრიცხვის ფაილის ატვირთვისას"
+LicenseAgreement="სალიცენზიო შეთანხმება"
+LicenseAgreement.PleaseReview="გთხოვთ, გაეცნოთ ლიცენზიის დადგენილებებს OBS-ით სარგებლობამდე. ამ პროგრამის გამოყენებით თქვენ ადასტურებთ, რომ წაიკითხეთ და ეთანხმებით GNU General Public v2.0 ლიცენზიის პირობებს. გთხოვთ, გადაადგილოთ გვერდი ქვემოთ, ხელშეკრულების სრულად სანახავად."
+LicenseAgreement.ClickIAgreeToContinue="თუ თანახმა ხართ, მიიღოთ ხელშეკრულების პირობები, დააწკაპეთ ღილაკს „ვეთანხმები“. OBS-ით სარგებლობისთვის, აუცილებელია წინამდებარე პირობების მიღება."
+LicenseAgreement.IAgree="ვეთანხმები"
LicenseAgreement.Exit="გასვლა"
+Remux.SourceFile="OBS ჩაწერა"
+Remux.TargetFile="საბოლოო ფაილი"
+Remux.Remux="ხელახლა მულტიპლექსირება"
+Remux.OBSRecording="OBS ჩაწერა"
+Remux.FinishedTitle="გარდაქმნა დასრულებულია"
+Remux.Finished="ჩანაწერის გარდაქმნა დასრულდა"
+Remux.FinishedError="ჩანაწერის გარდაქმნა დასრულდა, თუმცა ფაილი, შესაძლოა არასრული იყოს"
+Remux.SelectRecording="ფაილის არჩევა OBS ჩანაწერისთვის…"
+Remux.SelectTarget="საბოლოო ფაილის არჩევა …"
+Remux.FileExistsTitle="საბოლოო ფაილი უკვე არჩეულია"
+Remux.FileExists="საბოლოო ფაილი უკვე არჩეულია, გსურთ მისი შეცვლა?"
+Remux.ExitUnfinishedTitle="მიმდინარეობს გარდაქმნა"
+Remux.ExitUnfinished="გარდაქმნა ჯერ არ დასრულებულა, ახლავე შეწყვეტის შედეგად, დასამუშავებელი საბოლოო ფაილი გამოუსადეგარი გახდება.\nნამდვილად გსურთ გარდაქმნის შეწყვეტა?"
+UpdateAvailable="განახლება ხელმისაწვდომია"
+UpdateAvailable.Text="ხელმისაწვდომია ვერსია %1.%2.%3. დააწკაპეთ ჩამოსატვირთად"
+Basic.DesktopDevice1="Desktop Audio"
+Basic.DesktopDevice2="Desktop Audio 2"
+Basic.AuxDevice1="Mic/Aux"
Basic.AuxDevice2="Mic/Aux 2"
Basic.AuxDevice3="Mic/Aux 3"
Basic.AuxDevice4="Mic/Aux 4"
+Basic.Scene="სცენა"
+Basic.DisplayCapture="ეკრანის გადაღება"
+Basic.Main.PreviewConextMenu.Enable="შეთვალიერების შესაძლებლობა"
+ScaleFiltering="მასშტაბირების ფილტრი"
+ScaleFiltering.Point="წერტილოვანი"
+ScaleFiltering.Bilinear="ორხაზოვანი"
+ScaleFiltering.Bicubic="ბიკუბური"
+ScaleFiltering.Lanczos="Lanczos"
+Deinterlacing="Deinterlacing"
+Deinterlacing.Discard="გაუქმება"
Deinterlacing.Retro="Retro"
Deinterlacing.Blend="Blend"
Deinterlacing.Blend2x="Blend 2x"
+Deinterlacing.Linear="Linear"
Deinterlacing.Linear2x="Linear 2x"
Deinterlacing.Yadif="Yadif"
Deinterlacing.Yadif2x="Yadif 2x"
+Deinterlacing.TopFieldFirst="ზედა ველიდან"
+Deinterlacing.BottomFieldFirst="ქვედა ველიდან"
+
+VolControl.SliderUnmuted="ხმის სამართავი '%1': %2"
+VolControl.SliderMuted="ხმის სამართავი '%1': %2 (ამჟამად დადუმებული)"
+VolControl.Mute="'%1' დადუმება"
+VolControl.Properties="'%1' პარამეტრები"
+
+Basic.Main.AddSceneDlg.Title="სცენის დამატება"
+Basic.Main.AddSceneDlg.Text="გთხოვთ, მიუთითოთ სცენის დასახელება"
+
+Basic.Main.DefaultSceneName.Text="სცენა %1"
+
+Basic.Main.AddSceneCollection.Title="სცენის კრებულის დამატება"
+Basic.Main.AddSceneCollection.Text="გთხოვთ, მიუთითოთ სცენის კრებულის დასახელება"
+
+Basic.Main.RenameSceneCollection.Title="სცენის კრებულის გადარქმევა"
+
+AddProfile.Title="პროფილის დამატება"
+AddProfile.Text="გთხოვთ, მიუთითოთ პროფილის დასახელება"
+
+RenameProfile.Title="პროფილის გადარქმევა"
+
+Basic.Main.MixerRename.Title="ხმის წყაროს გადარქმევა"
+Basic.Main.MixerRename.Text="გთხოვთ, მიუთითოთ ხმის წყაროს დასახელება"
+Basic.Main.PreviewDisabled="შეთვალიერება მიუწვდომელია"
+Basic.SourceSelect="წყაროს შექმნა/მითითება"
+Basic.SourceSelect.CreateNew="ახლის შექმნა"
+Basic.SourceSelect.AddExisting="არსებულის დამატება"
+Basic.SourceSelect.AddVisible="წყაროს გამოჩენა"
+Basic.PropertiesWindow="'%1' პარამეტრები"
+Basic.PropertiesWindow.AutoSelectFormat="%1 (თვითშერჩევა: %2)"
+Basic.PropertiesWindow.SelectColor="ფერის შერჩევა"
+Basic.PropertiesWindow.SelectFont="შრიფტის შერჩევა"
+Basic.PropertiesWindow.ConfirmTitle="პარამეტრები შეცვლილია"
+Basic.PropertiesWindow.Confirm="ცვლილებები არაა დამახსოვრებული. გსურთ მათი შენახვა?"
+Basic.PropertiesWindow.NoProperties="პარამეტრები მიუწვდომელია"
+Basic.PropertiesWindow.AddFiles="ფაილების დამატება"
+Basic.PropertiesWindow.AddDir="საქაღალდის დამატება"
+Basic.PropertiesWindow.AddURL="მისამართის/URL-ს დამატება"
+Basic.PropertiesWindow.AddEditableListDir="საქაღალდის დამატება '%1'-ში"
+Basic.PropertiesWindow.AddEditableListFiles="ფაილების დამატება '%1'"
+Basic.PropertiesWindow.AddEditableListEntry="ელემენტის დამატება '%1'-ში"
+Basic.PropertiesWindow.EditEditableListEntry="ელემენტის ჩასწორება '%1'-ში"
+Basic.PropertiesView.FPS.Simple="ჩვეულებრივი FPS-მნიშვნელობები"
+Basic.PropertiesView.FPS.Rational="რაციონალურ რიცხვიანი FPS-მნიშვნელობები"
+Basic.PropertiesView.FPS.ValidFPSRanges="დაშვებული FPS-შუალედები:"
+Basic.InteractionWindow="'%1'-თან ურთიერთქმედება"
+Basic.StatusBar.Reconnecting="კავშირი გაწყვეტილია. ხელახლა დაკავშირება %2 წამში (%1 მცდელობა)"
+Basic.StatusBar.AttemptingReconnect="ხელახლა დაკავშირება... (%1 მცდელობა)"
+Basic.StatusBar.ReconnectSuccessful="ხელახლა დაუკავშირდა წარმატებით"
+Basic.StatusBar.Delay="დაყოვნება (%1 წმ)"
+Basic.StatusBar.DelayStartingIn="დაყოვნება (დაიწყება %1 წამში)"
+Basic.StatusBar.DelayStoppingIn="დაყოვნება (შეწყდება %1 წამში)"
+Basic.StatusBar.DelayStartingStoppingIn="დაყოვნება (შეწყდება %1 წამში, დაიწყება %2 წამში)"
+Basic.Filters="ფილტრები"
+Basic.Filters.AsyncFilters="ხმოვანი/ვიდეო ფილტრები"
+Basic.Filters.AudioFilters="ხმოვანი ფილტრები"
+Basic.Filters.EffectFilters="დასამუშავებელი ფილტრები"
+Basic.Filters.Title="'%1' ფილტრები"
+Basic.Filters.AddFilter.Title="ფილტრის დასახელება"
+Basic.Filters.AddFilter.Text="გთხოვთ, მიუთითოთ ფილტრის დასახელება"
-
-
-
-
-
-
-
-
+Basic.TransformWindow="სცენის ელემენტის გარდაქმნა"
Basic.TransformWindow.Position="პოზიცია"
+Basic.TransformWindow.Rotation="მობრუნება"
+Basic.TransformWindow.Size="ზომა"
+Basic.TransformWindow.Alignment="განლაგების გასწორება"
+Basic.TransformWindow.BoundsType="შემომსაზღვრელი ჩარჩოს სახეები"
+Basic.TransformWindow.BoundsAlignment="განლაგების გასწორება შემომსაზღვრელ ჩარჩოში"
+Basic.TransformWindow.Bounds="შემომსაზღვრელი ჩარჩოს ზომა"
+Basic.TransformWindow.Crop="შემოჭრა"
+Basic.TransformWindow.Alignment.TopLeft="მარცხნივ ზემოთ"
+Basic.TransformWindow.Alignment.TopCenter="შუაში ზემოთ"
+Basic.TransformWindow.Alignment.TopRight="მარჯვნივ ზემოთ"
+Basic.TransformWindow.Alignment.CenterLeft="მარცხნივ შუაში"
+Basic.TransformWindow.Alignment.Center="შუაში"
+Basic.TransformWindow.Alignment.CenterRight="მარჯვნივ შუაში"
+Basic.TransformWindow.Alignment.BottomLeft="მარცხნივ ქვემოთ"
+Basic.TransformWindow.Alignment.BottomCenter="შუაში ქვემოთ"
+Basic.TransformWindow.Alignment.BottomRight="მარჯვნივ ქვემოთ"
+Basic.TransformWindow.BoundsType.None="საზღვრების გარეშე"
+Basic.TransformWindow.BoundsType.MaxOnly="მხოლოდ უმაღლესი ზომა"
+Basic.TransformWindow.BoundsType.ScaleInner="ზომის მიყვანა შიდა საზღვრებამდე"
+Basic.TransformWindow.BoundsType.ScaleOuter="ზომის მიყვანა გარე საზღვრებამდე"
+Basic.TransformWindow.BoundsType.ScaleToWidth="ზომის დაყვანა საზღვრების სიგანემდე"
+Basic.TransformWindow.BoundsType.ScaleToHeight="ზომის დაყვანა საზღვრების სიმაღლემდე"
+Basic.TransformWindow.BoundsType.Stretch="გაწელვა საზღვრებამდე"
+Basic.Main.AddSourceHelp.Title="წყარო ვერ დაემატება"
+Basic.Main.AddSourceHelp.Text="საჭიროა, სულ მცირე 1 სცენა წყაროს დასამატებლად."
+Basic.Main.Scenes="სცენები"
+Basic.Main.Sources="წყაროები"
+Basic.Main.Controls="სამართავი"
+Basic.Main.Connecting="უკავშირდება..."
+Basic.Main.StartRecording="ჩაწერის დაწყება"
+Basic.Main.StartReplayBuffer="გადახვევის ჩართვა"
+Basic.Main.StartStreaming="ნაკადის გაშვება"
+Basic.Main.StopRecording="ნაკადის შეწყვეტა"
+Basic.Main.StoppingRecording="ჩაწერის შეწყვეტა..."
+Basic.Main.StopReplayBuffer="გადახვევის გამორთვა"
+Basic.Main.StoppingReplayBuffer="გადახვევა გამოირთვება..."
+Basic.Main.StopStreaming="ნაკადის შეწყვეტა"
+Basic.Main.StoppingStreaming="ნაკადი წყდება..."
+Basic.Main.ForceStopStreaming="ნაკადი წყდება (დაყოვნება უქმდება)"
+Basic.Main.Group="ჯგუფი %1"
+Basic.Main.GroupItems="შერჩეულების დაჯგუფება"
+Basic.Main.Ungroup="განჯგუფება"
+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.ShowSettingsFolder="პარამეტრების საქაღალდის ჩვენება"
+Basic.MainMenu.File.ShowProfileFolder="პროფილის საქაღალდის ჩვენება"
+Basic.MainMenu.AlwaysOnTop="&ყოველთვის წინა პლანზე"
+Basic.MainMenu.File.Exit="&გამოსვლა"
+Basic.MainMenu.Edit="&ჩასწორება"
+Basic.MainMenu.Edit.Undo="&დაბრუნება"
+Basic.MainMenu.Edit.Redo="&კვლავ შესრულება"
+Basic.MainMenu.Edit.UndoAction="&დაბრუნება $1"
+Basic.MainMenu.Edit.RedoAction="&კვლავ შესრულება $1"
+Basic.MainMenu.Edit.LockPreview="შეთვალიერების &ჩაკეტვა"
+Basic.MainMenu.Edit.Scale="შეთვალიერების &ზომის შეცვლა"
+Basic.MainMenu.Edit.Scale.Window="ფანჯრის ზომამდე"
+Basic.MainMenu.Edit.Scale.Canvas="ფონის ზომამდე (%1x%2)"
+Basic.MainMenu.Edit.Scale.Output="გამომავალი ვიდეოს ზომამდე (%1x%2)"
+Basic.MainMenu.Edit.Transform="&გარდაქმნა"
+Basic.MainMenu.Edit.Transform.EditTransform="გარდაქმნის &ჩასწორება..."
+Basic.MainMenu.Edit.Transform.CopyTransform="გარდაქმნის დაკოპირება"
+Basic.MainMenu.Edit.Transform.PasteTransform="გარდაქმნის ჩასმა"
+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.FlipVertical="&შვეულად შეტრიალება"
+Basic.MainMenu.Edit.Transform.FitToScreen="ეკრანის ზომაზე &მორგება"
+Basic.MainMenu.Edit.Transform.StretchToScreen="ეკრანის ზომაზე &გაწელვა"
+Basic.MainMenu.Edit.Transform.CenterToScreen="ეკრანის &შუაში განთავსება"
+Basic.MainMenu.Edit.Order="&დალაგება"
+Basic.MainMenu.Edit.Order.MoveUp="&ზემოთ აწევა"
+Basic.MainMenu.Edit.Order.MoveDown="&ქვემოთ ჩამოწევა"
+Basic.MainMenu.Edit.Order.MoveToTop="&თავში გადატანა"
+Basic.MainMenu.Edit.Order.MoveToBottom="&ბოლოში გადატანა"
+Basic.MainMenu.Edit.AdvAudio="ხმის &გაფართოებული პარამეტრები"
+Basic.MainMenu.View="&ხედი"
+Basic.MainMenu.View.Toolbars="&ხელსაწყოები"
+Basic.MainMenu.View.Docks="იერსახის ნაწილები"
Basic.MainMenu.View.Docks.ResetUI="UI-ს გადატვირთვა"
+Basic.MainMenu.View.Docks.LockUI="UI-ს ჩაკეტვა"
+Basic.MainMenu.View.Toolbars.Listboxes="&სიები"
+Basic.MainMenu.View.SceneTransitions="ს&ცენებს შორის გადასვლები"
+Basic.MainMenu.View.StatusBar="&მდგომარეობის ზოლი"
+Basic.MainMenu.View.Fullscreen.Interface="სრულეკრანიანი"
+Basic.MainMenu.SceneCollection="&სცენის კრებული"
+Basic.MainMenu.Profile="&პროფილი"
+Basic.MainMenu.Profile.Import="პროფილის შემოტანა"
+Basic.MainMenu.Profile.Export="პროფილის შენახვა"
+Basic.MainMenu.SceneCollection.Import="სცენის კრებულის შემოტანა"
+Basic.MainMenu.SceneCollection.Export="სცენის კრებულის შენახვა"
+Basic.MainMenu.Profile.Exists="ასეთი პროფილი უკვე არსებობს"
+Basic.MainMenu.SceneCollection.Exists="ამ სცენის კრებული უკვე არსებობს"
+Basic.MainMenu.Tools="&ხელსაწყოები"
+Basic.MainMenu.Help="&დახმარება"
+Basic.MainMenu.Help.HelpPortal="დახმარების &გვერდი"
+Basic.MainMenu.Help.Website="ეწვიეთ &ვებსაიტს"
+Basic.MainMenu.Help.Discord="&Discord სერვერზე შესვლა"
+Basic.MainMenu.Help.Logs="&აღრიცხვის ფაილები"
+Basic.MainMenu.Help.Logs.ShowLogs="აღრიცხვის ფაილების &ჩვენება"
+Basic.MainMenu.Help.Logs.UploadCurrentLog="&მიმდინარე აღრიცხვის ფაილის ატვირთვა"
+Basic.MainMenu.Help.Logs.UploadLastLog="&ბოლო აღრიცხვის ფაილის ატვირთვა"
+Basic.MainMenu.Help.Logs.ViewCurrentLog="მიმდინარე აღრიცხვის ფაილის &ნახვა"
+Basic.MainMenu.Help.CheckForUpdates="განახლებებზე შემოწმება"
+Basic.MainMenu.Help.CrashLogs="ავარიული დახურვების &მოხსენებები"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="ავარიული დახურვების მოხსენებების &ჩვენება"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="&ბოლო მოხსენების ატვირთვა"
+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="სტატისტიკის ფანჯრის გახსნა ჩართვისას"
+Basic.Settings.General.WarnBeforeStartingStream="დადასტურების ფანჯრის ჩვენება ნაკადის გაშვებისას"
+Basic.Settings.General.WarnBeforeStoppingStream="დადასტურების ფანჯრის ჩვენება ნაკადის შეწყვეტისას"
+Basic.Settings.General.Projectors="ჩვენებები"
+Basic.Settings.General.HideProjectorCursor="მაჩვენებლის დამალვა ჩვენებებზე"
+Basic.Settings.General.ProjectorAlwaysOnTop="ჩვენებების სხვა ფანჯრების ზემოთ დატოვება"
+Basic.Settings.General.Snapping="წყაროს ჩარჩოს მიზიდვა განთავსებისას"
+Basic.Settings.General.ScreenSnapping="წყაროს ჩარჩოს მიზიდვა ეკრანის კიდესთან"
+Basic.Settings.General.CenterSnapping="წყაროს ჩარჩოს მიზიდვა შუაშია, თარაზულად და შვეულად"
+Basic.Settings.General.SourceSnapping="წყაროს ჩარჩოს სხვა წყაროს ჩარჩოზე მიზიდვა"
+Basic.Settings.General.SnapDistance="მიზიდვის ძალა"
+Basic.Settings.General.RecordWhenStreaming="პირდაპირი ეთერის ავტომატური ჩაწერა"
+Basic.Settings.General.KeepRecordingWhenStreamStops="ჩაწერის გაგრძელება ნაკადის შეწყვეტის შემდეგ"
+Basic.Settings.General.ReplayBufferWhileStreaming="გადახვევის ჩართვა ავტომატურად ნაკადის გაშვებისას"
+Basic.Settings.General.KeepReplayBufferStreamStops="გადახვევის შენარჩუნება, ნაკადის შეწყვეტისას"
+Basic.Settings.General.SysTray="სისტემის არე"
+Basic.Settings.General.SysTrayWhenStarted="სისტემის არეში ჩაკეცვა დაწყებისას"
+Basic.Settings.General.SystemTrayHideMinimize="ყოველთვის სისტემის არეში ჩაკეცვა, ამოცანათა ზოლის ნაცვლად"
+Basic.Settings.General.SaveProjectors="ჩვენებების დამახსოვრება გასვლისას"
+Basic.Settings.General.SwitchOnDoubleClick="სცენაზე გადასვლა ორჯერ დაწკაპებისას"
+Basic.Settings.General.StudioPortraitLayout="შვეული განლაგების ჩართვა"
+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.Horizontal.Top="თარაზულად, ზემოთ (8 სცენა)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="თარაზულად, ქვემოთ (8 სცენა)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="შვეულად, მარცხნივ (8 სცენა)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="შვეულად, მარჯვნივ (8 სცენა)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="თარაზულად, ზემოთ (24 სცენა)"
+Basic.Settings.Stream="ნაკადი"
+Basic.Settings.Stream.StreamType="ნაკადის სახეობა"
+Basic.Settings.Output="გამომავალი სიგნალი"
+Basic.Settings.Output.Format="ჩაწერის ფორმატი"
+Basic.Settings.Output.Encoder="დამშიფრავი"
+Basic.Settings.Output.SelectDirectory="საქაღალდის არჩევა ჩანაწერისთვის"
+Basic.Settings.Output.SelectFile="ფაილის არჩევა ჩანაწერისთვის"
+Basic.Settings.Output.EnforceBitrate="ნაკადის გაშვების მომსახურების ბიტური სიხშირის ზღვრების დადგენა"
+Basic.Settings.Output.Mode="გამომავალი სიგნალის რეჟიმი"
+Basic.Settings.Output.Mode.Simple="მარტივი"
+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.EstimateUnknown="შეუძლებელია მეხსიერების მიახლოებითი მოხმარების დადგენა. გთხოვთ, მიუთითოთ მეხსიერების დასაშვები ზღვარი."
+Basic.Settings.Output.ReplayBuffer.HotkeyMessage="(შენიშვნა: დარწმუნდით, რომ გადახვევისთვის სწრაფი ღილაკები დაყენებულია შესაბამის განყოფილებაში)"
+Basic.Settings.Output.ReplayBuffer.Prefix="გადასახვევი მასალის შესანახი ფაილის თავსართი"
+Basic.Settings.Output.ReplayBuffer.Suffix="ბოლოსართი"
+Basic.Settings.Output.Simple.SavePath="ჩანაწერის მდებარეობა"
+Basic.Settings.Output.Simple.RecordingQuality="ჩაწერის ხარისხი"
+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, რაც წარმოადგენს უმაღლეს დაშვებულ ზღვარს, ნაკადის გაშვების მოცემული მომსახურებისთვის. თუ ნამდვილად გსურთ %1 ბიტურ სიხშირეზე მეტის მიღება, ჩართეთ დაშიფვრის გაფართოებული პარამეტრები და მოხსენით მონიშვნა \"ნაკადის გაშვების მომსახურების ბიტური სიხშირის ზღვრების დადგენას\"."
+Basic.Settings.Output.Simple.Warn.AudioBitrate="გაფრთხილება: ხმოვანი ნაკადის ბიტურ სიხშირედ მიეთითება %1, რაც წარმოადგენს უმაღლეს დაშვებულ ზღვარს, ნაკადის გაშვების მოცემული მომსახურებისთვის. თუ ნამდვილად გსურთ %1 ბიტურ სიხშირეზე მეტის მიღება, ჩართეთ დაშიფვრის გაფართოებული პარამეტრები და მოხსენით მონიშვნა \"ნაკადის გაშვების მომსახურების ბიტური სიხშირის ზღვრების დადგენას\"."
+Basic.Settings.Output.Simple.Warn.Encoder="გაფრთხილება: გაშვებული ნაკადისგან განსხვავებულ ხარისხში ჩანაწერის დაშიფვრა, ზრდის პროცესორის დატვირთვას, როცა ნაკადის გაშვება და ჩაწერა, ერთდროულად მიმდინარეობს."
+Basic.Settings.Output.Simple.Warn.Lossless="გაფრთხილება: უდანაკარგო ხარისხის მითითების შემთხვევაში, შეიქმნება მეტისმეტად დიდი ზომის ფაილები! უდანაკარგო ხარისხის ვიდეოს თითოეული წუთის მოცულობამ დისკზე, შესაძლოა 7 გიგაბაიტს გადააჭარბოს, მაღალი გარჩევადობისა და კადრის სიხშირის პირობებში. ხანგრძლივი ჩანაწერებისთვის, უდანაკარგოს არჩევა არაა მიზანშეწონილი, თუ არ გაქვთ საკმარისად დიდი მოცულობის თავისუფალი ადგილი დისკზე."
+Basic.Settings.Output.Simple.Warn.Lossless.Msg="ნამდვილად გსურთ უდანაკარგო ხარისხის მითითება?"
+Basic.Settings.Output.Simple.Warn.Lossless.Title="გაფრთხილება, უდანაკარგო ხარისხის შესახებ!"
+Basic.Settings.Output.Simple.Warn.MultipleQSV="გაფრთხილება: ერთდროულად რამდენიმე სხვადასხვა QSV დამშიფრავის გამოყენება ნაკადის გაშვებისას და ჩაწერისას, შეუძლებელია. თუ გსურთ ნაკადის გაშვება და ჩაწერა ერთდროულად, გთხოვთ შეცვალოთ ან ჩაწერის დამშიფრავი, ან ნაკადის დამშიფრავი."
+Basic.Settings.Output.Simple.Encoder.Software="პროგრამული (x264)"
+Basic.Settings.Output.Simple.Encoder.Hardware.QSV="აპარატურული (QSV)"
+Basic.Settings.Output.Simple.Encoder.Hardware.AMD="აპარატურული (AMD)"
+Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="აპარატურული (NVENC)"
+Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="პროგრამული (x264 პროცესორის დაბალი მოხმარების მზა პარამეტრები, ზრდის ფაილის ზომას)"
+Basic.Settings.Output.VideoBitrate="ვიდეოს ბიტური სიხშირე"
+Basic.Settings.Output.AudioBitrate="ხმის ბიტური სიხშირე"
+Basic.Settings.Output.Reconnect="ხელახლა დაკავშირება ავტომატურად"
+Basic.Settings.Output.RetryDelay="გამეორების დაყოვნება (წამი)"
+Basic.Settings.Output.MaxRetries="გამეორების დასაშვები რაოდენობა"
+Basic.Settings.Output.Advanced="დამშიფრავის გაფართოებული პარამეტრების ჩართვა"
+Basic.Settings.Output.EncoderPreset="დაშიფვრის მზა პარამეტრები (მაღალი = ნაკლები CPU-დატვირთვა)"
+Basic.Settings.Output.CustomEncoderSettings="დამშიფრავის პარამეტრების მითითება"
+Basic.Settings.Output.CustomMuxerSettings="მულტიპლექსორის მითითებული პარამეტრები"
+Basic.Settings.Output.NoSpaceFileName="ფაილის სახელის შექმნა, გამოტოვებული ადგილების გარეშე"
+Basic.Settings.Output.Adv.Rescale="გამომავალი ვიდეოს ზომების შეცვლა"
+Basic.Settings.Output.Adv.AudioTrack="ხმოვანი ბილიკი"
+Basic.Settings.Output.Adv.Streaming="ნაკადი"
+Basic.Settings.Output.Adv.ApplyServiceSettings="ნაკადის გაშვების მომსახურების პარამეტრების გამოყენება"
+Basic.Settings.Output.Adv.Audio.Track1="ბილიკი 1"
+Basic.Settings.Output.Adv.Audio.Track2="ბილიკი 2"
+Basic.Settings.Output.Adv.Audio.Track3="ბილიკი 3"
+Basic.Settings.Output.Adv.Audio.Track4="ბილიკი 4"
+Basic.Settings.Output.Adv.Audio.Track5="ბილიკი 5"
+Basic.Settings.Output.Adv.Audio.Track6="ბილიკი 6"
+Basic.Settings.Output.Adv.Recording="ჩაწერა"
Basic.Settings.Output.Adv.Recording.Type="ტიპი"
Basic.Settings.Output.Adv.Recording.Type.Standard="სტანდარტული"
+Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="მითითებული გამომავალი სიგნალი (FFmpeg)"
+Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(გაშვებული ნაკადის დამშიფრავის გამოყენება)"
+Basic.Settings.Output.Adv.Recording.Filename="ფაილის დასახელების ფორმატი"
+Basic.Settings.Output.Adv.Recording.OverwriteIfExists="არსებულ ფაილზე გადაწერა"
+Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg გამომავალი სიგნალის სახე"
+Basic.Settings.Output.Adv.FFmpeg.Type.URL="მითითებულ URL ბულზე"
+Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="ფაილში"
+Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="ჩაწერის ცნობილი ფორმატები"
Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="ყველა ფაილი"
+Basic.Settings.Output.Adv.FFmpeg.SavePathURL="ფაილის მისამართი ან URL"
+Basic.Settings.Output.Adv.FFmpeg.Format="სათავსის ფორმატი"
+Basic.Settings.Output.Adv.FFmpeg.FormatAudio="ხმა"
Basic.Settings.Output.Adv.FFmpeg.FormatVideo="ვიდეო"
+Basic.Settings.Output.Adv.FFmpeg.FormatDefault="ნაგულისხმევი ფორმატი"
+Basic.Settings.Output.Adv.FFmpeg.FormatDesc="სათავსის ფორმატის აღწერა"
+Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="ხმის/ვიდეოს კოდეკის ამოცნობა მითითებული ფაილის მისამართიდან ან URL-დან"
+Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="ნაგულისხმევი დამშიფრავი"
+Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="დამშიფრავის გამორთვა"
+Basic.Settings.Output.Adv.FFmpeg.VEncoder="ვიდეოს დამშიფრავი"
+Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="ვიდეოს დამშიფრავის პარამეტრები (თუ არის)"
+Basic.Settings.Output.Adv.FFmpeg.AEncoder="ხმის დამშიფრავი"
+Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="ხმის დამშიფრავის პარამეტრები (თუ არის)"
+Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="მულტიპლექსორის პარამეტრები (თუ არის)"
+Basic.Settings.Output.Adv.FFmpeg.GOPSize="საკვანძო კადრებს შორის შუალედი (კადრები)"
+Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="ყველა კოდეკის ჩვენება (მათ შორის არათავსებადებისაც)"
+FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z"
+FilenameFormatting.TT="%CCYY წელი, ოთხი ციფრი\n%YY წელი, ბოლო ორის ციფრი(00-99)\n%MM თვე, ათობითი რიცხვის სახით (01-12)\n%DD თვის რიცხვი, წინ ნულით (01-31)\n%hh საათი, 24-საათიანი ფორმატით (00-23)\n%mm წუთი(00-59)\n%ss წამი(00-61)\n%% A % ნიშანი\n%a კვირის დღე შემოკლებულად\n%A კვირის დღის სრული დასახელება\n%b თვე შემოკლებულად\n%B თვის სრული დასახელება\n%d თვის რიცხვი, წინ ნულით (01-31)\n%H საათი, 24-საათიანი ფორმატით (00-23)\n%I საათი, 12-საათიანი ფორმატით (01-12)\n%m თვე, ათობითი რიცხვის სახით (01-12)\n%M წუთი (00-59)\n%p AM ან PM აღნიშვნა\n%S წამი (00-61)\n%y წელი, ბოლო ორი ციფრი (00-99)\n%Y წელი\n%z ISO 8601 სხვაობა UTC დროსთან ან სასაათე სარტყელთან\n სრული ან შემოკლებული დასახელება\n%Z სასაათე სარტყელი ან მისი შემოკლებული დასახელება\n"
+Basic.Settings.Video="ვიდეო"
+Basic.Settings.Video.Adapter="ვიდეოდაფა"
+Basic.Settings.Video.BaseResolution="ეკრანის (ფონის) ძირითადი გაფართოება"
+Basic.Settings.Video.ScaledResolution="გამომავალი ვიდეოს (მასშტაბირებულის) გაფართოება"
+Basic.Settings.Video.DownscaleFilter="მასშტაბირების ფილტრი"
+Basic.Settings.Video.DisableAeroWindows="Aero გაფორმების გათიშვა (მხოლოდ Windows-ზე)"
Basic.Settings.Video.FPS="FPS"
+Basic.Settings.Video.FPSCommon="საერთო FPS-მნიშვნელობები"
+Basic.Settings.Video.FPSInteger="მთელრიცხვიანი FPS-მნიშვნელობები"
+Basic.Settings.Video.FPSFraction="წილადიანი FPS-მნიშვნელობები"
+Basic.Settings.Video.Numerator="მრიცხველი"
Basic.Settings.Video.Denominator="მნიშვნელი"
+Basic.Settings.Video.Renderer="დამმუშავებელი"
+Basic.Settings.Video.InvalidResolution="გაფართოების არასწორი მნიშვნელობა. უნდა იყოს [width]x[height] (მაგ. 1920x1080)"
+Basic.Settings.Video.CurrentlyActive="ვიდეო ამჟამად გაშვებულია. გთხოვთ, გამორთოთ ყველა გამომავალი სიგნალი, ვიდეოს პარამეტრების ჩასასწორებლად."
+Basic.Settings.Video.DisableAero="Aero გაფორმების გათიშვა"
+Basic.Settings.Video.DownscaleFilter.Bilinear="ორხაზოვანი (უსწრაფესი, მაგრამ ბუნდოვანი მასშტაბირება)"
+Basic.Settings.Video.DownscaleFilter.Bicubic="ბიკუბური (მკვეთრი მასშტაბირება, 16 შერჩევა)"
+Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (მკვეთრი მასშტაბირება, 32 შერჩევა)"
+Basic.Settings.Audio="ხმა"
+Basic.Settings.Audio.SampleRate="სიხშირე"
Basic.Settings.Audio.Channels="არხები"
+Basic.Settings.Audio.MeterDecayRate="ხმის ზოლის დაშვების სიჩქარე"
+Basic.Settings.Audio.MeterDecayRate.Fast="სწრაფი"
+Basic.Settings.Audio.MeterDecayRate.Medium="საშუალო (I სახის PPM)"
+Basic.Settings.Audio.MeterDecayRate.Slow="ნელი (II სახის PPM)"
+Basic.Settings.Audio.PeakMeterType="ხმის სიმაღლის მზომის სახეები"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="უბრალო"
+Basic.Settings.Audio.PeakMeterType.TruePeak="ზუსტი (პროცესორის მაღალი მოხმარებით)"
+Basic.Settings.Audio.MultiChannelWarning.Enabled="ყურადღება: ჩართულია მოცულობითი ხმა."
+Basic.Settings.Audio.MultichannelWarning="ნაკადის გაშვებისას, გადაამოწმეთ მომსახურების მომწოდებელთან, არის თუ არა მხარდაჭერილი ორივე, მოცულობითი ხმოვანი სიგნალის მიღებაც და მოსმენაც. მაგალითად Twitch, Facebook 360 Live, Mixer RTMP, Smashcast შემთხვევებში, მოცულობითი ხმოვანი სიგნალი, სრულადაა მხარდაჭერილი. მიუხედავად იმისა, რომ Facebook Live და YouTube Live, ორივე იღებს მოცულობით ხმოვან სიგნალს, Facebook Live გარდაქმნის მას სტერეო-სიგნალად, ხოლო YouTube Live უშვებს მხოლოდ ორი არხით.\n\nOBS-ის ხმოვანი ფილტრები თავსებადია მოცულობით ხმოვან სიგნალთან, მაგრამ VST მოდულის მხარდაჭერა, შესაძლოა არ იყოს უზრუნველყოფილი."
+Basic.Settings.Audio.MultichannelWarning.Title="ჩაირთოს მოცულობითი ხმოვანი სიგნალი?"
+Basic.Settings.Audio.MultichannelWarning.Confirm="ნამდვილად გსურთ, ჩართოთ მოცულობითი ხმოვანი სიგნალი?"
+Basic.Settings.Audio.DesktopDevice="ხმის მოწყობილობა"
+Basic.Settings.Audio.DesktopDevice2="ხმის მოწყობილობა 2"
+Basic.Settings.Audio.AuxDevice="მიკროფონი/ხმის დამატებითი მოწყობილობა"
+Basic.Settings.Audio.AuxDevice2="მიკროფონი/ხმის დამატებითი მოწყობილობა 2"
+Basic.Settings.Audio.AuxDevice3="მიკროფონი/ხმის დამატებითი მოწყობილობა 3"
+Basic.Settings.Audio.EnablePushToMute="დაჭერით დადუმების ჩართვა"
+Basic.Settings.Audio.PushToMuteDelay="დაჭერით დადუმების დაყოვნება"
+Basic.Settings.Audio.EnablePushToTalk="დაჭერით საუბრის ჩართვა"
+Basic.Settings.Audio.PushToTalkDelay="დაჭერით საუბრის დაყოვნება"
+Basic.Settings.Audio.UnknownAudioDevice="[მოწყობილობა არაა დაკავშირებული ან მიუწვდომელია]"
+Basic.Settings.Advanced="დამატებითი"
+Basic.Settings.Advanced.General.ProcessPriority="უპირატესობა დამუშავებისას"
+Basic.Settings.Advanced.General.ProcessPriority.High="მაღალი"
+Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="საშუალოზე მაღალი"
+Basic.Settings.Advanced.General.ProcessPriority.Normal="საშუალო"
+Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="საშუალოზე დაბალი"
+Basic.Settings.Advanced.General.ProcessPriority.Idle="უმოქმედო"
+Basic.Settings.Advanced.FormatWarning="გაფრთხილება: ფერთა ფორმატები, გარდა NV12-ისა, ძირითადად განკუთვნილია ჩაწერისთვის და არა ნაკადის გაშვებისთვის. ნაკადის გაშვებისას, ფერთა ფორმატის გარდაქმნებმა შესაძლოა გამოიწვიოს პროცესორის მეტად დატვირთვა."
+Basic.Settings.Advanced.Audio.BufferingTime="ხმის ბუფერის დრო"
+Basic.Settings.Advanced.Video.ColorFormat="ფერთა ფორმატი"
+Basic.Settings.Advanced.Video.ColorSpace="YUV ფერთა სისტემა"
+Basic.Settings.Advanced.Video.ColorRange="YUV ფერთა გამა"
+Basic.Settings.Advanced.Video.ColorRange.Partial="ნაწილობრივი"
+Basic.Settings.Advanced.Video.ColorRange.Full="სრული"
+Basic.Settings.Advanced.Audio.MonitoringDevice="ხმის მოსასმენი მოწყობილობა"
+Basic.Settings.Advanced.Audio.MonitoringDevice.Default="ნაგულისხმევი"
+Basic.Settings.Advanced.Audio.DisableAudioDucking="Windows-ის მიერ ხმის დონის დადაბლების არიდება"
+Basic.Settings.Advanced.StreamDelay="ნაკადის დაყოვნება"
+Basic.Settings.Advanced.StreamDelay.Duration="ხანგრძლივობა (წამი)"
+Basic.Settings.Advanced.StreamDelay.Preserve="მოსაჭრელი წერტილის მომარაგება (დაყოვნების გაზრდა) ხელახლა დაკავშირებისას"
+Basic.Settings.Advanced.StreamDelay.MemoryUsage="მეხსიერების მიახლოებითი მოხმარება: %1 MB"
+Basic.Settings.Advanced.Network="ქსელი"
+Basic.Settings.Advanced.Network.BindToIP="დაკავშირება IP მისამართზე"
+Basic.Settings.Advanced.Network.EnableNewSocketLoop="ახალი ქსელის კოდის ჩართვა"
+Basic.Settings.Advanced.Network.EnableLowLatencyMode="დაყოვნების შემცირება"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="სწრაფი ღილაკების გათიშვა, მთავარ ფანჯარაზე გადასვლისას"
+Basic.AdvAudio="ხმის გაფართოებული პარამეტრები"
Basic.AdvAudio.Name="სახელი"
+Basic.AdvAudio.Volume="ხმის სიმაღლე (%)"
+Basic.AdvAudio.Mono="ერთ არხში გაერთიანება"
+Basic.AdvAudio.Panning="წონასწორობა"
+Basic.AdvAudio.SyncOffset="სინქრონიზაციის გასწორება (მწ)"
+Basic.AdvAudio.Monitoring="ხმის მოსმენა"
+Basic.AdvAudio.Monitoring.None="მოსმენის გარეშე"
+Basic.AdvAudio.Monitoring.MonitorOnly="მხოლოდ მოსმენა (უხმო გამომავალი სიგნალით)"
+Basic.AdvAudio.Monitoring.Both="მოსმენა და გამოტანა"
+Basic.AdvAudio.AudioTracks="ბილიკები"
+Basic.Settings.Hotkeys="სწრაფი ღილაკები"
+Basic.Settings.Hotkeys.Pair="'%1' - ღილაკების ერთობლიობა იმუშავებს გადამრთველის სახით"
+Basic.Hotkeys.SelectScene="სცენაზე გადასვლა"
+Basic.SystemTray.Show="ჩვენება"
+Basic.SystemTray.Hide="დამალვა"
+Basic.SystemTray.Message.Reconnecting="კავშირი გაწყდა. უკავშირდება ხელახლა..."
+Hotkeys.Insert="Insert"
+Hotkeys.Delete="Delete"
+Hotkeys.Home="Home"
+Hotkeys.End="End"
+Hotkeys.PageUp="Page Up"
+Hotkeys.PageDown="Page Down"
+Hotkeys.NumLock="Num Lock"
+Hotkeys.ScrollLock="Scroll Lock"
Hotkeys.CapsLock="Caps Lock"
Hotkeys.Backspace="Backspace"
+Hotkeys.Tab="Tab"
+Hotkeys.Print="Print"
Hotkeys.Pause="პაუზა"
+Hotkeys.Left="მარცხენა ისარი"
+Hotkeys.Right="მარჯვენა ისარი"
+Hotkeys.Up="ზედა ისარი"
+Hotkeys.Down="ქვედა ისარი"
+Hotkeys.Windows="Windows"
Hotkeys.Super="Super"
Hotkeys.Menu="მენიუ"
Hotkeys.Space="Space"
Hotkeys.NumpadNum="Numpad %1"
+Hotkeys.NumpadMultiply="გამრავლება ციფრების დაფაზე"
+Hotkeys.NumpadDivide="გაყოფა ციფრების დაფაზე"
+Hotkeys.NumpadAdd="მიმატება ციფრების დაფაზე"
+Hotkeys.NumpadSubtract="გამოკლება ციფრების დაფაზე"
+Hotkeys.NumpadDecimal="ათწილადის ნიშანი ციფრების დაფაზე"
+Hotkeys.AppleKeypadNum="%1 (კლავიატურაზე)"
+Hotkeys.AppleKeypadMultiply="* (კლავიატურაზე)"
+Hotkeys.AppleKeypadDivide="/ (კლავიატურაზე)"
+Hotkeys.AppleKeypadAdd="+ (კლავიატურაზე)"
+Hotkeys.AppleKeypadSubtract="- (კლავიატურაზე)"
+Hotkeys.AppleKeypadDecimal=". (კლავიატურაზე)"
+Hotkeys.AppleKeypadEqual="= (კლავიატურაზე)"
+Hotkeys.MouseButton="თაგვი %1"
+Mute="დადუმება"
+Unmute="ხმის ჩართვა"
+Push-to-mute="დაჭერისას დადუმება"
+Push-to-talk="დაჭერისას საუბარი"
+SceneItemShow="'%1'-ის გამოჩენა"
+SceneItemHide="'%1'-ის დამალვა"
+OutputWarnings.NoTracksSelected="უნდა მიუთითოთ ერთი ხმოვანი ბილიკი მაინც"
+OutputWarnings.MultiTrackRecording="გაფრთხილება: ცალკეული სახის ფაილებში (როგორიცაა FLV), არაა მხარდაჭერილი რამდენიმე ბილიკი, თითოეული ჩაწერისას"
+OutputWarnings.MP4Recording="გაფრთხილება: MP4 სახით შენახული ჩანაწერები ვეღარ აღდგება, მუშაობის შეწყვეტის შემთხვევაში (მაგ. ლურჯი ეკრანის ამოგდებისას, ძაბვის ვარდნისას და ა.შ.). თუ გსურთ რამდენიმე ხმოვანი ფაილის ჩაწერა, სასურველია ამისთვის გამოიყენოთ MKV და დასრულების შემდეგ გარდაქმნათ MP4-ად. (ფაილი->ჩანაწერების გარდაქმნა)"
+FinalScene.Title="სცენის წაშლა"
+FinalScene.Text="საჭიროებს, სულ მცირე ერთ სცენას."
+
+NoSources.Title="წყაროები არაა"
+NoSources.Text="როგორც ჩანს, თქვენ ჯერ არ დაგიმატებიათ ვიდეოს არცერთი წყარო, შედეგად მიიღებთ ცარიელ ეკრანს. ნამდვილად გსურთ, განაგრძოთ?"
+NoSources.Text.AddSource="წყაროს დამატება შეგიძლიათ ნებისმიერ დროს + ნიშანზე დაწკაპებით, მთავარი ფანჯრის წყაროების არეში."
+
+ChangeBG="ფერის დაყენება"
+CustomColor="ფერის მითითება"
+
+BrowserSource.EnableHardwareAcceleration="ბრაუზერის აპარატურული აჩქარების ჩართვა"
diff --git a/UI/data/locale/ko-KR.ini b/UI/data/locale/ko-KR.ini
index fb684d8..4abb6a2 100644
--- a/UI/data/locale/ko-KR.ini
+++ b/UI/data/locale/ko-KR.ini
@@ -78,6 +78,8 @@ None="없음"
StudioMode.Preview="미리보기"
StudioMode.Program="프로그램"
ShowInMultiview="다중화면으로 표시"
+VerticalLayout="수직으로 배치"
+Group="하나로 묶기"
AlreadyRunning.Title="OBS가 이미 실행 중입니다"
AlreadyRunning.Text="OBS가 이미 실행 중입니다! 의도한 것이 아니라면 새로운 OBS를 실행하기 전에 이미 동작 중인 프로그램을 종료하십시오. OBS가 시스템 트레이에 최소화되어 있는지도 확인하십시오."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="리플레이 버퍼를 멈추고 있습니다..
Basic.Main.StopStreaming="방송 중단"
Basic.Main.StoppingStreaming="방송을 중지합니다..."
Basic.Main.ForceStopStreaming="방송 중지 (지연된 분량도 마무리없이 즉시 송출 중단)"
+Basic.Main.Group="묶음: %1"
+Basic.Main.GroupItems="선택한 항목 묶기"
+Basic.Main.Ungroup="묶음 해체"
Basic.MainMenu.File="파일(&F)"
Basic.MainMenu.File.Export="내보내기(&E)"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="도구(&T)"
Basic.MainMenu.Help="도움말(&H)"
Basic.MainMenu.Help.HelpPortal="도움말 및 포털"
Basic.MainMenu.Help.Website="웹사이트 방문(&W)"
+Basic.MainMenu.Help.Discord="공식 디스코드 참여(&D)"
Basic.MainMenu.Help.Logs="기록 파일(&L)"
Basic.MainMenu.Help.Logs.ShowLogs="기록 파일 표시(S)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="현재 기록 파일 올리기(&C)"
Basic.MainMenu.Help.Logs.UploadLastLog="마지막 기록 파일 올리기(&L)"
Basic.MainMenu.Help.Logs.ViewCurrentLog="현재 기록 보기(&V)"
Basic.MainMenu.Help.CheckForUpdates="판올림 확인"
+Basic.MainMenu.Help.CrashLogs="오류 보고(&R)"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="오류 보고서 보기(&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="마지막 오류 보고서 전송하기(&L)"
Basic.Settings.ProgramRestart="설정을 적용하려면 프로그램을 다시 시작해야 합니다."
Basic.Settings.ConfirmTitle="변경사항 확인"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="작업 표시줄 대신 시스템
Basic.Settings.General.SaveProjectors="종료 시 프로젝터 저장"
Basic.Settings.General.SwitchOnDoubleClick="더블클릭 시 장면으로 전환"
Basic.Settings.General.StudioPortraitLayout="프로젝터를 수직으로 배열"
+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.Horizontal.Top="수평, 위"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="수평, 아래"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="수직, 왼쪽"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="수직, 오른쪽"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="수평, 상단 (8 장면)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="수평, 하단 (8장면)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="수직, 좌측 (8장면)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="수직, 우측 (8장면)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="수평, 상단 (24 장면)"
Basic.Settings.Stream="방송"
Basic.Settings.Stream.StreamType="방송 형식"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="오디오 측정기 감퇴 속도"
Basic.Settings.Audio.MeterDecayRate.Fast="빠름"
Basic.Settings.Audio.MeterDecayRate.Medium="중간 (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="느림 (Type II PPM)"
+Basic.Settings.Audio.PeakMeterType="피크 미터 형식"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="샘플 피크"
+Basic.Settings.Audio.PeakMeterType.TruePeak="트루 피크 (더 높은 CPU 자원 요구)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="경고: 서라운드 음향이 켜져 있습니다."
Basic.Settings.Audio.MultichannelWarning="방송 중이라면 서비스에서 서라운드 음향에 대한 입력 및 재생을 지원하는지 확인하세요. 트위치, 페이스북 360 라이브, Mixer RTMP, Smashcast 는 해당 기능을 사용할 수 있습니다. 페이스북 Live와 유튜브 Live 서비스는 입력은 할 수 있지만 스테레오로 전환하며 유튜브 Live는 오로지 2채널로만 재생합니다.\n\nOBS 오디오 필터는 서라운드 음향을 지원하지만 VST 플러그인 지원은 보장하지 않습니다."
Basic.Settings.Audio.MultichannelWarning.Title="서라운드 음향을 활성화할까요?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="네트워크"
Basic.Settings.Advanced.Network.BindToIP="IP에 고정"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="새로운 네트워크 코드 활성화"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="짧은 지연시간 방식 사용"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="주요 창을 초점으로 둘 때 단축키를 비활성화"
Basic.AdvAudio="오디오 고급 설정"
Basic.AdvAudio.Name="이름"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="경고: MP4로 녹화를 하면 파일이 마무리
FinalScene.Title="장면 삭제"
FinalScene.Text="적어도 하나의 장면은 존재해야 합니다."
+NoSources.Title="소스 없음"
+NoSources.Text="어떠한 영상 소스도 추가하지 않아서 빈 화면만 송출할 것입니다. 그래도 계속하시겠습니까?"
+NoSources.Text.AddSource="주 화면 소스 목록에 있는 + 아이콘을 누르면 소스를 추가할 수 있습니다."
+
+ChangeBG="색상 지정"
+CustomColor="사용자 색상"
+
+BrowserSource.EnableHardwareAcceleration="브라우저 소스에 하드웨어 가속 활성화"
+
diff --git a/UI/data/locale/lt-LT.ini b/UI/data/locale/lt-LT.ini
index 8e59372..97e889f 100644
--- a/UI/data/locale/lt-LT.ini
+++ b/UI/data/locale/lt-LT.ini
@@ -326,3 +326,6 @@ Push-to-talk="Kalbėti paspaudus"
FinalScene.Title="Ištrinti sceną"
FinalScene.Text="Turi būti bent viena scena."
+
+
+
diff --git a/UI/data/locale/ms-MY.ini b/UI/data/locale/ms-MY.ini
index c0b7375..1413cc0 100644
--- a/UI/data/locale/ms-MY.ini
+++ b/UI/data/locale/ms-MY.ini
@@ -52,11 +52,34 @@ Reset="Set semula"
Hours="Jam"
Minutes="Minit"
Seconds="Saat"
+Import="Import"
+Export="Eksport"
+Copy="Salin"
+Paste="Tampal"
+PasteReference="Tampal (rujukan)"
+PasteDuplicate="Tampal (dua salinan)"
+Next="Seterusnya"
+Back="Kembali"
+BandwidthTest.Region.EU="Eropah"
+BandwidthTest.Region.Asia="Asia"
+BandwidthTest.Region.Other="Lain-lain"
+Basic.AutoConfig.ApplySettings="Gunakan seting"
+Basic.AutoConfig.StartPage="Maklumat penggunaan"
+Basic.AutoConfig.VideoPage="Seting video"
+Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Gunakan semasa (%1x%2)"
+Basic.AutoConfig.VideoPage.BaseResolution.Display="Paparan %1 (%2x%3)"
+Basic.AutoConfig.VideoPage.FPS.UseCurrent="Gunakan semasa (%1)"
+Basic.AutoConfig.StreamPage="Maklumat aliran"
+Basic.AutoConfig.StreamPage.SubTitle="Sila taip maklumat aliran anda"
+Basic.AutoConfig.StreamPage.Service="Perkhidmatan-perkhidmatan"
+Basic.AutoConfig.StreamPage.Service.ShowAll="Tunjuk semua..."
+Basic.AutoConfig.StreamPage.Server="Server"
+Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)"
@@ -428,3 +451,6 @@ Push-to-talk="Tekan-untuk-cakap"
+
+
+
diff --git a/UI/data/locale/nb-NO.ini b/UI/data/locale/nb-NO.ini
index 82be312..35561a4 100644
--- a/UI/data/locale/nb-NO.ini
+++ b/UI/data/locale/nb-NO.ini
@@ -78,6 +78,8 @@ None="Ingen"
StudioMode.Preview="Forhåndsvisning"
StudioMode.Program="Program"
ShowInMultiview="Vis i Multiview"
+VerticalLayout="Loddrett oppsett"
+Group="Gruppe"
AlreadyRunning.Title="OBS kjører allerede"
AlreadyRunning.Text="OBS kjører allerede! Med mindre du ikke mente dette, vennligst lukk alle eksisterende kjørende tilfeller av OBS før du kjører noen nye. Hvis du har satt OBS til å minimere til systemkurven, vennligst sjekk om den fortsatt kjører der."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Stopper omspillingsbufferen..."
Basic.Main.StopStreaming="Stopp Strømming"
Basic.Main.StoppingStreaming="Stanser strøm…"
Basic.Main.ForceStopStreaming="Stopp strømming (forkast forsinkelse)"
+Basic.Main.Group="Gruppe %1"
+Basic.Main.GroupItems="Gruppér merkede gjenstander"
+Basic.Main.Ungroup="Adskill"
Basic.MainMenu.File="&Fil"
Basic.MainMenu.File.Export="&Eksportér"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Verktøy"
Basic.MainMenu.Help="&Hjelp"
Basic.MainMenu.Help.HelpPortal="&Portal for hjelp"
Basic.MainMenu.Help.Website="Besøk &nettstedet"
+Basic.MainMenu.Help.Discord="Bli med i &Discord-serveren"
Basic.MainMenu.Help.Logs="&Loggfiler"
Basic.MainMenu.Help.Logs.ShowLogs="Vis &loggfiler"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Last opp &nåværende loggfil"
Basic.MainMenu.Help.Logs.UploadLastLog="Last opp &siste loggfil"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Vis gjeldende logg"
Basic.MainMenu.Help.CheckForUpdates="Se etter oppdateringer"
+Basic.MainMenu.Help.CrashLogs="Krasjrapporter"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Vi&s krasjrapporter"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Last opp den &nyeste krasjrapporten"
Basic.Settings.ProgramRestart="Programmet må startes på nytt for at disse innstillingene skal tre i kraft."
Basic.Settings.ConfirmTitle="Bekreft endringer"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Alltid minimere til systemstatusf
Basic.Settings.General.SaveProjectors="Lagre projektorer ved avslutning"
Basic.Settings.General.SwitchOnDoubleClick="Dobbeltklikking vil gå til scenen"
Basic.Settings.General.StudioPortraitLayout="Aktiver portrett/loddrett vindu"
+Basic.Settings.General.Multiview="Flervisning"
+Basic.Settings.General.Multiview.MouseSwitch="Klikk for å bytte mellom scener"
+Basic.Settings.General.Multiview.DrawSourceNames="Vis scenenes navn"
+Basic.Settings.General.Multiview.DrawSafeAreas="Tegn trygge områder (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Flervisningsplassering"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Vannrett, øverst"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Vannrett, nederst"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Loddrett, venstre"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Loddrett, høyre"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Vannrett, topp (8 scener)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Vannrett, bunn (8 scener)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Loddrett, venstre (8 scener)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Loddrett, høyre (8 scener)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Vannrett, topp (24 scener)"
Basic.Settings.Stream="Strøm"
Basic.Settings.Stream.StreamType="Strømmetype"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Audiometerets forfallsfrekvens"
Basic.Settings.Audio.MeterDecayRate.Fast="Raskt"
Basic.Settings.Audio.MeterDecayRate.Medium="Middels (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Tregt (Type II PPM)"
+Basic.Settings.Audio.PeakMeterType="Toppunktmålertype"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Samplingstoppunkt"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Ekte toppunkt (Høyere CPU-bruk)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="Advarsel: Surroundlyd er aktivert."
Basic.Settings.Audio.MultichannelWarning="Hvis du strømmer, sjekk om strømmetjenesten din støtter både surround-lydinnføring og surround-lydavspilling. Twitch, Facebook 360 Live, Mixer RTMP, og Smashcast er eksempler hvor surroundlyd er full støttet. Selv om Facebook Live og YouTube Live begge støtter surround-innføring, nedmikser Facebook Live det ned til Stereo, og YouTube Live spiller bare av to kanaler.\n\nOBS-lydfiltre er kompatible med surroundlyd, selv om VST-tilleggsstøtte ikke er garantert."
Basic.Settings.Audio.MultichannelWarning.Title="Vil du aktivere surround-lyd?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Nettverk"
Basic.Settings.Advanced.Network.BindToIP="Bind til IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Aktiver den nye nettverkskoden"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Lavlatens-modus"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Deaktiver hurtigtaster når hovedvinduet er i fokus"
Basic.AdvAudio="Avanserte lydinnstillinger"
Basic.AdvAudio.Name="Navn"
@@ -749,3 +767,11 @@ OutputWarnings.MP4Recording="Advarsel: Opptak lagret i MP4 bil bli slettet hvis
FinalScene.Title="Slett scene"
FinalScene.Text="Det må være minst én scene."
+NoSources.Title="Ingen kilder"
+NoSources.Text="Det ser ut som du ikke har lagt til noen videokilder ennå, så du vil kun sende en blank skjerm. Er du sikker på at du vil gjøre dette?"
+NoSources.Text.AddSource="Du kan legge til kilder ved å klikke på +-ikonet under Kilder-boksen i hovedvinduet til enhver tid."
+
+ChangeBG="Velg farge"
+CustomColor="Egendefinert farge"
+
+
diff --git a/UI/data/locale/nl-NL.ini b/UI/data/locale/nl-NL.ini
index 07ede93..34ceac9 100644
--- a/UI/data/locale/nl-NL.ini
+++ b/UI/data/locale/nl-NL.ini
@@ -42,7 +42,7 @@ Clear="Wissen"
Revert="Herstellen"
Show="Weergeven"
Hide="Verbergen"
-UnhideAll="Verberge alle"
+UnhideAll="Allemaal zichtbaar maken"
Untitled="Naamloos"
New="Nieuw"
Duplicate="Dupliceren"
@@ -78,6 +78,8 @@ None="Geen"
StudioMode.Preview="Preview"
StudioMode.Program="Programma"
ShowInMultiview="Weergeven in Multiview"
+VerticalLayout="Verticale Lay-out"
+Group="Groep"
AlreadyRunning.Title="OBS is al actief"
AlreadyRunning.Text="OBS is al actief! Tenzij je dit wilde doen, sluit a.u.b. alle reeds draaiende instanties van OBS voor je een nieuwe instantie opstart. Als je OBS hebt ingesteld om naar het systeemvak te minimaliseren, controleer dan of hij daar nog staat."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Replay Buffer aan het stoppen..."
Basic.Main.StopStreaming="Stream Stoppen"
Basic.Main.StoppingStreaming="Stream Stoppen..."
Basic.Main.ForceStopStreaming="Stop Stream (vertraging negeren)"
+Basic.Main.Group="Groep %1"
+Basic.Main.GroupItems="Groepeer geselecteerde items"
+Basic.Main.Ungroup="Degroeperen"
Basic.MainMenu.File="&Bestand"
Basic.MainMenu.File.Export="&Exporteren"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Tools"
Basic.MainMenu.Help="&Help"
Basic.MainMenu.Help.HelpPortal="Help-&portal"
Basic.MainMenu.Help.Website="&Website Bezoeken"
+Basic.MainMenu.Help.Discord="Wordt lid van &Discord server"
Basic.MainMenu.Help.Logs="&Logbestanden"
Basic.MainMenu.Help.Logs.ShowLogs="Logbe&standen Weergeven"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Upload &Huidige Logbestand"
Basic.MainMenu.Help.Logs.UploadLastLog="Upload &Laatste Logbestand"
Basic.MainMenu.Help.Logs.ViewCurrentLog="Toon Huidige Logbestand (&V)"
Basic.MainMenu.Help.CheckForUpdates="Controleer Op Updates"
+Basic.MainMenu.Help.CrashLogs="Fouten&rapport"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Toon crashrapporten (&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Upload &laatste crash rapport"
Basic.Settings.ProgramRestart="Het programma moet opnieuw worden opgestart om deze instellingen te activeren."
Basic.Settings.ConfirmTitle="Wijzigingen Bevestigen"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Altijd minimaliseren naar het sys
Basic.Settings.General.SaveProjectors="Projectors opslaan bij afsluiten"
Basic.Settings.General.SwitchOnDoubleClick="Ga over naar scène bij dubbelklik"
Basic.Settings.General.StudioPortraitLayout="Portret/verticale layout inschakelen"
+Basic.Settings.General.Multiview="Multiview"
+Basic.Settings.General.Multiview.MouseSwitch="Klik hier om tussen scènes te wisselen"
+Basic.Settings.General.Multiview.DrawSourceNames="Toon namen van scènes"
+Basic.Settings.General.Multiview.DrawSafeAreas="Veilige gebieden (EBU R 95) tekenen"
Basic.Settings.General.MultiviewLayout="Multiview-indeling"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontaal, Boven"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontaal, Onder"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Verticaal, Links"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Verticaal, Rechts"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontaal, boven (8 scènes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontaal, onder (8 scènes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Verticaal, links (8 scènes)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Verticaal, rechts (8 scènes)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontaal, boven (24 scènes)"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Stream Type"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Vervalsnelheid Volumemeter"
Basic.Settings.Audio.MeterDecayRate.Fast="Snel"
Basic.Settings.Audio.MeterDecayRate.Medium="Gemiddeld (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Traag (Type II PPM)"
+Basic.Settings.Audio.PeakMeterType="Piek Meter Type"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Sample piek"
+Basic.Settings.Audio.PeakMeterType.TruePeak="True piek (hogere CPU-gebruik)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="Waarschuwing: Surround sound audio is ingeschakeld."
Basic.Settings.Audio.MultichannelWarning="Als je streamt, controleer dan of je streaming service zowel surround sound ingest als surround sound afspelen ondersteunt. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast zijn voorbeelden waar surround sound volledig ondersteund is. Alhoewel Facebook Live en Youtube Live beide surround ingest ondersteunen, downmixt Facebook Live het naar stereo, terwijl Youtube Live slechts twee kanalen afspeelt.\n\nOBS audio filters kunnen overweg met surround sound, maar ondersteuning bij VST plugins is niet gegarandeerd."
Basic.Settings.Audio.MultichannelWarning.Title="Surround sound audio inschakelen?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Netwerk"
Basic.Settings.Advanced.Network.BindToIP="Bind aan IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Schakel nieuwe netwerkcode in"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Lage latency modus"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Hotkeys uitschakelen wanneer hoofdvenster in focus is"
Basic.AdvAudio="Geavanceerde Audioinstellingen"
Basic.AdvAudio.Name="Naam"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Waarschuwing: Opnames opgeslagen als MP4 zijn niet
FinalScene.Title="Verwijder scène"
FinalScene.Text="Er moet tenminste één scène zijn."
+NoSources.Title="Geen bronnen"
+NoSources.Text="Er zijn nog geen video bronnen toegevoegd, er zal dus een leeg scherm weergegeven worden. Weet je zeker dat je dit wilt doen?"
+NoSources.Text.AddSource="Bronnen kunnen toegevoegd worden door op het + icoon onder het vak \"Bronnen\" in het hoofdvenster te klikken."
+
+ChangeBG="Kleur instellen"
+CustomColor="Aangepaste kleur"
+
+BrowserSource.EnableHardwareAcceleration="Browser bron hardwareversnelling inschakelen"
+
diff --git a/UI/data/locale/nn-NO.ini b/UI/data/locale/nn-NO.ini
index 9ddedc3..582e19a 100644
--- a/UI/data/locale/nn-NO.ini
+++ b/UI/data/locale/nn-NO.ini
@@ -197,6 +197,9 @@ Basic.Settings.General.Language="Språk"
+
+
+
diff --git a/UI/data/locale/pl-PL.ini b/UI/data/locale/pl-PL.ini
index ace22ef..3ed7ca6 100644
--- a/UI/data/locale/pl-PL.ini
+++ b/UI/data/locale/pl-PL.ini
@@ -78,6 +78,8 @@ None="Brak"
StudioMode.Preview="Podgląd"
StudioMode.Program="Program"
ShowInMultiview="Pokaż w Multiview"
+VerticalLayout="Układ pionowy"
+Group="Grupa"
AlreadyRunning.Title="OBS jest już uruchomiony"
AlreadyRunning.Text="OBS jest już uruchomiony! Sprawdź wszystkie uruchomione wystąpienia OBS zanim uruchomisz go jeszcze raz. Jeżeli OBS jest zminimalizowany do zasobnika systemowego, sprawdź czy nie jest uruchomiony także w tym miejscu."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Zatrzymywanie buforu replay..."
Basic.Main.StopStreaming="Zatrzymaj stream"
Basic.Main.StoppingStreaming="Zatrzymywanie streamowania..."
Basic.Main.ForceStopStreaming="Zatrzymaj stream (anuluj opóźnienie)"
+Basic.Main.Group="Grupa %1"
+Basic.Main.GroupItems="Grupuj wybrane elementy"
+Basic.Main.Ungroup="Rozgrupuj"
Basic.MainMenu.File="&Plik"
Basic.MainMenu.File.Export="&Eksport"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Narzędzia"
Basic.MainMenu.Help="P&omoc"
Basic.MainMenu.Help.HelpPortal="&Portal pomocy"
Basic.MainMenu.Help.Website="Od&wiedź naszą stronę"
+Basic.MainMenu.Help.Discord="Dołącz do serwera &Discord"
Basic.MainMenu.Help.Logs="P&liki dziennika"
Basic.MainMenu.Help.Logs.ShowLogs="Pokaż pliki dziennika (&s)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Wyślij &aktualny plik dziennika"
Basic.MainMenu.Help.Logs.UploadLastLog="Wyślij o&statni plik dziennika"
Basic.MainMenu.Help.Logs.ViewCurrentLog="Podgląd akty&wnego pliku dziennika"
Basic.MainMenu.Help.CheckForUpdates="Sprawdź dostępność aktualizacji"
+Basic.MainMenu.Help.CrashLogs="&Raporty o awariach"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Pokaż raporty o awariach (&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Wyślij ostatni raport o awariach (&L)"
Basic.Settings.ProgramRestart="Aby te ustawienia zaczęły obowiązywać, należy ponownie uruchomić program."
Basic.Settings.ConfirmTitle="Potwierdź zmiany"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Zawsze minimalizuj do zasobnika s
Basic.Settings.General.SaveProjectors="Zapisz konfigurację podglądów na pełnym ekranie przy zamknięciu aplikacji"
Basic.Settings.General.SwitchOnDoubleClick="Przejdź do sceny po dwukrotnym kliknięciu"
Basic.Settings.General.StudioPortraitLayout="Włącz układ pionowy"
+Basic.Settings.General.Multiview="Multiview"
+Basic.Settings.General.Multiview.MouseSwitch="Kliknij, aby przełączać się między scenami"
+Basic.Settings.General.Multiview.DrawSourceNames="Pokaż nazwy scen"
+Basic.Settings.General.Multiview.DrawSafeAreas="Rysowanie bezpiecznych obszarów (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Podgląd wielokrotny"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Poziomo, Góra"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Poziomo, Dół"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Pionowo, Lewo"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Pionowo, Prawo"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Poziomo, Góra (8 scen)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Poziomo, Dół (8 scen)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Pionowo, Lewo (8 scen)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Pionowo, Prawo (8 scen)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Poziomo, Góra (24 sceny)"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Typ streamu"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Szybkość powrotu miernika audio"
Basic.Settings.Audio.MeterDecayRate.Fast="Szybko"
Basic.Settings.Audio.MeterDecayRate.Medium="Średnio (PPM typu I)"
Basic.Settings.Audio.MeterDecayRate.Slow="Wolno (PPM typu II)"
+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 streamowania sprawdź, czy docelowa usługa obsługuje wielokanałowe wejście oraz odtwarzanie dźwięku surround. Twitch, Facebook, 360 Live, Mixer RTMP, Smashcast to przykłady usług obsługujących dźwięk wielokanałowy. O ile Facebook Live i Youtube Live obsługują wejście dźwięku wielokanałowego, to w przypadku odtwarzania Facebook miksuje kanału do stereo a Youtube Live odtwarza tylko dwa kanały.\n\nFiltry dźwiękowe OBS są w pełni kompatybilne z dźwiękiem kanałowym, natomiast wsparcie pluginów VST nie jest gwarantowane."
Basic.Settings.Audio.MultichannelWarning.Title="Włączyć dźwięk przestrzenny?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Sieć"
Basic.Settings.Advanced.Network.BindToIP="Przypisane IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Aktywuj nowy kod sieciowy"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Tryb niskich opóźnień"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Wyłącz skróty klawiszowe, gdy główne okno programu jest aktywne na pierwszym planie"
Basic.AdvAudio="Zaawansowane ustawienia dźwięku"
Basic.AdvAudio.Name="Nazwa"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Ostrzeżenie: Nagrania zapisanego w formacie mp4 ni
FinalScene.Title="Usuń scenę"
FinalScene.Text="Musi być co najmniej jedna scena."
+NoSources.Title="Brak źródeł"
+NoSources.Text="Wygląda na to, że nie dodano żadnych źródeł video. Na wyjściu otrzymasz tylko czarny ekran. Czy na pewno chcesz to zrobić?"
+NoSources.Text.AddSource="Źródła dodać można w każdym momencie klikając przycisk + w okienku Źródła głównego ekranu aplikacji."
+
+ChangeBG="Ustaw kolor"
+CustomColor="Kolor niestandardowy"
+
+BrowserSource.EnableHardwareAcceleration="Włącz akcelerację sprzętową w pluginie przeglądarki"
+
diff --git a/UI/data/locale/pt-BR.ini b/UI/data/locale/pt-BR.ini
index a036101..5938697 100644
--- a/UI/data/locale/pt-BR.ini
+++ b/UI/data/locale/pt-BR.ini
@@ -78,6 +78,8 @@ None="Nenhuma"
StudioMode.Preview="Prévia"
StudioMode.Program="Programa"
ShowInMultiview="Mostrar no Multiview"
+VerticalLayout="Layout Vertical"
+Group="Grupo"
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."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Parando Buffer do Replay..."
Basic.Main.StopStreaming="Parar Transmissão"
Basic.Main.StoppingStreaming="Parando Transmissão..."
Basic.Main.ForceStopStreaming="Pare de transmitir (descartar atraso)"
+Basic.Main.Group="Grupo %1"
+Basic.Main.GroupItems="Agrupar Itens Selecionados"
+Basic.Main.Ungroup="Desagrupar"
Basic.MainMenu.File="&Arquivo"
Basic.MainMenu.File.Export="&Exportar"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="Ferramentas (&T)"
Basic.MainMenu.Help="&Ajuda"
Basic.MainMenu.Help.HelpPortal="&Portal de Ajuda"
Basic.MainMenu.Help.Website="Visitar &website"
+Basic.MainMenu.Help.Discord="Juntar-se ao Servidor do &Discord"
Basic.MainMenu.Help.Logs="&Arquivos de Log"
Basic.MainMenu.Help.Logs.ShowLogs="&Mostrar Arquivos de Log"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Enviar &Arquivo de Log Atual"
Basic.MainMenu.Help.Logs.UploadLastLog="Enviar &Ultimo Arquivo de Log"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Exibir Log atual"
Basic.MainMenu.Help.CheckForUpdates="Verificar se há atualizações"
+Basic.MainMenu.Help.CrashLogs="&Relatórios de erros"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Exibir relatórios de erro&s"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Enviar ú<imo relatório de erros"
Basic.Settings.ProgramRestart="O Programa precisar ser reiniciado para que estas configurações surtam efeito."
Basic.Settings.ConfirmTitle="Confirmar Alterações"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Sempre minimizar para a bandeja (
Basic.Settings.General.SaveProjectors="Salvar projetores ao sair"
Basic.Settings.General.SwitchOnDoubleClick="Mudar para a cena quando clicar duas vezes"
Basic.Settings.General.StudioPortraitLayout="Ativar o layout Paisagem/Retrato"
+Basic.Settings.General.Multiview="Multiview"
+Basic.Settings.General.Multiview.MouseSwitch="Clique para alternar entre cenas"
+Basic.Settings.General.Multiview.DrawSourceNames="Mostrar nome das cenas"
+Basic.Settings.General.Multiview.DrawSafeAreas="Mostrar áreas de segurança (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Layout do Multiview"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, Acima"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, Abaixo"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, à Esquerda"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, à Direita"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, Acima (8 Cenas)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, Abaixo (8 Cenas)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, à Esquerda (8 Cenas)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, à Direita (8 Cenas)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontal, Acima (24 Cenas)"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Tipo de Stream"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Taxa de Decaimento do Medidor de Áudio"
Basic.Settings.Audio.MeterDecayRate.Fast="Rápida"
Basic.Settings.Audio.MeterDecayRate.Medium="Média (PPM Tipo I)"
Basic.Settings.Audio.MeterDecayRate.Slow="Devagar (PPM Tipo II)"
+Basic.Settings.Audio.PeakMeterType="Tipo do medidor de pico"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Exemplo de pico"
+Basic.Settings.Audio.PeakMeterType.TruePeak="True Peak (Uso elevado de CPU)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="AVISO: Áudio Surround está habilitado."
Basic.Settings.Audio.MultichannelWarning="Antes de transmitir, verifique se o seu serviço de transmissão suporta tanto receber como reproduzir som surround. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast são exemplos onde o som surround é totalmente suportado. Embora o Facebook Live e o YouTube Live ambos aceitem receber som surround, o Facebook Live transformará para estéreo e o YouTube Live reproduz apenas dois canais.\n\nOs filtros de áudio do OBS são compatíveis com o som surround, embora o suporte do plugin VST não seja garantido."
Basic.Settings.Audio.MultichannelWarning.Title="Ativar o som surround?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Rede"
Basic.Settings.Advanced.Network.BindToIP="Transmitir pelo IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Habilitar o novo código de rede"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Modo de baixa latência"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Desativar teclas de atalho quando a janela principal estiver em foco"
Basic.AdvAudio="Propriedades de áudio avançadas"
Basic.AdvAudio.Name="Nome"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Atenção: Gravações salvas em arquivos MP4 se to
FinalScene.Title="Excluir cena"
FinalScene.Text="É preciso haver pelo menos uma cena."
+NoSources.Title="Sem Fontes"
+NoSources.Text="Parece que você ainda não adicionou nenhuma fonte de vídeo, portanto, você só exibirá uma tela em branco. Você tem certeza de que quer fazer isso?"
+NoSources.Text.AddSource="Você pode adicionar fontes clicando no ícone + sob a caixa de Fontes na janela principal, a qualquer momento."
+
+ChangeBG="Definir Cor"
+CustomColor="Cor Personalizada"
+
+BrowserSource.EnableHardwareAcceleration="Habilitar a aceleração por Hardware do Navegador"
+
diff --git a/UI/data/locale/pt-PT.ini b/UI/data/locale/pt-PT.ini
index 25c82a7..5c6cd0e 100644
--- a/UI/data/locale/pt-PT.ini
+++ b/UI/data/locale/pt-PT.ini
@@ -478,10 +478,6 @@ Basic.Settings.General.RecordWhenStreaming="Gravar automaticamente quando estive
Basic.Settings.General.KeepRecordingWhenStreamStops="Continuar a gravar quando a transmissão parar"
Basic.Settings.General.SysTray="Bandeja do Sistema"
Basic.Settings.General.SysTrayWhenStarted="Minimizar para a área de notificações quando iniciado"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, Topo"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, Inferior"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, Esquerda"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, Direita"
Basic.Settings.Stream="Transmissão"
Basic.Settings.Stream.StreamType="Tipo de transmissão"
@@ -697,3 +693,6 @@ OutputWarnings.MultiTrackRecording="Aviso: Alguns formatos (como FLV) não supor
FinalScene.Title="Apagar Cena"
+
+
+
diff --git a/UI/data/locale/ro-RO.ini b/UI/data/locale/ro-RO.ini
index 0c3b094..43208f0 100644
--- a/UI/data/locale/ro-RO.ini
+++ b/UI/data/locale/ro-RO.ini
@@ -598,3 +598,6 @@ OutputWarnings.MultiTrackRecording="Atenție: Anumite formate (precum FLV) nu su
FinalScene.Text="Trebuie să existe cel puțin o scenă."
+
+
+
diff --git a/UI/data/locale/ru-RU.ini b/UI/data/locale/ru-RU.ini
index 39a90ce..cb2a1a7 100644
--- a/UI/data/locale/ru-RU.ini
+++ b/UI/data/locale/ru-RU.ini
@@ -78,6 +78,8 @@ None="Нет"
StudioMode.Preview="Предпросмотр"
StudioMode.Program="Программа"
ShowInMultiview="Отображать в Мульти-обзоре"
+VerticalLayout="Вертикальное расположение"
+Group="Группа"
AlreadyRunning.Title="OBS уже запущен"
AlreadyRunning.Text="OBS уже запущен! Пожалуйста, закройте все запущенные экземпляры OBS перед попыткой запустить новые (только если вы не хотели именно этого). Если вы настроили OBS на сворачивание в системный трей, пожалуйста, проверьте, возможно он до сих пор запущен."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Остановка повтора..."
Basic.Main.StopStreaming="Остановить трансляцию"
Basic.Main.StoppingStreaming="Остановка вещания..."
Basic.Main.ForceStopStreaming="Остановить трансляцию (сбросить задержку)"
+Basic.Main.Group="Группа %1"
+Basic.Main.GroupItems="Сгруппировать выбранные элементы"
+Basic.Main.Ungroup="Разгруппировать"
Basic.MainMenu.File="&Файл"
Basic.MainMenu.File.Export="&Экспорт"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Инструменты"
Basic.MainMenu.Help="&Справка"
Basic.MainMenu.Help.HelpPortal="&Портал помощи"
Basic.MainMenu.Help.Website="Посетить &веб-сайт"
+Basic.MainMenu.Help.Discord="Зайти на сервер &Discord"
Basic.MainMenu.Help.Logs="&Log файлы"
Basic.MainMenu.Help.Logs.ShowLogs="&Показать лог-файлы"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Загрузить &текущий Log файл"
Basic.MainMenu.Help.Logs.UploadLastLog="Загрузить &последний Log файл"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Просмотреть текущий журнал"
Basic.MainMenu.Help.CheckForUpdates="Проверить наличие обновлений"
+Basic.MainMenu.Help.CrashLogs="&Отчёты об ошибках"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="&Показать отчёты об ошибках"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="&Загрузить последний отчёт об ошибке"
Basic.Settings.ProgramRestart="Для изменения этих параметров требуется перезапустить программу."
Basic.Settings.ConfirmTitle="Подтверждить Изменения"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Всегда сворачиват
Basic.Settings.General.SaveProjectors="Сохранять проекторы при выходе"
Basic.Settings.General.SwitchOnDoubleClick="Переход к сцене при двойном щелчке"
Basic.Settings.General.StudioPortraitLayout="Включить портретное/вертикальное расположение"
+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.Horizontal.Top="Горизонтально, вверху"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Горизонтально, внизу"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Вертикально, слева"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Вертикально, справа"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Горизонтально, верх (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Горизонтально, низ (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Вертикально, слева (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Вертикально, справа (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Горизонтально, верх (24 сцены)"
Basic.Settings.Stream="Вещание"
Basic.Settings.Stream.StreamType="Тип вещания"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Скорость спада аудиоме
Basic.Settings.Audio.MeterDecayRate.Fast="Быстро"
Basic.Settings.Audio.MeterDecayRate.Medium="Средне (PPM типа I)"
Basic.Settings.Audio.MeterDecayRate.Slow="Медленно (PPM типа II)"
+Basic.Settings.Audio.PeakMeterType="Тип измерителя пиков"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Упрощенный"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Точный (Повышенное использование ЦП)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="ВНИМАНИЕ: Объемный звук включен."
Basic.Settings.Audio.MultichannelWarning="При запуске стрима проверьте, поддерживает ли ваш сервис потокового вещания объемный звук (как запись, так и воспроизведение). Twitch, Facebook 360 Live, Mixer RTMP, Smashcast — примеры сервисов, полностью поддерживающих объемное звучание. Несмотря на то, что Facebook Live и Youtube Live принимают объёмный звук, Facebook Live микширует его в стерео, а Youtube Live воспроизводит только два канала.\n\nЗвуковые фильтры OBS совместимы с объемным звучанием, хотя поддержка плагинов VST не гарантируется."
Basic.Settings.Audio.MultichannelWarning.Title="Включить объемный звук?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Сеть"
Basic.Settings.Advanced.Network.BindToIP="Привязать к IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Включить новый сетевой код"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Режим низкой задержки"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Отключить горячие клавиши, если главное окно находится в фокусе"
Basic.AdvAudio="Расширенные свойства аудио"
Basic.AdvAudio.Name="Название"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Внимание: Записи, сохраненн
FinalScene.Title="Удалить сцену"
FinalScene.Text="Здесь должна быть по крайней мере одна сцена."
+NoSources.Title="Нет источников"
+NoSources.Text="Похоже, вы еще не добавили ни одного источника. Вы будете выводить только пустой экран. Вы уверены, что хотите этого?"
+NoSources.Text.AddSource="Вы можете добавить источники, нажав иконку + под окном Источники в главном окне в любое время."
+
+ChangeBG="Установить цвет"
+CustomColor="Пользовательский цвет"
+
+BrowserSource.EnableHardwareAcceleration="Включить аппаратное ускорение Браузера"
+
diff --git a/UI/data/locale/sk-SK.ini b/UI/data/locale/sk-SK.ini
index fc9bb99..fd45ee8 100644
--- a/UI/data/locale/sk-SK.ini
+++ b/UI/data/locale/sk-SK.ini
@@ -78,6 +78,8 @@ None="Nič"
StudioMode.Preview="Náhľad"
StudioMode.Program="Program"
ShowInMultiview="Zobraziť v Multiview"
+VerticalLayout="Vertikálne rozloženie"
+Group="Skupina"
AlreadyRunning.Title="OBS je už spustený"
AlreadyRunning.Text="OBS je už spustená! Prosím vypnite všetky existujúce inštancie OBS pred pokusom o spustenie novej inštancie. Ak máte OBS minimalizovaný do systémovej lišty, prosím skontrolujte či tam stále beží."
@@ -112,28 +114,41 @@ Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 alebo 30, ale radšej 60, ak je
Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 alebo 30, ale radšej vysoké rozlíšenie"
Basic.AutoConfig.VideoPage.CanvasExplanation="Poznámka: rozlíšenie (základné) plátna nie je nevyhnutne rovnaké ako rozlíšenie v ktorom budete streamovať alebo nahrávať. Skutočné streamovacie/nahrávacie rozlíšenie môže byť zmenšené preto aby sa znížilo používanie dát alebo zdrojov."
Basic.AutoConfig.StreamPage="Informácie o streame"
+Basic.AutoConfig.StreamPage.SubTitle="Prosím, zadajte svoje údaje o vysielaní"
Basic.AutoConfig.StreamPage.Service="Služba"
Basic.AutoConfig.StreamPage.Service.ShowAll="Zobraziť všetky..."
Basic.AutoConfig.StreamPage.Server="Server"
Basic.AutoConfig.StreamPage.StreamKey="Streamovací kľúč"
Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Odkaz)"
+Basic.AutoConfig.StreamPage.PerformBandwidthTest="Odhadnúť bitrate pomocou testu rýchlosti pripojenia (môže to trvať pár minút)"
Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferovať hardvérové enkódovanie"
+Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardwarové 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.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é"
+Basic.AutoConfig.TestPage.TestingBandwidth="Prebieha testovanie rýchlosti pripojenia. Toto môže chvíľku trvať..."
Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Pripájanie k: %1..."
+Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Nepodarilo sa pripojiť k žiadnemu serveru. Skontrolujte vaše internetové pripojenie a skúste to znovu."
+Basic.AutoConfig.TestPage.TestingBandwidth.Server="Testovanie rýchlosti pripojenia pre: %1"
Basic.AutoConfig.TestPage.TestingStreamEncoder="Testovanie streamovacieho enkodéra, toto môže trvať minútu..."
Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testovanie nahrávacieho enkodéra, toto môže trvať minútu..."
Basic.AutoConfig.TestPage.TestingRes="Testovanie rozlíšení, toto môže trvať niekoľko minút..."
Basic.AutoConfig.TestPage.TestingRes.Fail="Nepodarilo sa spustiť enkodér"
Basic.AutoConfig.TestPage.TestingRes.Resolution="Testovanie %1x%2 %3 FPS..."
-Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streamovací Enkodér"
-Basic.AutoConfig.TestPage.Result.RecordingEncoder="Nahrávací Enkodér"
+Basic.AutoConfig.TestPage.Result.StreamingEncoder="Kodér vysielania"
+Basic.AutoConfig.TestPage.Result.RecordingEncoder="Kodér nahrávania"
+Basic.AutoConfig.TestPage.Result.Header="Program zistil, že nasledujúce nastavenia by mohli byť pre vás najvhodnejšie:"
+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.Stats="Štatistiky"
Basic.Stats.CPUUsage="Využitie CPU"
Basic.Stats.HDDSpaceAvailable="Dostupné miesto na HDD"
Basic.Stats.MemoryUsage="Využitie pamäte"
+Basic.Stats.AverageTimeToRender="Priemerný čas vykreslenia snímku"
+Basic.Stats.SkippedFrames="Preskočené snímky kvôli chybe kódovania"
+Basic.Stats.MissedFrames="Nevyužité snímky kvôli chybe vykresľovania"
Basic.Stats.Output.Stream="Stream"
Basic.Stats.Output.Recording="Nahrávanie"
Basic.Stats.Status="Stav"
@@ -151,15 +166,23 @@ Updater.UpdateNow="Aktualizovať teraz"
Updater.RemindMeLater="Pripomenúť neskôr"
Updater.Skip="Preskočiť verziu"
Updater.Running.Title="Program momentálne aktívny"
+Updater.Running.Text="Niektoré výstupy sú stále aktívne. Zastavte ich pred ďalším pokusom o aktualizáciu"
Updater.NoUpdatesAvailable.Title="Neboli nájdené žiadne aktualizácie"
Updater.NoUpdatesAvailable.Text="Nie sú momentálne k dispozícii žiadne aktualizácie"
Updater.FailedToLaunch="Nepodarilo sa spustiť aktualizátor"
Updater.GameCaptureActive.Title="Zachytenie hry aktívne"
+Updater.GameCaptureActive.Text="Knižnica pre záznam hier je stále aktívna. Ukončite, prosím, všetky snímané hry/programy (alebo reštartujte Windows) a skúste to znovu."
+QuickTransitions.SwapScenes="Prehodiť scény po prechode"
+QuickTransitions.SwapScenesTT="Prehodí scény pre náhľad a výstup po prechode (ak pôvodná scéna výstupu stále existuje).\nToto nevráti žiadne zmeny vykonané v pôvodnej scéne pre výstup."
QuickTransitions.DuplicateScene="Duplikovať scénu"
+QuickTransitions.DuplicateSceneTT="Pri úprave rovnakej scény umožňuje úpravu pozície/viditeľnosti zdrojov bez úpravy výstupu.\nPre úpravu vlastností zdrojov bez úpravy výstupu povoľte možnosť \"Duplikovať zdroje\".\nZmena tejto hodnoty spôsobí vyresetovanie súčasnej scény výstupu (ak stále existuje)."
QuickTransitions.EditProperties="Duplikovať zdroje"
+QuickTransitions.EditPropertiesTT="Pri úprave rovnakej scény umožňuje nastavenie zdrojov bez úpravy výstupu.\nTáto funkcia môže byť použitá, len ak je zapnutá možnosť \"Duplikovať scénu\".\nNiektoré zdroje (napr. zdroje záznamu či mediálne zdroje) túto funkciu nepodporujú a nemôžu byť upravované samostatne.\nPo zmene tejto hodnoty bude súčasná scéna výstupu vyresetovaná (ak stále existuje).\n\nVarovanie: Z dôvodu duplikácie zdrojov môže táto funkcia vyžadovať viac systémových prostriedkov."
QuickTransitions.HotkeyName="Rýchly prechod: %1"
+Basic.AddTransition="Pridať nastaviteľný prechod"
+Basic.RemoveTransition="Odstrániť nastaviteľný prechod"
Basic.TransitionProperties="Vlastnosti prechodu"
Basic.SceneTransitions="Prechody medzi scénami"
Basic.TransitionDuration="Trvanie"
@@ -193,56 +216,85 @@ ConfirmRemove.TextMultiple="Naozaj si prajete odstrániť %1 položiek?"
Output.StartStreamFailed="Nepodarilo sa spustiť streamovanie"
Output.StartRecordingFailed="Nepodarilo sa spustiť nahrávanie"
Output.StartReplayFailed="Nepodarilo sa spustiť medzipamäť znovunahrávania"
+Output.StartFailedGeneric="Nastala chyba pri spúšťaní nahrávania. Podrobnosti nájdete v textovom logu.\n\nPoznámka: Ak používate NVENC alebo AMD enkodér, uistite sa, že používate najnovšiu verziu grafického ovládača."
Output.ConnectFail.Title="Spojenie sa nepodarilo"
Output.ConnectFail.BadPath="Neplatná cesta alebo URL. Prosím, skontrolujte, či sú vaše nastavenia správne."
Output.ConnectFail.ConnectFailed="Spojenie so serverom sa nepodarilo"
+Output.ConnectFail.InvalidStream="K nastavenému kanálu alebo kľúču sa nedá pristupovať. Skontrolujte, prosím, či je váš vysielací kľúč správny. Ak áno, mohol nastať problém s pripojením k serveru."
+Output.ConnectFail.Error="Pri pokuse o pripojenie k serveru nastala neočakávaná chyba. Ďalšie informácie nájdete v textovom logu."
Output.ConnectFail.Disconnected="Odpojený od servera."
Output.RecordFail.Title="Nepodarilo sa spustiť nahrávanie"
+Output.RecordFail.Unsupported="Výstupný formát nie je podporovaný alebo nepodporuje viac ako jednu zvukovú stopu. Skontrolujte nastavenia a skúste to znovu."
Output.RecordNoSpace.Title="Nedostatok miesta na disku"
+Output.RecordNoSpace.Msg="Pre pokračovanie nahrávania nie je dostatok miesta na disku."
Output.RecordError.Title="Chyba nahrávania"
+Output.RecordError.Msg="Pri nahrávaní došlo k nešpecifikovanej chybe."
Output.ReplayBuffer.NoHotkey.Title="Nepriradená žiadna klávesová skratka!"
+Output.ReplayBuffer.NoHotkey.Msg="Nie je nastavená žiadna klávesová skratka pre uloženie záznamu. Nastavte ju, prosím, aby ste mohli ukladať záznam."
Output.BadPath.Title="Nesprávna cesta k súboru"
+Output.BadPath.Text="Nastavená cesta k výstupnému súboru je chybná. Prosím, skontrolujte správnosť nastavenej cesty."
LogReturnDialog="Nahranie logu bolo úspešné"
LogReturnDialog.CopyURL="Kopírovať URL"
LogReturnDialog.ErrorUploadingLog="Chyba pri nahrávaní súboru denníka chýb"
LicenseAgreement="Licenčná zmluva"
+LicenseAgreement.PleaseReview="Pred použitím OBS si, prosím, prečítajte licenčné podmienky. Používaním tohto programu potvrdzujete, že ste si prečítali a súhlasíte s podmienkami GNU General Public License v2.0. Pre zobrazenie zvyšku licencie prejdite nižšie."
+LicenseAgreement.ClickIAgreeToContinue="Ak súhlasíte s podmienkami licencie, kliknite na Súhlasím. Pred začatím používania OBS musíte súhlasiť s ujednaním."
LicenseAgreement.IAgree="Súhlasím"
LicenseAgreement.Exit="Ukončiť"
Remux.SourceFile="OBS nahrávka"
Remux.TargetFile="Cieľový súbor"
+Remux.Remux="Previesť"
Remux.OBSRecording="OBS nahrávanie"
+Remux.FinishedTitle="Prevod dokončený"
+Remux.Finished="Nahrávka prevedená"
+Remux.FinishedError="Nahrávka prevedená, ale súbor nemusí byť kompletný"
Remux.SelectRecording="Vybrať OBS nahrávku …"
Remux.SelectTarget="Vyberte cieľový súbor …"
Remux.FileExistsTitle="Cieľový súbor existuje"
Remux.FileExists="Cieľový súbor už existuje, chcete ho nahradiť?"
+Remux.ExitUnfinishedTitle="Prebieha prevod"
+Remux.ExitUnfinished="Prevod nie je dokončený. Zastavenie môže spôsobiť poškodenie výstupného súboru.\nNaozaj chcete prevod prerušiť?"
UpdateAvailable="Je dostupná aktualizácia"
UpdateAvailable.Text="Verzia %1.%2.%3 je dostupná. Kliknite sem na jej stiahnutie"
+Basic.DesktopDevice1="Zvuk plochy"
+Basic.DesktopDevice2="Zvuk plochy 2"
Basic.AuxDevice1="Mik/Aux"
Basic.AuxDevice2="Mik/Aux 2"
+Basic.AuxDevice3="Mikrofon / AUX 3"
+Basic.AuxDevice4="Mikrofon / AUX 4"
Basic.Scene="Scéna"
Basic.DisplayCapture="Zachytávanie monitora"
Basic.Main.PreviewConextMenu.Enable="Zapnúť náhľad"
+ScaleFiltering="Filter škálovania"
ScaleFiltering.Point="Bodové"
ScaleFiltering.Bilinear="Bilineárne"
ScaleFiltering.Bicubic="Bikubické"
ScaleFiltering.Lanczos="Lanczos"
Deinterlacing="Odstránenie prekladania"
+Deinterlacing.Discard="Zahodenie"
Deinterlacing.Retro="Retro"
+Deinterlacing.Blend="Prechod"
+Deinterlacing.Blend2x="Prechod 2x"
+Deinterlacing.Linear="Lineárny"
+Deinterlacing.Linear2x="Lineárny 2x"
Deinterlacing.Yadif="Yadif"
Deinterlacing.Yadif2x="Yadif 2x"
+Deinterlacing.TopFieldFirst="Najprv vrchný riadok"
+Deinterlacing.BottomFieldFirst="Najprv spodný riadok"
+VolControl.SliderUnmuted="Posuvník hlasitosti pre '%1': %2"
VolControl.SliderMuted="Ovládanie hlasitosti pre '%1': %2 (momentálne umlčané)"
VolControl.Mute="Umlčať '%1'"
VolControl.Properties="Vlastnosti pre '%1'"
@@ -252,12 +304,18 @@ Basic.Main.AddSceneDlg.Text="Prosím, zadajte názov scény"
Basic.Main.DefaultSceneName.Text="Scéna %1"
+Basic.Main.AddSceneCollection.Title="Pridať sadu scén"
+Basic.Main.AddSceneCollection.Text="Prosím, zadajte názov sady scén"
+Basic.Main.RenameSceneCollection.Title="Premenovať sadu scén"
AddProfile.Title="Pridanie profilu"
+AddProfile.Text="Prosím, zadajte názov profilu"
RenameProfile.Title="Premenovať profil"
+Basic.Main.MixerRename.Title="Premenovať zdroj zvuku"
+Basic.Main.MixerRename.Text="Prosím, zadajte názov zdroja zvuku"
Basic.Main.PreviewDisabled="Náhľad je momentálne vypnutý"
@@ -265,6 +323,7 @@ Basic.Main.PreviewDisabled="Náhľad je momentálne vypnutý"
Basic.SourceSelect="Vytvoriť/Vybrať zdroj"
Basic.SourceSelect.CreateNew="Vytvoriť nový"
Basic.SourceSelect.AddExisting="Pridať existujúci"
+Basic.SourceSelect.AddVisible="Zviditeľniť zdroj"
Basic.PropertiesWindow="Vlastnosti pre '%1'"
Basic.PropertiesWindow.AutoSelectFormat="%1 (Automatický výber: %2)"
@@ -279,8 +338,11 @@ Basic.PropertiesWindow.AddURL="Pridať cestu/URL"
Basic.PropertiesWindow.AddEditableListDir="Pridať adresár do '%1'"
Basic.PropertiesWindow.AddEditableListFiles="Pridať súbory do '%1'"
Basic.PropertiesWindow.AddEditableListEntry="Pridať položku do '%1'"
+Basic.PropertiesWindow.EditEditableListEntry="Upraviť položku z '%1'"
Basic.PropertiesView.FPS.Simple="Jednoduché hodnoty FPS"
+Basic.PropertiesView.FPS.Rational="Rozumné hodnoty FPS"
+Basic.PropertiesView.FPS.ValidFPSRanges="Platné rozsahy FPS:"
Basic.InteractionWindow="V interakcii s '%1'"
@@ -295,6 +357,7 @@ Basic.StatusBar.DelayStartingStoppingIn="Oneskorenie (zastavenie za %1 s, začí
Basic.Filters="Filtre"
Basic.Filters.AsyncFilters="Audio/Video filtre"
Basic.Filters.AudioFilters="Audio filtre"
+Basic.Filters.EffectFilters="Filtre efektov"
Basic.Filters.Title="Filtre pre '%1'"
Basic.Filters.AddFilter.Title="Názov filtra"
Basic.Filters.AddFilter.Text="Prosím, zadajte názov filtra"
@@ -335,16 +398,24 @@ Basic.Main.Sources="Zdroje"
Basic.Main.Controls="Ovládacie prvky"
Basic.Main.Connecting="Pripájanie..."
Basic.Main.StartRecording="Spustiť nahrávanie"
+Basic.Main.StartReplayBuffer="Spustiť Replay Buffer"
Basic.Main.StartStreaming="Spustiť stream"
Basic.Main.StopRecording="Ukončiť nahrávanie"
Basic.Main.StoppingRecording="Zastavenie nahrávania..."
+Basic.Main.StopReplayBuffer="Zastaviť Replay Buffer"
+Basic.Main.StoppingReplayBuffer="Zastavujem Replay Buffer..."
Basic.Main.StopStreaming="Ukončiť stream"
Basic.Main.StoppingStreaming="Zastavenie streamu..."
+Basic.Main.ForceStopStreaming="Zastaviť vysielanie (bez oneskorenia)"
+Basic.Main.Group="Skupina %1"
+Basic.Main.GroupItems="Zoskupiť vybrané položky"
+Basic.Main.Ungroup="Rozdeliť skupinu"
Basic.MainMenu.File="Súbor (&F)"
Basic.MainMenu.File.Export="&Exportovať"
Basic.MainMenu.File.Import="&Importovať"
Basic.MainMenu.File.ShowRecordings="Zobraziť nah&rávky"
+Basic.MainMenu.File.Remux="Previesť &nahrávky"
Basic.MainMenu.File.Settings="Na&stavenia"
Basic.MainMenu.File.ShowSettingsFolder="Zobraziť priečinok nastavení"
Basic.MainMenu.File.ShowProfileFolder="Zobraziť priečinok profilu"
@@ -357,6 +428,8 @@ Basic.MainMenu.Edit.Redo="Opakovať v&rátené"
Basic.MainMenu.Edit.UndoAction="Vrátiť $1 (&U)"
Basic.MainMenu.Edit.RedoAction="Opakovať $1 (&R)"
Basic.MainMenu.Edit.LockPreview="&Zámknúť náhľad"
+Basic.MainMenu.Edit.Scale="%Rozmer náhľadu"
+Basic.MainMenu.Edit.Scale.Window="Vtesnať do okna"
Basic.MainMenu.Edit.Scale.Canvas="Plátno (%1x%2)"
Basic.MainMenu.Edit.Scale.Output="Výstup (%1x%2)"
Basic.MainMenu.Edit.Transform="&Transformácia"
@@ -377,24 +450,42 @@ Basic.MainMenu.Edit.Order.MoveUp="Pos&unúť vyššie"
Basic.MainMenu.Edit.Order.MoveDown="Posunúť nižšie (&D)"
Basic.MainMenu.Edit.Order.MoveToTop="Premies&tniť navrch"
Basic.MainMenu.Edit.Order.MoveToBottom="Premiestniť naspodok (&B)"
+Basic.MainMenu.Edit.AdvAudio="Rozšírené vl&astnosti zvuku"
+Basic.MainMenu.View="Zobraziť (&V)"
Basic.MainMenu.View.Toolbars="&Panely s nástrojmi"
Basic.MainMenu.View.Docks="Doky"
Basic.MainMenu.View.Docks.ResetUI="Resetovať UI"
Basic.MainMenu.View.Docks.LockUI="Zamknúť UI"
+Basic.MainMenu.View.Toolbars.Listboxes="Zoznamy (&L)"
+Basic.MainMenu.View.SceneTransitions="Pre&chody scén"
+Basic.MainMenu.View.StatusBar="&Stavový riadok"
+Basic.MainMenu.View.Fullscreen.Interface="Na celú obrazovku"
+Basic.MainMenu.SceneCollection="&Sada scén"
Basic.MainMenu.Profile="&Profil"
Basic.MainMenu.Profile.Import="Importovať profil"
Basic.MainMenu.Profile.Export="Exportovať profil"
+Basic.MainMenu.SceneCollection.Import="Importovať sadu scén"
+Basic.MainMenu.SceneCollection.Export="Exportovať sadu scén"
Basic.MainMenu.Profile.Exists="Tento profil už existuje"
+Basic.MainMenu.SceneCollection.Exists="Táto sada scén už existuje"
Basic.MainMenu.Tools="Nás&troje"
Basic.MainMenu.Help="Pomoc (&H)"
+Basic.MainMenu.Help.HelpPortal="&Portál pomoci"
Basic.MainMenu.Help.Website="Navštíviť &webové stránky"
+Basic.MainMenu.Help.Discord="Pripojiť sa na &Discord server"
Basic.MainMenu.Help.Logs="&Log súbory"
Basic.MainMenu.Help.Logs.ShowLogs="Zobraziť log &súbory"
+Basic.MainMenu.Help.Logs.UploadCurrentLog="Nahrať súčasný súbor záznamu (&C)"
+Basic.MainMenu.Help.Logs.UploadLastLog="Nahrať posledný súbor záznamu (&L)"
+Basic.MainMenu.Help.Logs.ViewCurrentLog="Zobraziť aktuálny záznam (&V)"
Basic.MainMenu.Help.CheckForUpdates="Skontrolovať aktualizácie"
+Basic.MainMenu.Help.CrashLogs="&Hlásenia o zlyhaní"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="&Zobraziť správy o zlyhaniach"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="&Nahrať poslednú správu"
Basic.Settings.ProgramRestart="Tieto nastavenia sa prejavia až po reštarte programu."
Basic.Settings.ConfirmTitle="Potvrdenie zmien"
@@ -403,11 +494,23 @@ 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.EnableAutoUpdates="Automaticky kontrolovať aktualizácie pri štarte"
Basic.Settings.General.Projectors="Projektory"
+Basic.Settings.General.HideProjectorCursor="Skryť kurzor pred náhľadmi"
+Basic.Settings.General.ProjectorAlwaysOnTop="Náhľady vždy navrchu"
+Basic.Settings.General.Snapping="Prichytávanie zdrojov"
+Basic.Settings.General.ScreenSnapping="Prichytávať zdroje k okraju obrazovky"
+Basic.Settings.General.CenterSnapping="Prichytávať zdroje ku stredu vodorovnej a zvislej osi"
+Basic.Settings.General.SourceSnapping="Prichytávať zdroje k iným zdrojom"
+Basic.Settings.General.SnapDistance="Prichytávať citlivosť"
+Basic.Settings.General.RecordWhenStreaming="Automaticky nahrávať pri vysielaní"
+Basic.Settings.General.KeepRecordingWhenStreamStops="Nahrávať aj po ukončení vysielania"
Basic.Settings.General.SysTray="Systémová lišta"
Basic.Settings.General.SysTrayWhenStarted="Minimalizovať do systémovej lišty pri spustení"
Basic.Settings.General.SystemTrayHideMinimize="Vždy minimalizovať do systémovej lišty namiesto panela úloh"
Basic.Settings.General.SaveProjectors="Uložiť projektory pri skončení"
+Basic.Settings.General.Multiview.DrawSourceNames="Zobraziť názvy scén"
+Basic.Settings.General.Multiview.DrawSafeAreas="Vykresliť bezpečné zóny (EBU R 95)"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Typ streamu"
@@ -422,32 +525,48 @@ Basic.Settings.Output.Mode="Režim výstupu"
Basic.Settings.Output.Mode.Simple="Jednoduchý"
Basic.Settings.Output.Mode.Adv="Rozšírené"
Basic.Settings.Output.Mode.FFmpeg="Výstup FFmpeg"
+Basic.Settings.Output.ReplayBuffer.Estimate="Odhadované využitie pamäte: %1 MB"
Basic.Settings.Output.ReplayBuffer.Suffix="Prípona"
+Basic.Settings.Output.Simple.SavePath="Nahrávacia cesta"
Basic.Settings.Output.Simple.RecordingQuality="Kvalita nahrávania"
Basic.Settings.Output.Simple.RecordingQuality.Stream="Rovnaká ako pre stream"
Basic.Settings.Output.Simple.RecordingQuality.Small="Vysoká kvalita, stredná veľkosť súboru"
Basic.Settings.Output.Simple.RecordingQuality.Lossless="Bezstratová kvalita, ohromne veľké súbory"
-Basic.Settings.Output.Simple.Encoder.Software="Softvér (x264)"
-Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardvér (QSV)"
-Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardvér (AMD)"
+Basic.Settings.Output.Simple.Warn.VideoBitrate="Upozornenie: Dátový tok vysielaného obrazu bude nastavený na %1, čo je horná hranica pre súčasnú vysielaciu službu. Ak ste si istý, že chcete vysielať nad %1, v rozšírených nastaveniach enkodéra zakážte \"Vynútiť limit dátového toku vysielacou službou\"."
+Basic.Settings.Output.Simple.Warn.AudioBitrate="Upozornenie: Dátový tok vysielaného zvuku bude nastavený na %1, čo je horná hranica pre súčasnú vysielaciu službu. Ak ste si istý, že chcete vysielať nad %1, v rozšírených nastaveniach enkodéra zakážte \"Vynútiť limit dátového toku vysielacou službou\"."
+Basic.Settings.Output.Simple.Warn.Encoder="Upozornenie: Nahrávanie softvérovým enkodérom s rozdielnou kvalitou než vysielanie spôsobí zvýšenú záťaž CPU pri nahrávaní a vysielaní zároveň."
+Basic.Settings.Output.Simple.Warn.MultipleQSV="Upozornenie: Nemôžete použiť viacero QSV kodérov pri vysielaní a nahrávaní zároveň. Ak chcete vysielať a nahrávať zároveň, zmeňte, prosím, kodér vysielania alebo kodér nahrávania."
+Basic.Settings.Output.Simple.Encoder.Software="Softvérový (x264)"
+Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardvérový (QSV)"
+Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardvérový (AMD)"
Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardvér (NVENC)"
-Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Softvér (x264 prednastavené nízke zaťaženie CPU, zvyšuje veľkosť súboru)"
+Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Softvérový (x264, nízke zaťaženie CPU, zvyšuje veľkosť súboru)"
Basic.Settings.Output.VideoBitrate="Bitrate videa"
Basic.Settings.Output.AudioBitrate="Bitrate zvuku"
Basic.Settings.Output.Reconnect="Automaticky znovupripájať"
Basic.Settings.Output.RetryDelay="Čas medzi pokusmi (sekundy)"
Basic.Settings.Output.MaxRetries="Maximálny počet pokusov"
Basic.Settings.Output.Advanced="Povoliť pokročilé nastavenia enkodéra"
+Basic.Settings.Output.EncoderPreset="Predvoľba enkodéra (vyššie = menej CPU)"
+Basic.Settings.Output.CustomEncoderSettings="Vlastné nastavenie enkodéra"
+Basic.Settings.Output.Adv.Rescale="Škálovať výstup"
+Basic.Settings.Output.Adv.AudioTrack="Zvuková stopa"
+Basic.Settings.Output.Adv.Streaming="Vysielanie"
+Basic.Settings.Output.Adv.ApplyServiceSettings="Vynútiť nastavenia kodéra vysielacou službou"
Basic.Settings.Output.Adv.Audio.Track1="Stopa 1"
Basic.Settings.Output.Adv.Audio.Track2="Stopa 2"
Basic.Settings.Output.Adv.Audio.Track3="Stopa 3"
Basic.Settings.Output.Adv.Audio.Track4="Stopa 4"
+Basic.Settings.Output.Adv.Audio.Track5="Stopa 5"
+Basic.Settings.Output.Adv.Audio.Track6="Stopa 6"
Basic.Settings.Output.Adv.Recording="Nahrávanie"
Basic.Settings.Output.Adv.Recording.Type="Typ"
Basic.Settings.Output.Adv.Recording.Type.Standard="Štandardný"
Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Vlastný výstup (FFmpeg)"
+Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Použiť kóder vysielania)"
+Basic.Settings.Output.Adv.Recording.Filename="Formát názvu súboru"
Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Prepísať, ak súbor existuje"
Basic.Settings.Output.Adv.FFmpeg.Type="Typ výstupu FFmpeg"
Basic.Settings.Output.Adv.FFmpeg.Type.URL="Výstup na URL"
@@ -455,18 +574,30 @@ Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Výstup do súboru"
Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="Bežné formáty nahrávania"
Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Všetky súbory"
Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Cesta k súboru alebo URL"
+Basic.Settings.Output.Adv.FFmpeg.Format="Formát kontajneru"
Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Zvuk"
Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Video"
Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Predvolený formát"
+Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Popis formátu"
+Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Kodér zvuku/obrazu uhádnutý z cesty súboru alebo URL adresy"
Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Predvolený enkodér"
Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Vypnúť enkodér"
-Basic.Settings.Output.Adv.FFmpeg.VEncoder="Video Enkodér"
-Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audio Enkodér"
+Basic.Settings.Output.Adv.FFmpeg.VEncoder="Kodér obrazu"
+Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Nastavenie kodéra obrazu (ak existuje)"
+Basic.Settings.Output.Adv.FFmpeg.AEncoder="Kodér zvuku"
+Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Nastavenie kodéra zvuku (ak existuje)"
+Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Nastavenie zmiešavača (ak existuje)"
+Basic.Settings.Output.Adv.FFmpeg.GOPSize="Interval kľúčových snímkov (snímky)"
+Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Zobraziť všetky kodeky (aj potenciálne nekompatibilné)"
+FilenameFormatting.TT="%CCYY Rok, 4 číslice\n%YY Rok, posledné dve cifry (00-99)\n%MM Mesiac, číslo (01-12)\n%DD Deň v mesiaci, s úvodnou nulou (01-31)\n%hh Hodina vo 24-hod. formáte (00-23)\n%mm Minúta (00-59)\n%ss Sekunda (00-61)\n%% Znak %\n%a Skrátený názov dňa v týždni\n%A Celý názov dňa v týždni\n%b Skrátený názov mesiaca\n%B Celý názov mesiaca\n%d Deň v mesiaci, s úvodnou nulou (01-31)\n%H Hodina v 24-hod. formáte (00-23)\n%I Hodina v 12-hod. formáte (01-12)\n%m Mesiac ako desiatkové číslo (01-12)\n%M Minúta (00-59)\n%p Časť dňa (AM/PM)\n%S Sekunda (00-61)\n%y Rok, posledné dve cifry (00-99)\n%Y Rok\n%z ISO 8601 časový posun od UTC\n%Z Názov alebo skratka časového pásma\n"
Basic.Settings.Video="Video"
Basic.Settings.Video.Adapter="Video adaptér"
+Basic.Settings.Video.BaseResolution="Základné rozlíšenie (plátno)"
+Basic.Settings.Video.ScaledResolution="Výstupné (škálované) rozlíšenie"
+Basic.Settings.Video.DownscaleFilter="Zmenšovací filter"
Basic.Settings.Video.DisableAeroWindows="Vypnúť Aero (len Windows)"
Basic.Settings.Video.FPS="FPS"
Basic.Settings.Video.FPSCommon="Bežné hodnoty FPS"
@@ -478,31 +609,60 @@ Basic.Settings.Video.InvalidResolution="Neplatné rozlíšenie. Správne je [š
Basic.Settings.Video.CurrentlyActive="Výstup videa je práve aktívny. Prosím, vypnite všetky výstupy na zmenu nastavení videa."
Basic.Settings.Video.DisableAero="Vypnúť Aero"
+Basic.Settings.Video.DownscaleFilter.Bilinear="Bilineárne (Najrýchlejšie, ale rozmazané pri škálovaní)"
+Basic.Settings.Video.DownscaleFilter.Bicubic="Bikubické (ostrejšie pri škálovaní, 16 vzoriek)"
+Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczosov (ostrejšie pri škálovaní, 32 vzoriek)"
Basic.Settings.Audio="Zvuk"
Basic.Settings.Audio.SampleRate="Vzorkovacia frekvencia"
Basic.Settings.Audio.Channels="Kanály"
+Basic.Settings.Audio.MeterDecayRate.Fast="Rýchlo"
+Basic.Settings.Audio.MeterDecayRate.Medium="Stredne (typ I PPM)"
+Basic.Settings.Audio.MeterDecayRate.Slow="Pomaly (typ II PPM)"
+Basic.Settings.Audio.PeakMeterType="Typ merača špičiek"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Špička vzorky"
+Basic.Settings.Audio.MultichannelWarning.Title="Povoliť priestorový zvuk?"
+Basic.Settings.Audio.MultichannelWarning.Confirm="Naozaj si želáte povoliť priestorový zvuk?"
+Basic.Settings.Audio.EnablePushToMute="Povoliť push-to-mute"
+Basic.Settings.Audio.PushToMuteDelay="Oneskorenie push-to-mute"
+Basic.Settings.Audio.EnablePushToTalk="Povoliť push-to-talk"
+Basic.Settings.Audio.PushToTalkDelay="Oneskorenie push-to-talk"
+Basic.Settings.Audio.UnknownAudioDevice="[Zariadenie nie je pripojené alebo dostupné]"
+Basic.Settings.Advanced="Pokročilé"
+Basic.Settings.Advanced.General.ProcessPriority="Priorita procesu"
Basic.Settings.Advanced.General.ProcessPriority.High="Vysoká"
Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Zvýšená"
Basic.Settings.Advanced.General.ProcessPriority.Normal="Normálna"
Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Znížená"
Basic.Settings.Advanced.General.ProcessPriority.Idle="Nízka"
+Basic.Settings.Advanced.FormatWarning="Upozornenie: Formáty farieb iné ako NV12 sú primárne určené na nahrávanie a nie sú odporúčane pre vysielanie. Použitie iných formátov môže spôsobiť zvýšené využitie CPU pri vysielaní kvôli prevodu medzi formátmi."
+Basic.Settings.Advanced.Audio.BufferingTime="Čas medzipamäte zvuku"
+Basic.Settings.Advanced.Video.ColorFormat="Formát farieb"
Basic.Settings.Advanced.Video.ColorSpace="Farebný priestor YUV"
Basic.Settings.Advanced.Video.ColorRange="Rozsah farieb YUV"
Basic.Settings.Advanced.Video.ColorRange.Partial="Čiastočný"
Basic.Settings.Advanced.Video.ColorRange.Full="Úplný"
Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Predvolené"
+Basic.Settings.Advanced.StreamDelay="Oneskorenie vysielania"
+Basic.Settings.Advanced.StreamDelay.Duration="Trvanie (sekundy)"
+Basic.Settings.Advanced.StreamDelay.MemoryUsage="Odhadované využitie pamäte: %1 MB"
+Basic.Settings.Advanced.Network="Sieť"
Basic.Settings.Advanced.Network.BindToIP="Zviazať s IP adresou"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Použiť nový sieťový kód"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Režim nízkej odozvy"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Zakázať klávesové skratky, keď je hlavné okno aktívne"
+Basic.AdvAudio="Pokročilé nastavenia zvuku"
Basic.AdvAudio.Name="Názov"
Basic.AdvAudio.Volume="Hlasitosť (%)"
Basic.AdvAudio.Mono="Previesť na Mono"
+Basic.AdvAudio.AudioTracks="Stopy"
Basic.Settings.Hotkeys="Klávesové skratky"
+Basic.Settings.Hotkeys.Pair="Klávesové skratky pre \"%1\" slúžia ako prepínače"
+Basic.Hotkeys.SelectScene="Prepnúť na scénu"
Basic.SystemTray.Show="Ukázať"
Basic.SystemTray.Hide="Skryť"
@@ -531,9 +691,28 @@ Hotkeys.Super="Super"
Hotkeys.Menu="Menu"
Hotkeys.Space="Medzerník"
Hotkeys.NumpadNum="Numerická klávesa %1"
+Hotkeys.AppleKeypadNum="%1 (num. klávesnica)"
+Hotkeys.AppleKeypadMultiply="* (num. klávesnica)"
+Hotkeys.AppleKeypadDivide="/ (num. klávesnica)"
+Hotkeys.AppleKeypadAdd="+ (num. klávesnica)"
+Hotkeys.AppleKeypadSubtract="- (num. klávesnica)"
+Hotkeys.AppleKeypadDecimal=". (num. klávesnica)"
+Hotkeys.AppleKeypadEqual="= (num. klávesnica)"
+Hotkeys.MouseButton="Tlačidlo myši %1"
+Mute="Stlmiť"
+Unmute="Zrušiť stlmenie"
FinalScene.Title="Odstrániť scénu"
+FinalScene.Text="Musí existovať aspoň jedna scéna."
+
+NoSources.Title="Žiadne zdroje"
+NoSources.Text="Nepridali ste žiadne zdroje obrazu, takže výstup bude prázdny. Naozaj chcete pokračovať?"
+NoSources.Text.AddSource="Zdroje môžete kedykoľvek pridať kliknutím na ikonu + v zozname zdrojov v hlavnom okne."
+
+ChangeBG="Nastaviť farbu"
+CustomColor="Vlastná farba"
+
diff --git a/UI/data/locale/sl-SI.ini b/UI/data/locale/sl-SI.ini
index ea3a3bd..d749b2c 100644
--- a/UI/data/locale/sl-SI.ini
+++ b/UI/data/locale/sl-SI.ini
@@ -329,3 +329,6 @@ Basic.Settings.Advanced.Video.ColorRange.Full="Celotno"
+
+
+
diff --git a/UI/data/locale/sq-AL.ini b/UI/data/locale/sq-AL.ini
index 8552391..7a88840 100644
--- a/UI/data/locale/sq-AL.ini
+++ b/UI/data/locale/sq-AL.ini
@@ -164,6 +164,9 @@ BandwidthTest.Region.Other="Të tjera"
+
+
+
diff --git a/UI/data/locale/sr-CS.ini b/UI/data/locale/sr-CS.ini
index d7fecd4..44ea89e 100644
--- a/UI/data/locale/sr-CS.ini
+++ b/UI/data/locale/sr-CS.ini
@@ -562,3 +562,6 @@ OutputWarnings.NoTracksSelected="Morate odabrati makar jednu traku"
OutputWarnings.MultiTrackRecording="Upozorenje: Određeni formati (kao što je FLV) ne podržavaju više traka po snimku"
+
+
+
diff --git a/UI/data/locale/sr-SP.ini b/UI/data/locale/sr-SP.ini
index 45c7242..49b2f82 100644
--- a/UI/data/locale/sr-SP.ini
+++ b/UI/data/locale/sr-SP.ini
@@ -562,3 +562,6 @@ OutputWarnings.NoTracksSelected="Морате одабрати макар јед
OutputWarnings.MultiTrackRecording="Упозорење: Одређени формати (као што је FLV) не подржавају више трака по снимку"
+
+
+
diff --git a/UI/data/locale/sv-SE.ini b/UI/data/locale/sv-SE.ini
index 01e34e9..b1b065b 100644
--- a/UI/data/locale/sv-SE.ini
+++ b/UI/data/locale/sv-SE.ini
@@ -78,6 +78,8 @@ None="Ingen"
StudioMode.Preview="Förhandsvisning"
StudioMode.Program="Program"
ShowInMultiview="Visa i multivy"
+VerticalLayout="Vertikal layout"
+Group="Grupp"
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."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Stoppar reprisbuffert..."
Basic.Main.StopStreaming="Sluta strömma"
Basic.Main.StoppingStreaming="Stoppar ström..."
Basic.Main.ForceStopStreaming="Sluta strömma (ignorera fördröjning)"
+Basic.Main.Group="Grupp %1"
+Basic.Main.GroupItems="Gruppmarkerade föremål"
+Basic.Main.Ungroup="Avgruppera"
Basic.MainMenu.File="&Arkiv"
Basic.MainMenu.File.Export="&Exportera"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Verktyg"
Basic.MainMenu.Help="&Hjälp"
Basic.MainMenu.Help.HelpPortal="Hjälp&portal"
Basic.MainMenu.Help.Website="Besök &webbplats"
+Basic.MainMenu.Help.Discord="Anslut till &Discord-servern"
Basic.MainMenu.Help.Logs="&Loggfiler"
Basic.MainMenu.Help.Logs.ShowLogs="&Visa loggfiler"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Ladda upp &aktuell loggfil"
Basic.MainMenu.Help.Logs.UploadLastLog="Ladda upp &senaste loggfil"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Visa Aktuell Logg"
Basic.MainMenu.Help.CheckForUpdates="Sök efter uppdateringar"
+Basic.MainMenu.Help.CrashLogs="Krasch&rapporter"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="&Visa kraschrapporter"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Ladda upp &senaste kraschrapport"
Basic.Settings.ProgramRestart="Du måste starta om programmet för att ändringarna ska träda i kraft."
Basic.Settings.ConfirmTitle="Bekräfta ändringar"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Minimera alltid till meddelandef
Basic.Settings.General.SaveProjectors="Spara projektorer vid avslut"
Basic.Settings.General.SwitchOnDoubleClick="Övergång till scen vid dubbelklick"
Basic.Settings.General.StudioPortraitLayout="Aktivera porträtt-/vertikalt utseende"
+Basic.Settings.General.Multiview="Multivy"
+Basic.Settings.General.Multiview.MouseSwitch="Klicka för att byta mellan scener"
+Basic.Settings.General.Multiview.DrawSourceNames="Visa scennamn"
+Basic.Settings.General.Multiview.DrawSafeAreas="Rita ut säkra områden (EBU R 95)"
Basic.Settings.General.MultiviewLayout="Utseende för multivy"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horisontal, överkant"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horisontal, nederkant"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertikal, vänster"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertikal, höger"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horisontal, överkant (8 scener)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horisontal, nederkant (8 scener)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertikal, vänsterkant (8 scener)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertikal, högerkant (8 scener)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horisontal, överkant (24 scener)"
Basic.Settings.Stream="Ström"
Basic.Settings.Stream.StreamType="Strömtyp"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Ljudmätarens förfallfrekvens"
Basic.Settings.Audio.MeterDecayRate.Fast="Snabb"
Basic.Settings.Audio.MeterDecayRate.Medium="Medium (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Långsam (Type II PPM)"
+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. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast är några exempel på tjänster som har fullt stöd. Fastän Facebook Live och YouTube Live stöder inmatning för surroundljud mixar Facebook Live ned till stereo och YouTube Live spelar upp i bara två kanaler.\n\nLjudfiltren i OBS är kompatibla med surroundljud, fast stöd för VST-insticksmodulen garanteras inte."
Basic.Settings.Audio.MultichannelWarning.Title="Aktivera surroundljud?"
@@ -661,7 +678,7 @@ Basic.Settings.Advanced.FormatWarning="Varning: Andra färgformat än NV12 är a
Basic.Settings.Advanced.Audio.BufferingTime="Ljudbuffringstid"
Basic.Settings.Advanced.Video.ColorFormat="Färgformat"
Basic.Settings.Advanced.Video.ColorSpace="YUV-färgrymd"
-Basic.Settings.Advanced.Video.ColorRange="YUV färgområde"
+Basic.Settings.Advanced.Video.ColorRange="YUV-färgområde"
Basic.Settings.Advanced.Video.ColorRange.Partial="Partiell"
Basic.Settings.Advanced.Video.ColorRange.Full="Full"
Basic.Settings.Advanced.Audio.MonitoringDevice="Ljuduppspelningsenhet"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Nätverk"
Basic.Settings.Advanced.Network.BindToIP="Bind till IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Aktivera ny nätverkskod"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Låg latens-läge"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Inaktivera kortkommandon när fokus ligger i huvudfönstret"
Basic.AdvAudio="Avancerade ljudinställningar"
Basic.AdvAudio.Name="Namn"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Varning: Inspelningar som sparas som MP4 kommer int
FinalScene.Title="Radera scen"
FinalScene.Text="Det måste finnas minst en scen."
+NoSources.Title="Inga källor"
+NoSources.Text="Det verkar som om du inte har lagt till några videokällor än, så du kommer endast att visa en tom skärm. Är du säker på att du vill göra detta?"
+NoSources.Text.AddSource="Du kan lägga till källor genom att klicka på plusikonen under rutan \"källor\" i huvudfönstret när som helst."
+
+ChangeBG="Ändra färg"
+CustomColor="Anpassad färg"
+
+BrowserSource.EnableHardwareAcceleration="Aktiverar webbläsarkällans hårdvaruaccelerering"
+
diff --git a/UI/data/locale/ta-IN.ini b/UI/data/locale/ta-IN.ini
index a93f85a..b419738 100644
--- a/UI/data/locale/ta-IN.ini
+++ b/UI/data/locale/ta-IN.ini
@@ -100,6 +100,9 @@ New="புதிய"
+
+
+
diff --git a/UI/data/locale/th-TH.ini b/UI/data/locale/th-TH.ini
index cbbb8e6..7ab922a 100644
--- a/UI/data/locale/th-TH.ini
+++ b/UI/data/locale/th-TH.ini
@@ -148,3 +148,6 @@ Basic.Settings.Audio="เสียง"
+
+
+
diff --git a/UI/data/locale/tl-PH.ini b/UI/data/locale/tl-PH.ini
index 73be893..c4d5628 100644
--- a/UI/data/locale/tl-PH.ini
+++ b/UI/data/locale/tl-PH.ini
@@ -508,10 +508,6 @@ Basic.Settings.General.SaveProjectors="I-save ang mga projector sa paglabas"
Basic.Settings.General.SwitchOnDoubleClick="Lumipat sa eksena kapag dalawang beses pinindot"
Basic.Settings.General.StudioPortraitLayout="Paganahin ang portrait/patayong layout"
Basic.Settings.General.MultiviewLayout="Multiview Layout"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Pahalang, Tuktok"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Pahalang, Pinaka baba"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Patayo, Kaliwa"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Patayo, Kanan"
Basic.Settings.Stream="Mag-stream"
Basic.Settings.Stream.StreamType="Uri ng Pag-stream"
@@ -749,3 +745,6 @@ OutputWarnings.MP4Recording="Babala: Ang mga rekording na naka-save sa MP4 ay hi
FinalScene.Title="Burahin ang Eksena"
FinalScene.Text="Kailangan mayroon kahit isang eksena."
+
+
+
diff --git a/UI/data/locale/tr-TR.ini b/UI/data/locale/tr-TR.ini
index dfc1200..7aee010 100644
--- a/UI/data/locale/tr-TR.ini
+++ b/UI/data/locale/tr-TR.ini
@@ -78,6 +78,8 @@ None="Hiçbiri"
StudioMode.Preview="Önizleme"
StudioMode.Program="Program"
ShowInMultiview="Çoklu Ekranda Göster"
+VerticalLayout="Dikey Düzen"
+Group="Grup"
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."
@@ -209,7 +211,7 @@ ConfirmExit.Text="OBS şu anda etkin. Tüm yayınlar / kayıtlar kapatılacak.
ConfirmRemove.Title="Kaldırmayı Onayla"
ConfirmRemove.Text="'$1''i kaldırmak istediğinizden emin misiniz?"
-ConfirmRemove.TextMultiple="%1 öğeyi kaldırmak istediğinizden emin misiniz?"
+ConfirmRemove.TextMultiple="%1 ögeyi kaldırmak istediğinizden emin misiniz?"
Output.StartStreamFailed="Yayın başlatılamadı"
Output.StartRecordingFailed="Kayıt başlatılamadı"
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Tekrar Oynatma Arabelleği Durduruluyor..."
Basic.Main.StopStreaming="Yayını Durdur"
Basic.Main.StoppingStreaming="Canlı Yayın Durduruluyor..."
Basic.Main.ForceStopStreaming="Yayını Durdur (gecikmeyi yoksay)"
+Basic.Main.Group="Grup %1"
+Basic.Main.GroupItems="Seçilen Ögeleri Grupla"
+Basic.Main.Ungroup="Grubu Çöz"
Basic.MainMenu.File="&Dosya"
Basic.MainMenu.File.Export="Dışa Aktar"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="&Araçlar"
Basic.MainMenu.Help="&Yardım"
Basic.MainMenu.Help.HelpPortal="Yardım &Portalı"
Basic.MainMenu.Help.Website="&Siteyi Ziyaret Et"
+Basic.MainMenu.Help.Discord="&Discord Sunucusuna Katıl"
Basic.MainMenu.Help.Logs="&Günlük Dosyaları"
Basic.MainMenu.Help.Logs.ShowLogs="&Günlük Dosyalarını Göster"
Basic.MainMenu.Help.Logs.UploadCurrentLog="&Mevcut Günlük Dosyasını Karşıya Yükle"
Basic.MainMenu.Help.Logs.UploadLastLog="&Son Günlük Dosyasını Karşıya Yükle"
Basic.MainMenu.Help.Logs.ViewCurrentLog="&Şimdiki Günlüğü Göster"
Basic.MainMenu.Help.CheckForUpdates="Güncellemeleri Denetle"
+Basic.MainMenu.Help.CrashLogs="Çökme &Raporları"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="Çökme Raporlarını &Göster"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="&Son Çökme Raporunu Yükle"
Basic.Settings.ProgramRestart="Programın, bu ayarların etkinleşmesi için yeniden başlatılması gerekir."
Basic.Settings.ConfirmTitle="Değişiklikleri Onayla"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Her zaman görev çubuğu yerine
Basic.Settings.General.SaveProjectors="Çıkışta projektörleri kaydet"
Basic.Settings.General.SwitchOnDoubleClick="Çift tıklamada sahneye geçiş yap"
Basic.Settings.General.StudioPortraitLayout="Dikey düzeni etkinleştir"
+Basic.Settings.General.Multiview="Çoklu görüntü"
+Basic.Settings.General.Multiview.MouseSwitch="Sahneler arası geçiş için tıkla"
+Basic.Settings.General.Multiview.DrawSourceNames="Sahne adlarını göster"
+Basic.Settings.General.Multiview.DrawSafeAreas="Güvenli alanları (EBU R 95) çiz"
Basic.Settings.General.MultiviewLayout="Çoklu Görüntü Düzeni"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Yatay, Üst"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Yatay, Alt"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Dikey, Sol"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Dikey, Sağ"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Yatay, Üst (8 Sahne)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Yatay, Alt (8 Sahne)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Dikey, Sol (8 Sahne)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Dikey, Sağ (8 Sahne)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Yatay, Üst (24 Sahne)"
Basic.Settings.Stream="Yayın"
Basic.Settings.Stream.StreamType="Yayın Türü"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Ses Ölçer Sönüm Hızı"
Basic.Settings.Audio.MeterDecayRate.Fast="Hızlı"
Basic.Settings.Audio.MeterDecayRate.Medium="Orta (Tür I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="Yavaş (Tür II PPM)"
+Basic.Settings.Audio.PeakMeterType="Tepe Ölçer Türü"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="Örnek Tepe"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Gerçek Tepe (Daha yüksek CPU kullanımı)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="Uyarı: Surround ses etkin."
Basic.Settings.Audio.MultichannelWarning="Yayın yapılıyorsa, yayın hizmetinizin hem surround ses alınımını hem de surround ses geri oynatımını desteklediğinden emin olun. Twitch, Facebook 360 Live, Karıştırıcı RTMP, Smashcast, surround sesin tam desteklendiği örneklerdir. Facebook Live'ın ve YouTube Live'ın her ikisi de surround alınımını desteklese de, Facebook Live stereo'ya indirger, ve YouTube Live sadece iki kanal oynatır.\n\nOBS ses filtreleri surround sesle uyumludur, ancak VST eklenti desteği kesin değildir."
Basic.Settings.Audio.MultichannelWarning.Title="Surround ses etkinleştirilsin mi?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Ağ"
Basic.Settings.Advanced.Network.BindToIP="IP Bağla"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Yeni ağ kodunu etkinleştir"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Düşük gecike modu"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Ana pencere odaktayken kısayol tuşlarını devre dışı bırak"
Basic.AdvAudio="Gelişmiş Ses Özellikleri"
Basic.AdvAudio.Name="İsim"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Uyarı: MP4'e kaydedilen kayıtlar eğer dosya sonl
FinalScene.Title="Sahneyi Sil"
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."
+
+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/uk-UA.ini b/UI/data/locale/uk-UA.ini
index f45890b..57ff3fc 100644
--- a/UI/data/locale/uk-UA.ini
+++ b/UI/data/locale/uk-UA.ini
@@ -78,6 +78,8 @@ None="Немає"
StudioMode.Preview="вікно Перегляду"
StudioMode.Program="Програма наживо"
ShowInMultiview="Показувати у Мульти-перегляді"
+VerticalLayout="Вертикальне компонування"
+Group="Група"
AlreadyRunning.Title="OBS вже виконується"
AlreadyRunning.Text="OBS вже запущено! Тільки якщо ви дійсно не намагаєтесь цього зробити, будь ласка позакривайте всі відкриті OBS перед тим як запускати нову копію. Якщо OBS налаштовано згортатися в трей, перевірте чи не виконується він там й досі."
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="Буфер Повторів зупиняєть
Basic.Main.StopStreaming="Закінчити трансляцію"
Basic.Main.StoppingStreaming="Припинення трансляції..."
Basic.Main.ForceStopStreaming="Закінчити трансляцію (миттєво)"
+Basic.Main.Group="Група %1"
+Basic.Main.GroupItems="Згрупувати вибрані елементи"
+Basic.Main.Ungroup="Розгрупувати"
Basic.MainMenu.File="&Файл"
Basic.MainMenu.File.Export="&Експорт"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="Додаткові &засоби"
Basic.MainMenu.Help="&Довідка"
Basic.MainMenu.Help.HelpPortal="&Портал з допомоги"
Basic.MainMenu.Help.Website="Відвідати &сайт"
+Basic.MainMenu.Help.Discord="Приє&днатися до серверу Discord"
Basic.MainMenu.Help.Logs="&Файли журналів"
Basic.MainMenu.Help.Logs.ShowLogs="&Показати файли журналів"
Basic.MainMenu.Help.Logs.UploadCurrentLog="Завантажити на сервер По&точний журнал"
Basic.MainMenu.Help.Logs.UploadLastLog="Завантажити на сервер &Останній журнал"
Basic.MainMenu.Help.Logs.ViewCurrentLog="П&ереглянути поточний журнал"
Basic.MainMenu.Help.CheckForUpdates="Перевірити оновлення"
+Basic.MainMenu.Help.CrashLogs="Звіти про &збої"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="П&оказати звіти про збої"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="Заванта&жити на сервер останній звіт про збій"
Basic.Settings.ProgramRestart="Програма потребує перезапуску, щоб нові налаштування набрали сили."
Basic.Settings.ConfirmTitle="Підтвердження змін"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="Згортати в трей за
Basic.Settings.General.SaveProjectors="Зберегти налаштування режиму Проектор при виході"
Basic.Settings.General.SwitchOnDoubleClick="Відео-перехід до сцени за подвійним клацанням"
Basic.Settings.General.StudioPortraitLayout="Увімкнути портретне/вертикальне компонування"
+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.Horizontal.Top="Горизонтально, зверху"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Горизонтально, внизу"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Вертикально, ліворуч"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Вертикально, праворуч"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="Горизонтально, зверху (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Горизонтально, знизу (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="Вертикально, зліва (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="Вертикально, праворуч (8 сцен)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Горизонтально, зверху (24 сцени)"
Basic.Settings.Stream="Трансляція"
Basic.Settings.Stream.StreamType="Тип Трансляції"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="Швидкість спаду індика
Basic.Settings.Audio.MeterDecayRate.Fast="Швидко"
Basic.Settings.Audio.MeterDecayRate.Medium="Середньо (PPM типу I)"
Basic.Settings.Audio.MeterDecayRate.Slow="Повільно (PPM типу II)"
+Basic.Settings.Audio.PeakMeterType="Тип вимірювача пікових значень"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="З точністю до вибірки"
+Basic.Settings.Audio.PeakMeterType.TruePeak="Істинно-піковий (більше навантаження на ЦП)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="ПОПЕРЕДЖЕННЯ: Увімкнуто об'ємний звук."
Basic.Settings.Audio.MultichannelWarning="Якщо робите трансляції, перевірте чи підтримує ваш сервіс трансляцій об'ємний звук на вході та виході. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast - це приклади сервісів де об'ємний звук цілком підтримується. Навпаки, Facebook Live і YouTube Live обидва підтримують вхідний об'ємний звук, але Facebook Live мікшує його до стерео, а YouTube Live відтворює лише два канали.\n\nАудіо фільтри самої OBS сумісні з об'ємним звуком, хоча підтримку VST плагінами не гарантовано."
Basic.Settings.Audio.MultichannelWarning.Title="Увімкнути об'ємний звук для виводу аудіо?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="Мережа"
Basic.Settings.Advanced.Network.BindToIP="Прив'язати до адаптера (IP)"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="Увімкнути новий мережевий код"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="Режим з низькою затримкою"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Відключати гарячі клавіші, коли головне вікно знаходиться у фокусі"
Basic.AdvAudio="Розширені Налаштування Аудіо"
Basic.AdvAudio.Name="Назва"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="Попередження: Запис в MP4 мож
FinalScene.Title="Видалення сцени"
FinalScene.Text="Повинна бути принаймні одна сцена."
+NoSources.Title="Немає Джерел"
+NoSources.Text="Схоже, що ви не ще додали будь-якого відео Джерела. Таким чином на виводі буде лише порожній екран. Ви справді бажаєте це зробити?"
+NoSources.Text.AddSource="Ви можете будь-коли додати Джерела, натиснувши на значок (+) під панеллю Джерела в головному вікні."
+
+ChangeBG="Позначити кольором"
+CustomColor="Особливий колір"
+
+BrowserSource.EnableHardwareAcceleration="Задіяти апаратне прискорення для джерела 'Браузер'"
+
diff --git a/UI/data/locale/ur-PK.ini b/UI/data/locale/ur-PK.ini
new file mode 100644
index 0000000..d0afe8d
--- /dev/null
+++ b/UI/data/locale/ur-PK.ini
@@ -0,0 +1,136 @@
+
+Language="انگلش"
+Region="متحدہ ریاستوں"
+
+OK="ٹھیک ہے"
+Apply="لاگو کریں"
+Cancel="منسوخ کریں"
+Close="بند کریں"
+Save="بچاؤ"
+Discard="چھوڑ دو"
+Disable="غیر فعال"
+Yes="جی ہاں"
+No="نہیں"
+Add="جمع"
+Remove="ہٹا دیں"
+Rename="تبدیل کریں"
+Interact="بات چیت"
+Filters="فلٹرز"
+Properties="خصوصیات"
+MoveUp="اوپر جایے"
+MoveDown="نیچے جائے"
+Settings="ترتیبات"
+Display="ڈسپلے"
+Name="نام"
+Exit="بند کریں"
+Mixer="مکسر"
+Browse="براؤز کریں"
+Mono="مونو"
+Stereo="سٹیریو"
+DroppedFrames="کھوئے گئے فریم 1 فیصد(2 فیصد)"
+StudioProgramProjector="پورے اسکرین پروجیکٹر (پروگرام)"
+PreviewProjector="پورے اسکرین پروجیکٹر (پیش نظارہ)"
+SceneProjector="پورے اسکرین پروجیکٹر (منظر)"
+SourceProjector="پورے اسکرین پروجیکٹر (ماخذ)"
+StudioProgramWindow="ونڈوز پروجیکٹر (پروگرام)"
+PreviewWindow="ونڈوز پروجیکٹر (پیش نظارہ)"
+SceneWindow="ہوا ہوا پروجیکٹر (منظر)"
+SourceWindow="ونڈوز پروجیکٹر (ماخذ)"
+MultiviewProjector="Multiview (پورے اسکرین)"
+MultiviewWindowed="Multi view (دریچہ شدہ)"
+Clear="واضح"
+Revert="واپس"
+Show="دکھائیں"
+Hide="چھپائیں"
+UnhideAll="سب کو چھوڑ دو"
+Untitled="غیر خطاب یافتہ"
+New="نیا"
+Duplicate="دونا کرنا"
+Enable="قابل کریں"
+DisableOSXVSync="OSX V-Sync کو غیر فعال کریں"
+ResetOSXVSyncOnExit="باہر نکلیں پرOSX V-Sync ری سیٹ کریں"
+HighResourceUsage="انکوڈنگ زیادہ اوورلوڈ! ویڈیو ترتیبات کو تبدیل کرنے یا تیزی سے انکوڈنگ کے پیش سیٹ کا استعمال کرتے ہوئے غور کریں."
+Transition="منتقلی"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/data/locale/vi-VN.ini b/UI/data/locale/vi-VN.ini
index b4caec9..763874d 100644
--- a/UI/data/locale/vi-VN.ini
+++ b/UI/data/locale/vi-VN.ini
@@ -66,8 +66,10 @@ RemuxRecordings="Remux video"
Next="Tiếp tục"
Back="Quay lại"
Defaults="Mặc định"
+None="Không có"
StudioMode.Preview="Xem trước"
StudioMode.Program="Chương trình"
+Group="Nhóm"
AlreadyRunning.Title="OBS đã chạy"
AlreadyRunning.Text="OBS đã chạy rồi! Trừ khi bạn muốn làm điều này, xin vui lòng tắt mọi chương trình hiện tại của OBS trước khi cố gắng chạy một chương trình mới. Nếu bạn có OBS thiết lập để thu nhỏ trên khay hệ thống, xin vui lòng kiểm tra để xem nếu nó vẫn đang chạy hay không."
@@ -292,6 +294,8 @@ AddProfile.Text="Vui lòng nhập tên cấu hình"
RenameProfile.Title="Đổi tên cấu hình"
+Basic.Main.MixerRename.Title="Đổi tên nguồn âm thanh"
+Basic.Main.MixerRename.Text="Hãy nhập tên nguồn âm thanh"
Basic.Main.PreviewDisabled="Xem trước hiện đang vô hiệu hoá"
@@ -383,6 +387,7 @@ Basic.Main.StoppingReplayBuffer="Đang dừng Replay Buffer..."
Basic.Main.StopStreaming="Ngừng Stream"
Basic.Main.StoppingStreaming="Đang dừng stream..."
Basic.Main.ForceStopStreaming="Ngừng Stream (huỷ chậm trễ)"
+Basic.Main.Group="Nhóm %1"
Basic.MainMenu.File="&Tập tin"
Basic.MainMenu.File.Export="&Xuất"
@@ -472,10 +477,6 @@ Basic.Settings.General.SysTrayWhenStarted="Thu nhỏ về khay hệ thống khi
Basic.Settings.General.SystemTrayHideMinimize="Luôn luôn thu nhỏ về khay hệ thống thay vì thanh tác vụ"
Basic.Settings.General.StudioPortraitLayout="Bật bố cục theo chiều ngang/dọc"
Basic.Settings.General.MultiviewLayout="Giao diện nhiều lớp"
-Basic.Settings.General.MultiviewLayout.Horizontal.Top="Ngang, Trên"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Ngang, Dưới"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="Dọc, Trái"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="Dọc, Phải"
Basic.Settings.Stream="Stream"
Basic.Settings.Stream.StreamType="Kiểu Stream"
@@ -697,3 +698,8 @@ OutputWarnings.MP4Recording="Chú ý: các bản ghi âm được lưu ở dạn
FinalScene.Title="Xóa cảnh"
FinalScene.Text="Cần có ít nhất một cảnh."
+
+ChangeBG="Thiết lập màu sắc"
+CustomColor="Tùy chỉnh màu sắc"
+
+
diff --git a/UI/data/locale/zh-CN.ini b/UI/data/locale/zh-CN.ini
index 905bbc5..86fb246 100644
--- a/UI/data/locale/zh-CN.ini
+++ b/UI/data/locale/zh-CN.ini
@@ -28,11 +28,11 @@ Browse="浏览"
Mono="单声道"
Stereo="立体声"
DroppedFrames="丢帧 %1 (%2%)"
-StudioProgramProjector="全屏投影仪(程序)"
+StudioProgramProjector="全屏投影仪(输出)"
PreviewProjector="全屏投影仪(预览)"
SceneProjector="全屏投影仪 (现场)"
SourceProjector="全屏投影仪(源)"
-StudioProgramWindow="窗口式投影仪 (程序)"
+StudioProgramWindow="窗口式投影仪 (输出)"
PreviewWindow="窗口化投影仪 (预览)"
SceneWindow="窗口化投影仪 (场景)"
SourceWindow="窗口化投影仪 (源)"
@@ -49,7 +49,7 @@ Duplicate="复制(&D)"
Enable="启用"
DisableOSXVSync="禁用 OSX V-Sync"
ResetOSXVSyncOnExit="退出时重置 OSX V-Sync"
-HighResourceUsage="编码过载! 考虑下调低视频设置或使用更快的编码预设."
+HighResourceUsage="编码过载! 请考虑降低视频设置或使用更快的编码预设"
Transition="过渡动画"
QuickTransitions="快速过渡动画"
Left="左"
@@ -61,7 +61,7 @@ Hours="小时"
Minutes="分钟"
Seconds="秒"
Deprecated="不推荐使用"
-ReplayBuffer="重拨缓存"
+ReplayBuffer="回放缓存"
Import="导入"
Export="导出"
Copy="复制"
@@ -76,15 +76,17 @@ HideMixer="混合器中隐藏"
TransitionOverride="过渡覆盖模式"
None="无"
StudioMode.Preview="预览"
-StudioMode.Program="程序"
+StudioMode.Program="输出"
ShowInMultiview="多屏中显示"
+VerticalLayout="垂直布局"
+Group="分组"
AlreadyRunning.Title="OBS 已在运行"
AlreadyRunning.Text="OBS 已经在运行! 除非你想要这样做, 请在你运行一个新的 OBS 前, 关闭任何已经在运行的 OBS. 如果你有一个 OBS 设置最小化到系统托盘, 请检查他是否仍在运行."
-AlreadyRunning.LaunchAnyway="无论如何启动"
+AlreadyRunning.LaunchAnyway="始终启动"
-Copy.Filters="复制筛选器"
-Paste.Filters="粘贴筛选器"
+Copy.Filters="复制滤镜"
+Paste.Filters="粘贴滤镜"
BandwidthTest.Region="区域"
BandwidthTest.Region.US="美国"
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="正在停止回放缓存..."
Basic.Main.StopStreaming="停止推流"
Basic.Main.StoppingStreaming="停止推流..."
Basic.Main.ForceStopStreaming="停止流 (放弃延迟)"
+Basic.Main.Group="分组 %1"
+Basic.Main.GroupItems="对所选项目进行分组"
+Basic.Main.Ungroup="取消分组"
Basic.MainMenu.File="文件(&F)"
Basic.MainMenu.File.Export="导出(&E)"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="工具 (&T)"
Basic.MainMenu.Help="帮助 (&H)"
Basic.MainMenu.Help.HelpPortal="帮助门户"
Basic.MainMenu.Help.Website="访问OBS主页 (&W)"
+Basic.MainMenu.Help.Discord="加入 &Discord 服务器"
Basic.MainMenu.Help.Logs="日志文件 (&L)"
Basic.MainMenu.Help.Logs.ShowLogs="显示日志文件 (&S)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="上传当前日志文件 (&C)"
Basic.MainMenu.Help.Logs.UploadLastLog="上传最后一个日志文件 (&L)"
Basic.MainMenu.Help.Logs.ViewCurrentLog="查看当前日志 (&V)"
Basic.MainMenu.Help.CheckForUpdates="检查升级(&C)"
+Basic.MainMenu.Help.CrashLogs="错误报告(&R)"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="查看错误报告(&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="上传上一次错误报告(&L)"
Basic.Settings.ProgramRestart="要使这些设置生效,必须重新启动该程序。"
Basic.Settings.ConfirmTitle="确认更改"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="总是最小化到系统托盘,
Basic.Settings.General.SaveProjectors="退出时保存投影仪"
Basic.Settings.General.SwitchOnDoubleClick="双击时切换到场景"
Basic.Settings.General.StudioPortraitLayout="启用纵向布局"
+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.Horizontal.Top="水平、顶部"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="水平、底部"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="垂直, 左侧"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="垂直, 右侧"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="水平, 顶部(8 场景)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="水平, 底部(8 场景)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="垂直, 左侧(8 场景)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="垂直, 右侧(8 场景)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="水平, 顶部(24 场景)"
Basic.Settings.Stream="流"
Basic.Settings.Stream.StreamType="流类型"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="音频表衰减率"
Basic.Settings.Audio.MeterDecayRate.Fast="快速"
Basic.Settings.Audio.MeterDecayRate.Medium="中速(峰值电平表I型)"
Basic.Settings.Audio.MeterDecayRate.Slow="慢速(峰值电平表II型)"
+Basic.Settings.Audio.PeakMeterType="峰值计类型"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="采样峰值"
+Basic.Settings.Audio.PeakMeterType.TruePeak="真峰值 (更高的的 CPU 使用率)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="警告: 已启用环绕声音频。"
Basic.Settings.Audio.MultichannelWarning="如果串流, 请检查串流服务是否支持环绕立体声接收和环绕立体声播放。 Twitch, Facebook 360 Live, Mixer RTMP, Smashcast 是充分支持环绕立体声的例子。 虽然 Facebook Live 和 Youtube Live 都支持环绕立体声接收, 但是Facebook Live 降低混合至立体声, 而 Youtube Live 则只播放两个声道。\n\nOBS 音频过滤器与环绕立体声兼容, 但 VST 插件支持无法保证。"
Basic.Settings.Audio.MultichannelWarning.Title="是否启用环绕立体声?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="网络"
Basic.Settings.Advanced.Network.BindToIP="绑定 IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="启用新的网络代码"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="低延迟模式"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="当主窗口获得焦点时禁用热键"
Basic.AdvAudio="高级音频属性"
Basic.AdvAudio.Name="名称"
@@ -682,10 +700,10 @@ Basic.AdvAudio.Volume="音量 (%)"
Basic.AdvAudio.Mono="下降混合为单声道"
Basic.AdvAudio.Panning="平移"
Basic.AdvAudio.SyncOffset="同步偏移 (毫秒)"
-Basic.AdvAudio.Monitoring="音频监测"
+Basic.AdvAudio.Monitoring="音频监听"
Basic.AdvAudio.Monitoring.None="关闭监视"
Basic.AdvAudio.Monitoring.MonitorOnly="仅显示器(静音输出)"
-Basic.AdvAudio.Monitoring.Both="监视器和输出"
+Basic.AdvAudio.Monitoring.Both="监听并输出"
Basic.AdvAudio.AudioTracks="轨道"
Basic.Settings.Hotkeys="热键"
@@ -749,3 +767,12 @@ OutputWarnings.MP4Recording="警告︰ 录制保存到 MP4 将无法恢复,如
FinalScene.Title="删除场景"
FinalScene.Text="至少要有一个场景."
+NoSources.Title="无来源"
+NoSources.Text="看起来您未添加任何视频源,所以我们将只会输出黑色萤幕。确实要这样做吗?"
+NoSources.Text.AddSource="您可以通过单击主窗口中“来源”框下的“+”图标以添加来源。"
+
+ChangeBG="设置颜色"
+CustomColor="自定义颜色"
+
+BrowserSource.EnableHardwareAcceleration="启用浏览器源硬件加速"
+
diff --git a/UI/data/locale/zh-TW.ini b/UI/data/locale/zh-TW.ini
index 8af7699..bc2a215 100644
--- a/UI/data/locale/zh-TW.ini
+++ b/UI/data/locale/zh-TW.ini
@@ -78,6 +78,8 @@ None="無"
StudioMode.Preview="預覽"
StudioMode.Program="程式"
ShowInMultiview="在多視圖中顯示"
+VerticalLayout="垂直排版"
+Group="群組"
AlreadyRunning.Title="OBS 已在執行中"
AlreadyRunning.Text="OBS 已在執行中!除非這是您的意圖,請在執行新的 OBS 前關閉現存的 OBS 。如果有設定 OBS 最小化到系統工具列,請確認是否仍在該處執行。"
@@ -405,6 +407,9 @@ Basic.Main.StoppingReplayBuffer="正在停止重播緩衝..."
Basic.Main.StopStreaming="停止串流"
Basic.Main.StoppingStreaming="停止串流..."
Basic.Main.ForceStopStreaming="停止實況(丟棄延遲)"
+Basic.Main.Group="群組 %1"
+Basic.Main.GroupItems="群組選取的項目"
+Basic.Main.Ungroup="取消群組"
Basic.MainMenu.File="檔案 (&F)"
Basic.MainMenu.File.Export="匯出 (&E)"
@@ -471,12 +476,16 @@ Basic.MainMenu.Tools="工具(&T)"
Basic.MainMenu.Help="說明 (&H)"
Basic.MainMenu.Help.HelpPortal="幫助和門戶"
Basic.MainMenu.Help.Website="前往 OBS 網站 (&W)"
+Basic.MainMenu.Help.Discord="加入 &Discord 伺服器"
Basic.MainMenu.Help.Logs="Log 檔案 (&L)"
Basic.MainMenu.Help.Logs.ShowLogs="顯示 Log (&S)"
Basic.MainMenu.Help.Logs.UploadCurrentLog="上傳目前 Log 檔 (&C)"
Basic.MainMenu.Help.Logs.UploadLastLog="上傳上次的 Log 檔 (&L)"
Basic.MainMenu.Help.Logs.ViewCurrentLog="顯示當前紀錄檔 (&V)"
Basic.MainMenu.Help.CheckForUpdates="檢查更新"
+Basic.MainMenu.Help.CrashLogs="錯誤報告"
+Basic.MainMenu.Help.CrashLogs.ShowLogs="顯示當機回報(&S)"
+Basic.MainMenu.Help.CrashLogs.UploadLastLog="上傳最新的錯誤回報(&L)"
Basic.Settings.ProgramRestart="為了套用新設定,請關閉後重啟。"
Basic.Settings.ConfirmTitle="確認修改"
@@ -507,11 +516,16 @@ Basic.Settings.General.SystemTrayHideMinimize="總是最小化到系統列,而
Basic.Settings.General.SaveProjectors="退出時保存投影設定"
Basic.Settings.General.SwitchOnDoubleClick="按兩下時切換到場景"
Basic.Settings.General.StudioPortraitLayout="啟用縱向/垂直佈局"
+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.Horizontal.Top="水平, 上"
-Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="水平, 下"
-Basic.Settings.General.MultiviewLayout.Vertical.Left="垂直, 左"
-Basic.Settings.General.MultiviewLayout.Vertical.Right="垂直, 右"
+Basic.Settings.General.MultiviewLayout.Horizontal.Top="橫排、頂部(八個場景)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="橫排、底部(八個場景)"
+Basic.Settings.General.MultiviewLayout.Vertical.Left="直行、靠左(八個場景)"
+Basic.Settings.General.MultiviewLayout.Vertical.Right="直行、靠右(八個場景)"
+Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="橫排、頂部(24 個場景)"
Basic.Settings.Stream="串流"
Basic.Settings.Stream.StreamType="串流類型"
@@ -635,6 +649,9 @@ Basic.Settings.Audio.MeterDecayRate="音量計衰減速率"
Basic.Settings.Audio.MeterDecayRate.Fast="快速"
Basic.Settings.Audio.MeterDecayRate.Medium="中 (Type I PPM)"
Basic.Settings.Audio.MeterDecayRate.Slow="慢 (Type II PPM)"
+Basic.Settings.Audio.PeakMeterType="峰值表類型"
+Basic.Settings.Audio.PeakMeterType.SamplePeak="範例峰值"
+Basic.Settings.Audio.PeakMeterType.TruePeak="真實峰值(較高的 CPU 用量)"
Basic.Settings.Audio.MultiChannelWarning.Enabled="警告: 已啟用環繞聲音訊。"
Basic.Settings.Audio.MultichannelWarning="如果是流媒體,請檢查您的流媒體服務是否同時支持環繞聲攝取和環繞聲播放。 Twitch,Facebook 360 Live,Mixer RTMP,Smashcast都是完全支持環繞聲的例子。 儘管Facebook Live和YouTube Live都接受環繞聲攝取,但是Facebook Live會混音為立體聲,而YouTube Live只播放兩個聲道。\n\n 儘管不支持VST插件,但OBS音頻濾波器與環繞聲兼容。"
Basic.Settings.Audio.MultichannelWarning.Title="是否啟用環繞聲音訊?"
@@ -675,6 +692,7 @@ Basic.Settings.Advanced.Network="網路"
Basic.Settings.Advanced.Network.BindToIP="綁定到 IP"
Basic.Settings.Advanced.Network.EnableNewSocketLoop="啟用新的網路程式碼"
Basic.Settings.Advanced.Network.EnableLowLatencyMode="低延遲模式"
+Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="當主視窗處於焦點,則停用熱鍵"
Basic.AdvAudio="進階音訊屬性"
Basic.AdvAudio.Name="名稱"
@@ -749,3 +767,11 @@ OutputWarnings.MP4Recording="警告︰ 如果檔案無法完成,儲存成 MP4
FinalScene.Title="刪除場景"
FinalScene.Text="至少要有一個場景。"
+NoSources.Title="沒有來源"
+NoSources.Text="看起來您尚未增加任何視訊來源,所以我們只會輸出黑畫面。確定?"
+NoSources.Text.AddSource="您可以透過點一下主視窗「來源」框底下的 + 按鈕,隨時增加來源。"
+
+ChangeBG="設定顏色"
+CustomColor="自訂顏色"
+
+
diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss
index af3eb90..ca9637e 100644
--- a/UI/data/themes/Acri.qss
+++ b/UI/data/themes/Acri.qss
@@ -1,3 +1,37 @@
+/* OBSTheme, main QApplication palette and QML values */
+OBSTheme {
+ window: #181819;
+ windowText: rgb(225,224,225);
+ base: rgb(18,18,21);
+ alternateBase: rgb(0,0,0);
+ text: rgb(225,224,225);
+ button: #162458;
+ buttonText: rgb(225,224,225);
+ brightText: #484848;
+
+ light: #162458;
+ mid: #181819;
+ dark: rgb(18,18,21);
+ shadow: rgb(0,0,0);
+
+ highlight: #252458;
+ highlightText: #FFFFFF;
+
+ link: #605ee6;
+ linkVisited: #605ee6;
+}
+
+OBSTheme::disabled {
+ text: #484848;
+ buttonText: #484848;
+ brightText: #484848;
+}
+
+OBSTheme::inactive {
+ highlight: rgb(48,47,48);
+ highlightText: rgb(255, 255, 255);
+}
+
/* General style, we override only what is needed. */
QWidget {
background-color: #181819;
@@ -9,6 +43,7 @@ QWidget {
font-family: "Open Sans", "Tahoma", "Arial", sans-serif;
font-size: 12px;
padding: 4px;
+ overflow: auto;
}
#menubar {
@@ -45,6 +80,14 @@ QWidget::disabled {
color: #484848;
}
+* [themeID="error"] {
+ color: #d91740;
+}
+
+* [themeID="warning"] {
+ color: #d9af17;
+}
+
/* Dropdown menus, Scenes box, Sources box */
QAbstractItemView {
background-color: #181819;
@@ -94,7 +137,8 @@ QMenuBar::item:selected {
}
/* Listbox item */
-QListWidget::item {
+QListWidget::item,
+SourceTree::item {
padding: 4px 2px;
margin-bottom: 2px;
margin-top: 0px;
@@ -110,11 +154,6 @@ QListWidget QLineEdit {
border-radius: none;
}
-SourceListWidget::item {
- margin-bottom: 1px;
- padding: -4px 2px;
-}
-
/* Dock stuff */
QDockWidget {
background: transparent;
@@ -157,6 +196,23 @@ SourceListWidget {
border-bottom: 2px solid #2f2f2f;
}
+SourceTree {
+ border: none;
+ border-bottom: 1px solid #2f2f2f;
+}
+
+SourceTree QLabel {
+ padding: 2px 0px;
+ margin: -2px 4px -2px;
+}
+
+SourceTree QLineEdit {
+ background-color: #0c101e;
+ padding: 2px;
+ margin: -2px 6px -2px 3px;
+ font-size: 12px;
+}
+
#scenesFrame,
#sourcesFrame {
margin-left: -7px;
@@ -179,13 +235,15 @@ SourceListWidget {
}
/* Listbox item selected, unfocused */
-QListWidget::item:hover {
+QListWidget::item:hover,
+SourceTree::item:hover {
background-color: #212121;
border: 1px solid #333336;
}
/* Listbox item selected */
-QListWidget::item:selected {
+QListWidget::item:selected,
+SourceTree::item:selected {
background-color: #131a30;
border: 1px solid #252a45;
}
@@ -453,10 +511,12 @@ QPushButton {
QPushButton::flat {
background-color: rgb(24,24,25);
+ border: none;
}
QPushButton:checked {
- background-color: #202b52;
+ background-color: #581624;
+ border-color: #84162d;
}
QPushButton:hover {
@@ -468,6 +528,16 @@ QPushButton:pressed {
background-color: #161f41;
}
+QPushButton:disabled {
+ border: 1px solid #232426;
+ background-color: #1a1a1b;
+}
+
+QPushButton::flat:hover,
+QPushButton::flat:disabled {
+ border: none;
+}
+
/* Progress Bar */
QProgressBar {
@@ -505,16 +575,20 @@ QSlider::handle:horizontal {
}
QSlider::handle:horizontal:pressed {
- background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0,
+ background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 rgb(240,239,240),
stop: 0.25 rgb(200,199,200),
stop: 1 rgb(162,161,162));
}
+QSlider::sub-page:horizontal {
+ background-color: #2a3a75;
+}
+
QSlider::sub-page:horizontal:disabled {
- background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1,
- stop: 0 rgb(31,30,31),
- stop: 0.75 rgb(50, 49, 50));
+ background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0,
+ stop: 0 rgb(26,25,26),
+ stop: 0.75 rgb(10, 10, 10));
border-radius: 2px;
}
@@ -533,23 +607,27 @@ QSlider::handle:vertical {
stop: 0.25 rgb(200,199,200),
stop: 1 rgb(162,161,162));
border: 1px solid rgb(24,24,25);
- border-radius: 4px;
+ border-radius: 3px;
width: 10px;
height: 18px;
- margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */
+ 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: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
+ background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,
stop: 0 rgb(240,239,240),
stop: 0.25 rgb(200,199,200),
stop: 1 rgb(162,161,162));
}
-QSlider::sub-page:vertical:disabled {
+QSlider::add-page:vertical {
+ background-color: #2a3a75;
+}
+
+QSlider::add-page:vertical:disabled {
background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,
- stop: 0 rgb(31,30,31),
- stop: 0.75 rgb(50, 49, 50));
+ stop: 0 rgb(26,25,26),
+ stop: 0.75 rgb(10, 10, 10));
border-radius: 2px;
}
@@ -557,15 +635,10 @@ QSlider::handle:hover {
background-color: rgb(200,199,200);
}
-QSlider::sub-page {
- background-color: #2a3a75;
-}
-
QSlider::handle:disabled {
background-color: rgb(15,15,16);
}
-
/* Volume Control */
/* Old Meters */
@@ -724,6 +797,30 @@ OBSHotkeyLabel[hotkeyPairHover=true] {
}
+/* Group Collapse Checkbox */
+
+SourceTreeSubItemCheckBox {
+ background: transparent;
+ outline: none;
+ padding: 0px;
+}
+
+SourceTreeSubItemCheckBox::indicator {
+ width: 12px;
+ height: 12px;
+}
+
+SourceTreeSubItemCheckBox::indicator:checked,
+SourceTreeSubItemCheckBox::indicator:checked:hover {
+ image: url(./Dark/expand.png);
+}
+
+SourceTreeSubItemCheckBox::indicator:unchecked,
+SourceTreeSubItemCheckBox::indicator:unchecked:hover {
+ image: url(./Dark/collapse.png);
+}
+
+
/* Label warning/error */
QLabel#warningLabel {
@@ -750,21 +847,10 @@ OBSBasicProperties,
background: #101010;
}
-#OBSBasicSourceSelect #sourceList {
- border-bottom: 2px solid #333336;
-}
-
FocusList::item {
padding: 0px 2px;
}
-#transitionsContainer QPushButton,
-#mixerDock QPushButton,
-#effectWidget QPushButton,
-#asyncWidget QPushButton {
- border: none;
-}
-
#fpsTypes {
padding: 0px;
}
diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss
index 4a9326c..934706a 100644
--- a/UI/data/themes/Dark.qss
+++ b/UI/data/themes/Dark.qss
@@ -28,6 +28,44 @@
/* rgb(42,130,218); /* blue */
+/* 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(225,224,225); /* veryLight */
+ base: rgb(31,30,31); /* veryDark */
+ alternateBase: rgb(11,10,11); /* veryVeryDark */
+ text: rgb(225,224,225); /* veryLight */
+ button: rgb(88,87,88); /* kindaDark */
+ buttonText: rgb(225,224,225); /* 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 */
+ highlightText: rgb(0,0,0);
+
+ link: rgb(114, 162, 255); /* OBS blue */
+ linkVisited: rgb(114, 162, 255); /* OBS blue */
+}
+
+OBSTheme::disabled {
+ text: rgb(122,121,122); /* light */
+ buttonText: rgb(122,121,122); /* light */
+ brightText: rgb(122,121,122); /* light */
+}
+
+OBSTheme::inactive {
+ highlight: rgb(48,47,48);
+ highlightText: rgb(255, 255, 255);
+}
+
+
/* General style, we override only what is needed. */
QWidget {
background-color: rgb(58,57,58); /* dark */
@@ -423,9 +461,9 @@ QPushButton::menu-indicator {
/* Sliders */
QSlider::groove:horizontal {
- background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1,
- stop: 0 rgb(31,30,31), /* veryDark */
- stop: 0.75 rgb(50, 49, 50));
+ background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0,
+ stop: 0 rgb(50, 49, 50), /* dark */
+ stop: 0.75 rgb(88,87,88)); /* kindaDark */
height: 4px;
border: none;
border-radius: 2px;
@@ -450,32 +488,37 @@ QSlider::handle:horizontal:pressed {
stop: 1 rgb(162,161,162)); /* light */
}
+QSlider::sub-page:horizontal {
+ background-color: rgb(42,130,218); /* blue */
+ border-radius: 2px;
+}
+
QSlider::sub-page:horizontal:disabled {
- background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1,
+ background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0,
stop: 0 rgb(31,30,31), /* veryDark */
- stop: 0.75 rgb(50, 49, 50));
+ stop: 0.75 rgb(50, 49, 50)); /* dark */
border-radius: 2px;
}
QSlider::groove:vertical {
- background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,
- stop: 0 rgb(31,30,31), /* veryDark */
- stop: 0.75 rgb(50, 49, 50));
+ background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
+ stop: 0 rgb(50, 49, 50), /* dark */
+ stop: 0.75 rgb(88,87,88)); /* kindaDark */
width: 4px;
border: none;
border-radius: 2px;
}
QSlider::handle:vertical {
- background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,
+ background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
stop: 0 rgb(240,239,240), /* lighter */
stop: 0.25 rgb(200,199,200),
stop: 1 rgb(162,161,162)); /* light */
border: 1px solid rgb(58,57,58); /* dark */
- border-radius: 4px;
+ border-radius: 3px;
width: 10px;
height: 18px;
- margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */
+ margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */
}
QSlider::handle:vertical:pressed {
@@ -485,10 +528,15 @@ QSlider::handle:vertical:pressed {
stop: 1 rgb(162,161,162)); /* light */
}
-QSlider::sub-page:vertical:disabled {
- background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,
+QSlider::add-page:vertical {
+ background-color: rgb(42,130,218); /* blue */
+ border-radius: 2px;
+}
+
+QSlider::add-page:vertical:disabled {
+ background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
stop: 0 rgb(31,30,31), /* veryDark */
- stop: 0.75 rgb(50, 49, 50));
+ stop: 0.75 rgb(50, 49, 50)); /* dark */
border-radius: 2px;
}
@@ -496,16 +544,10 @@ QSlider::handle:hover {
background-color: rgb(200,199,200); /* veryLight */
}
-QSlider::sub-page {
- background-color: rgb(42,130,218); /* blue */
- border-radius: 2px;
-}
-
QSlider::handle:disabled {
background-color: rgb(122,121,122); /* light */
}
-
/* Volume Control */
VolumeMeter {
@@ -547,6 +589,27 @@ OBSHotkeyLabel[hotkeyPairHover=true] {
}
+/* Group Collapse Checkbox */
+
+SourceTreeSubItemCheckBox {
+ background: transparent;
+ outline: none;
+}
+
+SourceTreeSubItemCheckBox::indicator {
+ width: 10px;
+ height: 10px;
+}
+
+SourceTreeSubItemCheckBox::indicator:checked {
+ image: url(./Dark/expand.png);
+}
+
+SourceTreeSubItemCheckBox::indicator:unchecked {
+ image: url(./Dark/collapse.png);
+}
+
+
/* Label warning/error */
QLabel#warningLabel {
diff --git a/UI/data/themes/Dark/collapse.png b/UI/data/themes/Dark/collapse.png
new file mode 100644
index 0000000..1fd72a1
Binary files /dev/null and b/UI/data/themes/Dark/collapse.png differ
diff --git a/UI/data/themes/Dark/expand.png b/UI/data/themes/Dark/expand.png
new file mode 100644
index 0000000..dd5d2b5
Binary files /dev/null and b/UI/data/themes/Dark/expand.png differ
diff --git a/UI/data/themes/Default.qss b/UI/data/themes/Default.qss
index 0b88dd4..37f4b20 100644
--- a/UI/data/themes/Default.qss
+++ b/UI/data/themes/Default.qss
@@ -51,6 +51,24 @@ MuteCheckBox::indicator:unchecked {
image: url(:/res/images/unmute.png);
}
+SourceTreeSubItemCheckBox {
+ background: transparent;
+ outline: none;
+}
+
+SourceTreeSubItemCheckBox::indicator {
+ width: 10px;
+ height: 10px;
+}
+
+SourceTreeSubItemCheckBox::indicator:checked {
+ image: url(:/res/images/expand.png);
+}
+
+SourceTreeSubItemCheckBox::indicator:unchecked {
+ image: url(:/res/images/collapse.png);
+}
+
OBSHotkeyLabel[hotkeyPairHover=true] {
color: red;
}
diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss
index 5c7887c..e5a8362 100644
--- a/UI/data/themes/Rachni.qss
+++ b/UI/data/themes/Rachni.qss
@@ -40,6 +40,76 @@
/***************************************************************************/
+/************************/
+/* ---- Main Theme ---- */
+/************************/
+
+OBSTheme {
+ window: rgb(49, 54, 59); /* Blue-gray */
+ windowText: rgb(239, 240, 241); /* White */
+ base: rgb(0, 139, 163); /* Dark Cyan (Primary Dark) */
+ alternateBase: rgb(186, 45, 101); /* Dark Pink (Secondary Dark) */
+ text: rgb(239, 240, 241); /* White */
+ button: rgb(0, 188, 212); /* Cyan (Primary) */
+ buttonText: rgb(239, 240, 241); /* White */
+ brightText: rgb(255, 148, 194); /* Light Pink (Secondary Light) */
+
+ light: rgb(162, 161, 162); /* Lighter Gray */
+ mid: rgb(118, 121, 124); /* Light Grey */
+ dark: rgb(84, 87, 91); /* Gray */
+ shadow: rgb(35, 38, 41); /* Dark Gray */
+
+ highlight: rgb(98, 238, 255); /* Light Cyan (Primary Light) */
+ highlightText: rgb(0,0,0);
+
+ link: rgb(98, 238, 255); /* Light Cyan (Primary Light) */
+ linkVisited: rgb(98, 238, 255); /* Light Cyan (Primary Light) */
+}
+
+OBSTheme::disabled {
+ text: rgb(118, 121, 124); /* Light Gray */
+ buttonText: rgb(118, 121, 124); /* Light Gray */
+ brightText: rgb(118, 121, 124); /* Light Gray */
+}
+
+OBSTheme::inactive {
+ highlight: rgb(0, 188, 212); /* Cyan (Primary) */
+ highlightText: rgb(239, 240, 241); /* White */
+}
+
+
+/************************/
+/* ---- SourceTree ---- */
+/************************/
+
+SourceTree::item:selected:!active {
+ color: rgb(239, 240, 241); /* White */
+ background-color: rgba(255, 148, 194, 0.25); /* Light Pink (Secondary Light) */
+ border: none;
+}
+
+SourceTree::item:selected {
+ background-color: rgba(240, 98, 146, 0.5); /* Pink (Secondary) */
+ border: none;
+}
+
+SourceTree::item:hover,
+SourceTree::item:disabled:hover,
+SourceTree::item:hover:!active {
+ background-color: rgb(0, 188, 212); /* Cyan (Primary) */
+ color: rgb(239, 240, 241); /* White */
+ border: none;
+}
+
+SourceTree QLineEdit {
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-right: 0;
+ padding-left: 2px;
+ border: none;
+ border-radius: none;
+}
+
/*************************/
/* --- General style --- */
/*************************/
@@ -535,16 +605,6 @@ QLineEdit {
color: rgb(239, 240, 241); /* White */
}
-QListWidget QLineEdit {
- padding-top: 0;
- padding-bottom: 0;
- padding-right: 0;
- padding-left: 2px;
- border: none;
- border-radius: none;
-}
-
-
/**********************/
/* --- Checkboxes --- */
/**********************/
@@ -701,6 +761,30 @@ MuteCheckBox::indicator:unchecked:disabled {
image: url(./Dark/unmute.png);
}
+/****************************/
+/* --- Group Checkboxes --- */
+/****************************/
+
+SourceTreeSubItemCheckBox {
+ background: transparent;
+ outline: none;
+}
+
+SourceTreeSubItemCheckBox::indicator {
+ width: 10px;
+ height: 10px;
+}
+
+SourceTreeSubItemCheckBox::indicator:checked,
+SourceTreeSubItemCheckBox::indicator:checked:hover {
+ image: url(./Dark/expand.png);
+}
+
+SourceTreeSubItemCheckBox::indicator:unchecked,
+SourceTreeSubItemCheckBox::indicator:unchecked:hover {
+ image: url(./Dark/collapse.png);
+}
+
/*************************/
/* --- Progress bars --- */
/*************************/
@@ -775,10 +859,10 @@ QPushButton:disabled {
}
QPushButton::menu-indicator {
- image: url(./Rachni/down_arrow.png);
- subcontrol-position: right;
- subcontrol-origin: padding;
- width: 25px;
+ image: url(./Rachni/down_arrow.png);
+ subcontrol-position: right;
+ subcontrol-origin: padding;
+ width: 25px;
}
/******************************/
@@ -1022,10 +1106,55 @@ QSlider::handle:horizontal:pressed {
stop: 1 rgb(162, 161, 162));
}
+QSlider::sub-page:horizontal {
+ background-color: rgb(0, 188, 212); /* Cyan (Primary) */
+ border-radius: 2px;
+}
+
QSlider::sub-page:horizontal:disabled {
background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 rgb(35, 38, 41), /* Dark Gray */
+ stop: 0.75 rgb(35, 38, 41)); /* Dark Gray */
+ border-radius: 2px;
+}
+
+QSlider::groove:vertical {
+ background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
+ stop: 0 rgb(35, 38, 41), /* Dark Gray */
stop: 0.75 rgb(50, 49, 50));
+ width: 4px;
+ border: none;
+ border-radius: 2px;
+}
+
+QSlider::handle:vertical {
+ background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
+ stop: 0 rgb(240, 239, 240),
+ stop: 0.25 rgb(200, 199, 200),
+ stop: 1 rgb(162, 161, 162));
+ border: 1px solid rgb(58, 57, 58);
+ border-radius: 3px;
+ width: 10px;
+ height: 18px;
+ margin: 0 -3px;
+}
+
+QSlider::handle:vertical:pressed {
+ background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
+ stop: 0 rgb(240, 239, 240),
+ stop: 0.25 rgb(200, 199, 200),
+ stop: 1 rgb(162, 161, 162));
+}
+
+QSlider::add-page:vertical {
+ background-color: rgb(0, 188, 212); /* Cyan (Primary) */
+ border-radius: 2px;
+}
+
+QSlider::add-page:vertical:disabled {
+ background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0,
+ stop: 0 rgb(35, 38, 41), /* Dark Gray */
+ stop: 0.75 rgb(35, 38, 41)); /* Dark Gray */
border-radius: 2px;
}
@@ -1033,11 +1162,6 @@ QSlider::handle:hover {
background-color: rgb(200, 199, 200);
}
-QSlider::sub-page {
- background-color: rgb(0, 188, 212); /* Cyan (Primary) */
- border-radius: 2px;
-}
-
QSlider::handle:disabled {
background-color: rgb(122, 121, 122);
}
@@ -1069,6 +1193,14 @@ QFrame[frameShape="0"] {
/* Misc style tweaks for dark themes */
+* [themeID="error"] {
+ color: rgb(255, 89, 76); /* Red Error */
+}
+
+* [themeID="warning"] {
+ color: rgb(255, 148, 194); /* Light Pink (Secondary Light) */
+}
+
QStatusBar::item {
border: none;
}
diff --git a/UI/expand-checkbox.hpp b/UI/expand-checkbox.hpp
new file mode 100644
index 0000000..375a4ce
--- /dev/null
+++ b/UI/expand-checkbox.hpp
@@ -0,0 +1,5 @@
+#include
+
+class ExpandCheckBox : public QCheckBox {
+ Q_OBJECT
+};
diff --git a/UI/forms/ColorSelect.ui b/UI/forms/ColorSelect.ui
new file mode 100644
index 0000000..b07e1f9
--- /dev/null
+++ b/UI/forms/ColorSelect.ui
@@ -0,0 +1,265 @@
+
+
+ ColorSelect
+
+
+
+ 0
+ 0
+ 98
+ 61
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+ QPushButton {
+ border: 1px solid black;
+}
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+ 2
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+ 3
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+ 4
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+ 5
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+ 6
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+ 7
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui
index 55e66c4..983a869 100644
--- a/UI/forms/OBSBasic.ui
+++ b/UI/forms/OBSBasic.ui
@@ -146,6 +146,7 @@
+
@@ -518,7 +519,7 @@
0
-
-
+
0
@@ -615,63 +616,112 @@
4
-
-
-
-
- 220
- 0
-
-
-
- Qt::CustomContextMenu
-
-
- QFrame::StyledPanel
-
-
- QFrame::Sunken
-
-
- Qt::ScrollBarAlwaysOn
-
-
- Qt::ScrollBarAlwaysOff
-
-
- true
-
-
-
-
- 0
- 0
- 230
- 16
-
+
+
+
+ Qt::CustomContextMenu
-
-
- 0
- 0
-
+
+ QFrame::StyledPanel
-
-
- 0
+
+ QFrame::Sunken
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 230
+ 16
+
-
- 0
+
+
+ 0
+ 0
+
-
- 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+
+ Qt::CustomContextMenu
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 16
+ 230
+
-
- 0
+
+
+ 0
+ 0
+
-
- 0
-
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
@@ -1507,7 +1557,7 @@
- Basic.AutoConfig.Beta
+ Basic.AutoConfig
@@ -1601,6 +1651,11 @@
Basic.MainMenu.Help.CrashLogs.UploadLastLog
+
+
+ Basic.MainMenu.Help.Discord
+
+
@@ -1614,6 +1669,12 @@
QStatusBar
window-basic-status-bar.hpp
+
+ HScrollArea
+ QScrollArea
+ horizontal-scroll-area.hpp
+ 1
+
VScrollArea
QScrollArea
@@ -1621,9 +1682,9 @@
1
- SourceListWidget
- QListWidget
-
+ SourceTree
+ QListView
+
diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui
index c89cc1e..c92fc9e 100644
--- a/UI/forms/OBSBasicSettings.ui
+++ b/UI/forms/OBSBasicSettings.ui
@@ -146,7 +146,7 @@
0
0
801
- 741
+ 836
@@ -582,10 +582,74 @@
+
+
+
+ -
+
+
+ Basic.Settings.General.Multiview
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 2
+
+
-
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 170
+ 5
+
+
+
+
+ -
+
+
+ Basic.Settings.General.Multiview.MouseSwitch
+
+
+ true
+
+
+
+ -
+
+
+ Basic.Settings.General.Multiview.DrawSourceNames
+
+
+ true
+
+
+
-
+
+
+ Basic.Settings.General.Multiview.DrawSafeAreas
+
+
+ true
+
+
+
+ -
- -
+
-
Basic.Settings.General.MultiviewLayout
@@ -3362,49 +3426,6 @@
- -
-
-
- true
-
-
-
-
- 0
- 0
- 800
- 69
-
-
-
-
-
- -
-
-
- color: rgb(255, 0, 4);
-
-
-
-
-
- true
-
-
-
- -
-
-
-
-
-
- true
-
-
- warning
-
-
-
-
@@ -3440,6 +3461,86 @@
+ -
+
+
+ Basic.Settings.Audio.PeakMeterType
+
+
+
+ -
+
+
+ 0
+
+
-
+
+ Basic.Settings.Audio.PeakMeterType.SamplePeak
+
+
+ -
+
+ Basic.Settings.Audio.PeakMeterType.TruePeak
+
+
+
+
+ -
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 800
+ 69
+
+
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+ error
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+ warning
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
@@ -3744,8 +3845,8 @@
0
0
- 98
- 28
+ 818
+ 697
@@ -3762,7 +3863,7 @@
-
+
0
@@ -3791,11 +3892,11 @@
0
0
- 593
- 761
+ 803
+ 807
-
+
0
@@ -3808,9 +3909,9 @@
9
- -
+
-
-
+
-
@@ -3820,6 +3921,12 @@
QFormLayout::AllNonFixedFieldsGrow
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 2
+
-
@@ -3833,6 +3940,19 @@
-
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 0
+
+
+
+
@@ -3848,6 +3968,9 @@
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+ 2
+
-
@@ -4020,6 +4143,19 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
@@ -4032,6 +4168,12 @@
QFormLayout::AllNonFixedFieldsGrow
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 2
+
-
@@ -4052,6 +4194,19 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
@@ -4067,6 +4222,9 @@
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+ 2
+
-
@@ -4129,6 +4287,19 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
@@ -4141,6 +4312,9 @@
QFormLayout::AllNonFixedFieldsGrow
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
2
@@ -4230,6 +4404,19 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
@@ -4242,6 +4429,12 @@
QFormLayout::AllNonFixedFieldsGrow
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 2
+
-
@@ -4311,6 +4504,19 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
@@ -4323,9 +4529,12 @@
QFormLayout::AllNonFixedFieldsGrow
- -
-
-
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 2
+
-
@@ -4336,6 +4545,9 @@
+ -
+
+
-
@@ -4353,33 +4565,84 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
-
-
-
- color: rgb(255, 0, 4);
-
-
-
-
-
- true
+
+
+ Basic.Main.Sources
+
+
+ 2
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
+ -
+
+
+ BrowserSource.EnableHardwareAcceleration
+
+
+
+
-
-
-
- color: rgb(255, 0, 4);
-
-
-
-
-
- true
+
+
+ Basic.Settings.Hotkeys
+
+
+ 2
+
+
-
+
+
+ Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 170
+ 20
+
+
+
+
+
@@ -4389,6 +4652,48 @@
+ -
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+
-
+
+
+ color: rgb(255, 0, 4);
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ color: rgb(255, 0, 4);
+
+
+
+
+
+ true
+
+
+
+
+
diff --git a/UI/forms/images/collapse.png b/UI/forms/images/collapse.png
new file mode 100644
index 0000000..04707a4
Binary files /dev/null and b/UI/forms/images/collapse.png differ
diff --git a/UI/forms/images/expand.png b/UI/forms/images/expand.png
new file mode 100644
index 0000000..1222bcd
Binary files /dev/null and b/UI/forms/images/expand.png differ
diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc
index 367fc13..a68bc70 100644
--- a/UI/forms/obs.qrc
+++ b/UI/forms/obs.qrc
@@ -17,6 +17,8 @@
images/tray_active.png
images/locked_mask.png
images/unlocked_mask.png
+ images/collapse.png
+ images/expand.png
images/settings/advanced.png
diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt
index 37a022a..ca29cad 100644
--- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt
+++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt
@@ -25,6 +25,7 @@ set(frontend-tools_HEADERS
tool-helpers.hpp
../../properties-view.hpp
../../properties-view.moc.hpp
+ ../../horizontal-scroll-area.hpp
../../vertical-scroll-area.hpp
../../double-slider.hpp
)
@@ -34,6 +35,7 @@ set(frontend-tools_SOURCES
frontend-tools.c
output-timer.cpp
../../properties-view.cpp
+ ../../horizontal-scroll-area.cpp
../../vertical-scroll-area.cpp
../../double-slider.cpp
)
diff --git a/UI/frontend-plugins/frontend-tools/captions.cpp b/UI/frontend-plugins/frontend-tools/captions.cpp
index 43ebf94..5c68d87 100644
--- a/UI/frontend-plugins/frontend-tools/captions.cpp
+++ b/UI/frontend-plugins/frontend-tools/captions.cpp
@@ -1,4 +1,5 @@
#include
+#include
#include
#include
diff --git a/UI/frontend-plugins/frontend-tools/data/locale/el-GR.ini b/UI/frontend-plugins/frontend-tools/data/locale/el-GR.ini
index 7684795..0fa26e4 100644
--- a/UI/frontend-plugins/frontend-tools/data/locale/el-GR.ini
+++ b/UI/frontend-plugins/frontend-tools/data/locale/el-GR.ini
@@ -25,5 +25,18 @@ OutputTimer.Record.StoppingIn="Διακοπή εγγραφής σε:"
OutputTimer.Stream.EnableEverytime="Ενεργοποίηση χρονόμετρου streaming κάθε φορά"
OutputTimer.Record.EnableEverytime="Ενεργοποίηση χρονόμετρου εγγραφής κάθε φορά"
+Scripts="Δέσμες ενεργειών"
+LoadedScripts="Φορτωμένες δέσμες ενεργειών"
+AddScripts="Προσθήκη δεσμών ενεργειών"
+RemoveScripts="Αφαίρεση δεσμών ενεργειών"
+ReloadScripts="Επαναφόρτωση δεσμών ενεργειών"
+PythonSettings="Ρυθμίσεις Python"
+PythonSettings.PythonInstallPath32bit="Χώρος εγκατάστασης Python (32bit)"
+PythonSettings.PythonInstallPath64bit="Χώρος εγκατάστασης Python (64bit)"
+PythonSettings.BrowsePythonPath="Περιήγηση τού χώρου εγκατάστασης Python"
+ScriptLogWindow="Script Log"
+Description="Περιγραφή"
+FileFilter.ScriptFiles="Κρυπτογραφημένο αρχείο δέσμης ενεργειών"
+FileFilter.AllFiles="Όλα τα αρχεία"
diff --git a/UI/frontend-plugins/frontend-tools/data/locale/gd-GB.ini b/UI/frontend-plugins/frontend-tools/data/locale/gd-GB.ini
new file mode 100644
index 0000000..c7597a6
--- /dev/null
+++ b/UI/frontend-plugins/frontend-tools/data/locale/gd-GB.ini
@@ -0,0 +1,42 @@
+SceneSwitcher="Suidsear fèin-obrachail nan sealladh"
+SceneSwitcher.OnNoMatch="Mura freagair uinneag"
+SceneSwitcher.OnNoMatch.DontSwitch="Na dèan suids"
+SceneSwitcher.OnNoMatch.SwitchTo="Dèan suids gu:"
+SceneSwitcher.CheckInterval="Thoir sùil air tiotal na h-uinneige gnìomhaich gach:"
+SceneSwitcher.ActiveOrNotActive="Tha suidsear nan sealladh:"
+InvalidRegex.Title="Chan eil an t-eas-preisean riaghailteach dligheach"
+InvalidRegex.Text="Chan eil an t-eas-preisean riaghailteach a chuir thu a-steach dligheach."
+Active="Gnìomhach"
+Inactive="Neo-ghnìomhach"
+Start="Tòisich"
+Stop="Cuir stad air"
+
+Captions="Fo-thiotalan (deuchainneil)"
+Captions.AudioSource="Tùs fuaime"
+Captions.CurrentSystemLanguage="Cànan làithreach an t-siostaim (%1)"
+Captions.Provider="Solaraiche"
+Captions.Error.GenericFail="Cha deach leinn na fo-thiotalan a thòiseachadh"
+
+OutputTimer="Tìmear an às-chuir"
+OutputTimer.Stream="Cuir stad air an t-sruthadh às dèidh:"
+OutputTimer.Record="Cuir stad air a’ chlàradh às dèidh:"
+OutputTimer.Stream.StoppingIn="Thèid an sruthadh a chur ’na stad às dèidh:"
+OutputTimer.Record.StoppingIn="Thèid an clàradh a chur ’na stad às dèidh:"
+OutputTimer.Stream.EnableEverytime="Cuir tìmear an t-sruthaidh an comas gach turas"
+OutputTimer.Record.EnableEverytime="Cuir tìmear a’ chlàraidh an comas gach turas"
+
+Scripts="Sgriobtaichean"
+LoadedScripts="Sgriobtaichean luchdaichte"
+AddScripts="Cuir sgriobtaichean ris"
+RemoveScripts="Thoir sgriobtaichean air falbh"
+ReloadScripts="Ath-luchdaich na sgriobtaichean"
+PythonSettings="Roghainnean Python"
+PythonSettings.PythonInstallPath32bit="Slighe stàlaidh Python (32 biod)"
+PythonSettings.PythonInstallPath64bit="Slighe stàlaidh Python (64 biod)"
+PythonSettings.BrowsePythonPath="Rùraich airson slighe stàlaidh Python"
+ScriptLogWindow="Loga nan sgriobt"
+Description="Tuairisgeul"
+
+FileFilter.ScriptFiles="Faidhlichean sgriobt"
+FileFilter.AllFiles="Na h-uile faidhle"
+
diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ms-MY.ini b/UI/frontend-plugins/frontend-tools/data/locale/ms-MY.ini
index 18ab93e..2de18fa 100644
--- a/UI/frontend-plugins/frontend-tools/data/locale/ms-MY.ini
+++ b/UI/frontend-plugins/frontend-tools/data/locale/ms-MY.ini
@@ -1,13 +1,28 @@
+SceneSwitcher.OnNoMatch.DontSwitch="Tidak perlu tukar"
+SceneSwitcher.OnNoMatch.SwitchTo="Tukar ke:"
Active="Aktif"
Inactive="Tidak Aktif"
Start="Mula"
Stop="Berhenti"
+Captions="Kapsyen (eksperimen)"
+Captions.AudioSource="Sumber audio"
+Captions.CurrentSystemLanguage="Sistem bahasa sekarang(%1)"
+Captions.Provider="Provider"
OutputTimer.Stream="Berhenti 'streaming' selepas:"
OutputTimer.Record="Berhenti merakam selepas:"
OutputTimer.Stream.StoppingIn="'Streaming' dihentikan dalam:"
OutputTimer.Record.StoppingIn="Rakaman dihentikan dalam:"
+Scripts="Skrip"
+AddScripts="Tambah skrip"
+RemoveScripts="Keluarkan skrip"
+PythonSettings="Setting Python"
+PythonSettings.PythonInstallPath32bit="Python memasang laluan (32 bit)"
+PythonSettings.PythonInstallPath64bit="Python memasang laluan (64 bit)"
+Description="Huraian"
+FileFilter.ScriptFiles="Fail skrip"
+FileFilter.AllFiles="Semua fail"
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 bbaac9c..6611d79 100644
--- a/UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini
+++ b/UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini
@@ -4,6 +4,8 @@ SceneSwitcher.OnNoMatch.DontSwitch="Không chuyển"
SceneSwitcher.OnNoMatch.SwitchTo="Chuyển sang:"
SceneSwitcher.CheckInterval="Kiểm tra tiêu đề cửa sổ mỗi:"
SceneSwitcher.ActiveOrNotActive="Chuyển cảnh đang:"
+InvalidRegex.Title="Cụm từ thông dụng không hợp lệ"
+InvalidRegex.Text="Cụm từ thông dụng mà bạn đã nhập không hợp lệ."
Active="Đang hoạt động"
Inactive="Không hoạt động"
Start="Bắt đầu"
@@ -15,12 +17,24 @@ Captions.CurrentSystemLanguage="Ngôn ngữ hiện tại của máy tính (%1)"
Captions.Provider="Nhà cung cấp"
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.Record.StoppingIn="Quay video sẽ dừng trong:"
+OutputTimer.Stream.EnableEverytime="Bật hẹn giờ phát trực tuyến mỗi lần"
+OutputTimer.Record.EnableEverytime="Bật hẹn giờ phát mỗi lần"
+Scripts="Kịch bản"
+LoadedScripts="Kịch bản đã nạp"
AddScripts="Thêm script"
+RemoveScripts="Gỡ bỏ kịch bản"
+ReloadScripts="Nạp lại kịch bản"
+PythonSettings="Thiết lập Python"
+PythonSettings.PythonInstallPath32bit="Đường dẫn cài đặt Python (32bit)"
+PythonSettings.PythonInstallPath64bit="Đường dẫn cài đặt Python (64bit)"
+PythonSettings.BrowsePythonPath="Duyệt đường dẫn Python"
+ScriptLogWindow="Bản ghi kịch bản"
Description="Mô tả"
FileFilter.ScriptFiles="Tập tin script"
diff --git a/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini b/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini
index 274a488..316d412 100644
--- a/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini
+++ b/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini
@@ -11,10 +11,10 @@ Inactive="未激活"
Start="开始"
Stop="停止"
-Captions="标题(实验)"
+Captions="字幕 (实验)"
Captions.AudioSource="音频源"
Captions.CurrentSystemLanguage="当前系统语言 (%1)"
-Captions.Provider="供应商"
+Captions.Provider="提供程序"
Captions.Error.GenericFail="启动捕获失败"
OutputTimer="输出计时器"
diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp
index d923dcf..364757e 100644
--- a/UI/frontend-plugins/frontend-tools/scripts.cpp
+++ b/UI/frontend-plugins/frontend-tools/scripts.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
diff --git a/UI/horizontal-scroll-area.cpp b/UI/horizontal-scroll-area.cpp
new file mode 100644
index 0000000..8f927fc
--- /dev/null
+++ b/UI/horizontal-scroll-area.cpp
@@ -0,0 +1,10 @@
+#include
+#include "horizontal-scroll-area.hpp"
+
+void HScrollArea::resizeEvent(QResizeEvent *event)
+{
+ if (!!widget())
+ widget()->setMaximumHeight(event->size().height());
+
+ QScrollArea::resizeEvent(event);
+}
diff --git a/UI/horizontal-scroll-area.hpp b/UI/horizontal-scroll-area.hpp
new file mode 100644
index 0000000..8a64c3e
--- /dev/null
+++ b/UI/horizontal-scroll-area.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include
+
+class QResizeEvent;
+
+class HScrollArea : public QScrollArea {
+ Q_OBJECT
+
+public:
+ inline HScrollArea(QWidget *parent = nullptr)
+ : QScrollArea(parent)
+ {
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ }
+
+protected:
+ virtual void resizeEvent(QResizeEvent *event) override;
+};
diff --git a/UI/item-widget-helpers.hpp b/UI/item-widget-helpers.hpp
index 9a4d43e..776c2fb 100644
--- a/UI/item-widget-helpers.hpp
+++ b/UI/item-widget-helpers.hpp
@@ -28,3 +28,15 @@ class QListWidgetItem;
QListWidgetItem *TakeListItem(QListWidget *widget, int row);
void DeleteListItem(QListWidget *widget, QListWidgetItem *item);
void ClearListItems(QListWidget *widget);
+
+template
+void InsertQObjectByName(std::vector &controls, QObjectPtr control)
+{
+ QString name = control->objectName();
+ auto finder = [name](QObjectPtr elem) {
+ return elem->objectName() > name;
+ };
+ auto found_at = std::find_if(controls.begin(), controls.end(), finder);
+
+ controls.insert(found_at, control);
+}
diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp
index 503e5e1..52b15a0 100644
--- a/UI/obs-app.cpp
+++ b/UI/obs-app.cpp
@@ -24,9 +24,10 @@
#include
#include
#include
-#include
+#include
#include
#include
+#include
#include
#include
@@ -77,8 +78,9 @@ string opt_starting_collection;
string opt_starting_profile;
string opt_starting_scene;
-// AMD PowerXpress High Performance Flags
+// GPU hint exports for AMD/NVIDIA laptops
#ifdef _MSC_VER
+extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1;
extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#endif
@@ -345,6 +347,7 @@ static void do_log(int log_level, const char *msg, va_list args, void *param)
}
#else
def_log_handler(log_level, msg, args2, nullptr);
+ va_end(args2);
#endif
if (log_level <= LOG_INFO || log_verbose) {
@@ -366,6 +369,7 @@ bool OBSApp::InitGlobalConfigDefaults()
config_set_default_string(globalConfig, "General", "Language",
DEFAULT_LANG);
config_set_default_uint(globalConfig, "General", "MaxLogs", 10);
+ config_set_default_int(globalConfig, "General", "InfoIncrement", -1);
config_set_default_string(globalConfig, "General", "ProcessPriority",
"Normal");
config_set_default_bool(globalConfig, "General", "EnableAutoUpdates",
@@ -418,9 +422,25 @@ bool OBSApp::InitGlobalConfigDefaults()
"CurrentTheme", "Dark");
}
+ config_set_default_bool(globalConfig, "BasicWindow",
+ "VerticalVolControl", false);
+
+ config_set_default_bool(globalConfig, "BasicWindow",
+ "MultiviewMouseSwitch", true);
+
+ config_set_default_bool(globalConfig, "BasicWindow",
+ "MultiviewDrawNames", true);
+
+ config_set_default_bool(globalConfig, "BasicWindow",
+ "MultiviewDrawAreas", true);
+
#ifdef _WIN32
+ uint32_t winver = GetWindowsVersion();
+
config_set_default_bool(globalConfig, "Audio", "DisableAudioDucking",
true);
+ config_set_default_bool(globalConfig, "General", "BrowserHWAccel",
+ winver > 0x601);
#endif
#ifdef __APPLE__
@@ -588,6 +608,42 @@ static string GetSceneCollectionFileFromName(const char *name)
return outputPath;
}
+bool OBSApp::UpdatePre22MultiviewLayout(const char *layout)
+{
+ if (!layout)
+ return false;
+
+ if (astrcmpi(layout, "horizontaltop") == 0) {
+ config_set_int(globalConfig, "BasicWindow", "MultiviewLayout",
+ static_cast(
+ MultiviewLayout::HORIZONTAL_TOP_8_SCENES));
+ return true;
+ }
+
+ if (astrcmpi(layout, "horizontalbottom") == 0) {
+ config_set_int(globalConfig, "BasicWindow", "MultiviewLayout",
+ static_cast(
+ MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES));
+ return true;
+ }
+
+ if (astrcmpi(layout, "verticalleft") == 0) {
+ config_set_int(globalConfig, "BasicWindow", "MultiviewLayout",
+ static_cast(
+ MultiviewLayout::VERTICAL_LEFT_8_SCENES));
+ return true;
+ }
+
+ if (astrcmpi(layout, "verticalright") == 0) {
+ config_set_int(globalConfig, "BasicWindow", "MultiviewLayout",
+ static_cast(
+ MultiviewLayout::VERTICAL_RIGHT_8_SCENES));
+ return true;
+ }
+
+ return false;
+}
+
bool OBSApp::InitGlobalConfig()
{
char path[512];
@@ -653,6 +709,13 @@ bool OBSApp::InitGlobalConfig()
changed = true;
}
+ if (config_has_user_value(globalConfig, "BasicWindow",
+ "MultiviewLayout")) {
+ const char *layout = config_get_string(globalConfig,
+ "BasicWindow", "MultiviewLayout");
+ changed |= UpdatePre22MultiviewLayout(layout);
+ }
+
if (changed)
config_save_safe(globalConfig, "tmp", nullptr);
@@ -727,6 +790,192 @@ 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);
+}
+
bool OBSApp::SetTheme(std::string name, std::string path)
{
theme = name;
@@ -748,12 +997,18 @@ bool OBSApp::SetTheme(std::string name, std::string path)
}
QString mpath = QString("file:///") + path.c_str();
+ setPalette(defaultPalette);
setStyleSheet(mpath);
+ ParseExtraThemeData(path.c_str());
+
+ emit StyleChanged();
return true;
}
bool OBSApp::InitTheme()
{
+ defaultPalette = palette();
+
const char *themeName = config_get_string(globalConfig, "General",
"CurrentTheme");
if (!themeName) {
@@ -916,6 +1171,9 @@ void OBSApp::AppInit()
EnableOSXVSync(false);
#endif
+ enableHotkeysInFocus = !config_get_bool(globalConfig, "General",
+ "DisableHotkeysInFocus");
+
move_basic_to_profiles();
move_basic_to_scene_collections();
@@ -942,6 +1200,18 @@ static bool StartupOBS(const char *locale, profiler_name_store_t *store)
return obs_startup(locale, path, store);
}
+inline void OBSApp::ResetHotkeyState(bool inFocus)
+{
+ obs_hotkey_enable_background_press(
+ inFocus || enableHotkeysInFocus);
+}
+
+void OBSApp::EnableInFocusHotkeys(bool enable)
+{
+ enableHotkeysInFocus = enable;
+ ResetHotkeyState(applicationState() != Qt::ApplicationActive);
+}
+
bool OBSApp::OBSInit()
{
ProfileScope("OBSApp::OBSInit");
@@ -960,6 +1230,19 @@ bool OBSApp::OBSInit()
if (!StartupOBS(locale.c_str(), GetProfilerNameStore()))
return false;
+#ifdef _WIN32
+ bool browserHWAccel = config_get_bool(globalConfig, "General",
+ "BrowserHWAccel");
+
+ obs_data_t *settings = obs_data_create();
+ obs_data_set_bool(settings, "BrowserHWAccel", browserHWAccel);
+ obs_apply_private_data(settings);
+ obs_data_release(settings);
+
+ blog(LOG_INFO, "Browser Hardware Acceleration: %s",
+ browserHWAccel ? "true" : "false");
+#endif
+
blog(LOG_INFO, "Portable mode: %s",
portable_mode ? "true" : "false");
@@ -973,13 +1256,12 @@ bool OBSApp::OBSInit()
mainWindow->OBSInit();
connect(this, &QGuiApplication::applicationStateChanged,
- [](Qt::ApplicationState state)
+ [this](Qt::ApplicationState state)
{
- obs_hotkey_enable_background_press(
+ ResetHotkeyState(
state != Qt::ApplicationActive);
});
- obs_hotkey_enable_background_press(
- applicationState() != Qt::ApplicationActive);
+ ResetHotkeyState(applicationState() != Qt::ApplicationActive);
return true;
} else {
return false;
@@ -1002,7 +1284,9 @@ string OBSApp::GetVersionString() const
#ifdef _WIN32
if (sizeof(void*) == 8)
- ver << "64bit, ";
+ ver << "64-bit, ";
+ else
+ ver << "32-bit, ";
ver << "windows)";
#elif __APPLE__
@@ -1366,33 +1650,32 @@ static int run_program(fstream &logFile, int argc, char *argv[])
OBSApp program(argc, argv, profilerNameStore.get());
try {
+ bool created_log = false;
+
program.AppInit();
-
- OBSTranslator translator;
-
- create_log_file(logFile);
delete_oldest_file(false, "obs-studio/profiler_data");
+ OBSTranslator translator;
program.installTranslator(&translator);
#ifdef _WIN32
/* --------------------------------------- */
/* check and warn if already running */
+ bool cancel_launch = false;
bool already_running = false;
RunOnceMutex rom = GetRunOnceMutex(already_running);
- if (already_running && !multi) {
- blog(LOG_WARNING, "\n================================");
- blog(LOG_WARNING, "Warning: OBS is already running!");
- blog(LOG_WARNING, "================================\n");
+ if (!already_running) {
+ goto run;
+ }
+ if (!multi) {
QMessageBox::StandardButtons buttons(
QMessageBox::Yes | QMessageBox::Cancel);
QMessageBox mb(QMessageBox::Question,
QTStr("AlreadyRunning.Title"),
- QTStr("AlreadyRunning.Text"),
- buttons,
+ QTStr("AlreadyRunning.Text"), buttons,
nullptr);
mb.setButtonText(QMessageBox::Yes,
QTStr("AlreadyRunning.LaunchAnyway"));
@@ -1401,23 +1684,37 @@ static int run_program(fstream &logFile, int argc, char *argv[])
QMessageBox::StandardButton button;
button = (QMessageBox::StandardButton)mb.exec();
- if (button == QMessageBox::Cancel) {
- blog(LOG_INFO, "User shut down the program "
- "because OBS was already "
- "running");
- return 0;
- }
+ cancel_launch = button == QMessageBox::Cancel;
+ }
- blog(LOG_WARNING, "User is now running a secondary "
- "instance of OBS!");
+ if (cancel_launch)
+ return 0;
- } else if (already_running && multi) {
+ if (!created_log) {
+ create_log_file(logFile);
+ created_log = true;
+ }
+
+ if (multi) {
blog(LOG_INFO, "User enabled --multi flag and is now "
"running multiple instances of OBS.");
+ } else {
+ blog(LOG_WARNING, "================================");
+ blog(LOG_WARNING, "Warning: OBS is already running!");
+ blog(LOG_WARNING, "================================");
+ blog(LOG_WARNING, "User is now running multiple "
+ "instances of OBS!");
}
/* --------------------------------------- */
+run:
#endif
+
+ if (!created_log) {
+ create_log_file(logFile);
+ created_log = true;
+ }
+
if (argc > 1) {
stringstream stor;
stor << argv[1];
@@ -1887,6 +2184,7 @@ int main(int argc, char *argv[])
#endif
#ifdef _WIN32
+ obs_init_win32_crash_handler();
SetErrorMode(SEM_FAILCRITICALERRORS);
load_debug_privilege();
base_set_crash_handler(main_crash_handler, nullptr);
diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp
index 87d6a1c..9fd7448 100644
--- a/UI/obs-app.hpp
+++ b/UI/obs-app.hpp
@@ -73,13 +73,26 @@ private:
os_inhibit_t *sleepInhibitor = nullptr;
int sleepInhibitRefs = 0;
+ bool enableHotkeysInFocus = true;
+
+
std::deque translatorHooks;
+ bool UpdatePre22MultiviewLayout(const char *layout);
+
bool InitGlobalConfig();
bool InitGlobalConfigDefaults();
bool InitLocale();
bool InitTheme();
+ inline void ResetHotkeyState(bool inFocus);
+
+ QPalette defaultPalette;
+
+ void ParseExtraThemeData(const char *path);
+ void AddExtraThemeColor(QPalette &pal, int group,
+ const char *name, uint32_t color);
+
public:
OBSApp(int &argc, char **argv, profiler_name_store_t *store);
~OBSApp();
@@ -87,6 +100,8 @@ public:
void AppInit();
bool OBSInit();
+ void EnableInFocusHotkeys(bool enable);
+
inline QMainWindow *GetMainWindow() const {return mainWindow.data();}
inline config_t *GlobalConfig() const {return globalConfig;}
@@ -150,6 +165,9 @@ public:
{
translatorHooks.pop_front();
}
+
+signals:
+ void StyleChanged();
};
int GetConfigPath(char *path, size_t size, const char *name);
diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp
index ee47fff..c3c933a 100644
--- a/UI/obs-frontend-api/obs-frontend-api.cpp
+++ b/UI/obs-frontend-api/obs-frontend-api.cpp
@@ -148,6 +148,13 @@ void obs_frontend_set_current_scene_collection(const char *collection)
c->obs_frontend_set_current_scene_collection(collection);
}
+bool obs_frontend_add_scene_collection(const char *name)
+{
+ return callbacks_valid()
+ ? c->obs_frontend_add_scene_collection(name)
+ : false;
+}
+
char **obs_frontend_get_profiles(void)
{
if (!callbacks_valid())
@@ -297,6 +304,18 @@ void obs_frontend_save(void)
c->obs_frontend_save();
}
+void obs_frontend_defer_save_begin(void)
+{
+ if (callbacks_valid())
+ c->obs_frontend_defer_save_begin();
+}
+
+void obs_frontend_defer_save_end(void)
+{
+ if (callbacks_valid())
+ c->obs_frontend_defer_save_end();
+}
+
void obs_frontend_add_save_callback(obs_frontend_save_cb callback,
void *private_data)
{
diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h
index 05483ff..ae67494 100644
--- a/UI/obs-frontend-api/obs-frontend-api.h
+++ b/UI/obs-frontend-api/obs-frontend-api.h
@@ -42,7 +42,8 @@ enum obs_frontend_event {
OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED,
OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED,
- OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP
+ OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP,
+ OBS_FRONTEND_EVENT_FINISHED_LOADING
};
/* ------------------------------------------------------------------------- */
@@ -95,6 +96,7 @@ EXPORT void obs_frontend_set_current_transition(obs_source_t *transition);
EXPORT char **obs_frontend_get_scene_collections(void);
EXPORT char *obs_frontend_get_current_scene_collection(void);
EXPORT void obs_frontend_set_current_scene_collection(const char *collection);
+EXPORT bool obs_frontend_add_scene_collection(const char *name);
EXPORT char **obs_frontend_get_profiles(void);
EXPORT char *obs_frontend_get_current_profile(void);
@@ -150,6 +152,8 @@ EXPORT void obs_frontend_replay_buffer_stop(void);
EXPORT bool obs_frontend_replay_buffer_active(void);
EXPORT void obs_frontend_save(void);
+EXPORT void obs_frontend_defer_save_begin(void);
+EXPORT void obs_frontend_defer_save_end(void);
EXPORT obs_output_t *obs_frontend_get_streaming_output(void);
EXPORT obs_output_t *obs_frontend_get_recording_output(void);
diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp
index 0749d2e..b1a9064 100644
--- a/UI/obs-frontend-api/obs-frontend-internal.hpp
+++ b/UI/obs-frontend-api/obs-frontend-internal.hpp
@@ -26,6 +26,7 @@ struct obs_frontend_callbacks {
virtual char *obs_frontend_get_current_scene_collection(void)=0;
virtual void obs_frontend_set_current_scene_collection(
const char *collection)=0;
+ virtual bool obs_frontend_add_scene_collection(const char *name)=0;
virtual void obs_frontend_get_profiles(
std::vector &strings)=0;
@@ -61,7 +62,9 @@ struct obs_frontend_callbacks {
virtual config_t *obs_frontend_get_profile_config(void)=0;
virtual config_t *obs_frontend_get_global_config(void)=0;
- virtual void obs_frontend_save(void)=0;
+ virtual void obs_frontend_save(void) = 0;
+ virtual void obs_frontend_defer_save_begin(void) = 0;
+ virtual void obs_frontend_defer_save_end(void) = 0;
virtual void obs_frontend_add_save_callback(
obs_frontend_save_cb callback, void *private_data)=0;
virtual void obs_frontend_remove_save_callback(
diff --git a/UI/platform-osx.mm b/UI/platform-osx.mm
index 19b1a9c..37e7c0a 100644
--- a/UI/platform-osx.mm
+++ b/UI/platform-osx.mm
@@ -168,3 +168,11 @@ void EnableOSXVSync(bool enable)
deferred_updates(enable ? 1 : 0);
}
}
+
+void EnableOSXDockIcon(bool enable)
+{
+ if (enable)
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+ else
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
+}
diff --git a/UI/platform.hpp b/UI/platform.hpp
index 5011e60..4da23b3 100644
--- a/UI/platform.hpp
+++ b/UI/platform.hpp
@@ -63,4 +63,5 @@ RunOnceMutex GetRunOnceMutex(bool &already_running);
#ifdef __APPLE__
void EnableOSXVSync(bool enable);
+void EnableOSXDockIcon(bool enable);
#endif
diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp
index 169b9f7..4a286b6 100644
--- a/UI/properties-view.cpp
+++ b/UI/properties-view.cpp
@@ -576,6 +576,10 @@ void OBSPropertiesView::AddEditableList(obs_property_t *prop,
for (size_t i = 0; i < count; i++) {
obs_data_t *item = obs_data_array_item(array, i);
list->addItem(QT_UTF8(obs_data_get_string(item, "value")));
+ list->setItemSelected(list->item((int)i),
+ obs_data_get_bool(item, "selected"));
+ list->setItemHidden(list->item((int)i),
+ obs_data_get_bool(item, "hidden"));
obs_data_release(item);
}
@@ -634,9 +638,16 @@ void OBSPropertiesView::AddColor(obs_property_t *prop, QFormLayout *layout,
button->setText(QTStr("Basic.PropertiesWindow.SelectColor"));
button->setToolTip(QT_UTF8(obs_property_long_description(prop)));
+ color.setAlpha(255);
+
+ QPalette palette = QPalette(color);
colorLabel->setFrameStyle(QFrame::Sunken | QFrame::Panel);
colorLabel->setText(color.name(QColor::HexArgb));
- colorLabel->setPalette(QPalette(color));
+ colorLabel->setPalette(palette);
+ colorLabel->setStyleSheet(
+ QString("background-color :%1; color: %2;")
+ .arg(palette.color(QPalette::Window).name(QColor::HexArgb))
+ .arg(palette.color(QPalette::WindowText).name(QColor::HexArgb)));
colorLabel->setAutoFillBackground(true);
colorLabel->setAlignment(Qt::AlignCenter);
colorLabel->setToolTip(QT_UTF8(obs_property_long_description(prop)));
@@ -1626,13 +1637,19 @@ bool WidgetInfo::ColorChanged(const char *setting)
#endif
color = QColorDialog::getColor(color, view, QT_UTF8(desc), options);
+ color.setAlpha(255);
if (!color.isValid())
return false;
QLabel *label = static_cast(widget);
label->setText(color.name(QColor::HexArgb));
- label->setPalette(QPalette(color));
+ QPalette palette = QPalette(color);
+ label->setPalette(palette);
+ label->setStyleSheet(
+ QString("background-color :%1; color: %2;")
+ .arg(palette.color(QPalette::Window).name(QColor::HexArgb))
+ .arg(palette.color(QPalette::WindowText).name(QColor::HexArgb)));
obs_data_set_int(view->settings, setting, color_to_int(color));
@@ -1646,11 +1663,18 @@ bool WidgetInfo::FontChanged(const char *setting)
uint32_t flags;
QFont font;
+ QFontDialog::FontDialogOptions options;
+
+#ifdef __APPLE__
+ options = QFontDialog::DontUseNativeDialog;
+#endif
+
if (!font_obj) {
- font = QFontDialog::getFont(&success, view);
+ QFont initial;
+ font = QFontDialog::getFont(&success, initial, view, "Pick a Font", options);
} else {
MakeQFont(font_obj, font);
- font = QFontDialog::getFont(&success, font, view);
+ font = QFontDialog::getFont(&success, font, view, "Pick a Font", options);
obs_data_release(font_obj);
}
@@ -1690,7 +1714,10 @@ void WidgetInfo::EditableListChanged()
obs_data_t *arrayItem = obs_data_create();
obs_data_set_string(arrayItem, "value",
QT_TO_UTF8(item->text()));
-
+ obs_data_set_bool(arrayItem, "selected",
+ item->isSelected());
+ obs_data_set_bool(arrayItem, "hidden",
+ item->isHidden());
obs_data_array_push_back(array, arrayItem);
obs_data_release(arrayItem);
}
diff --git a/UI/qt-wrappers.hpp b/UI/qt-wrappers.hpp
index 7899387..5273f92 100644
--- a/UI/qt-wrappers.hpp
+++ b/UI/qt-wrappers.hpp
@@ -17,8 +17,10 @@
#pragma once
+#include
#include
#include
+#include
#include
#include
@@ -79,3 +81,10 @@ public:
};
void DeleteLayout(QLayout *layout);
+
+static inline Qt::ConnectionType WaitConnection()
+{
+ return QThread::currentThread() == qApp->thread()
+ ? Qt::DirectConnection
+ : Qt::BlockingQueuedConnection;
+}
diff --git a/UI/slider-absoluteset-style.hpp b/UI/slider-absoluteset-style.hpp
index a94ebeb..d275ad2 100644
--- a/UI/slider-absoluteset-style.hpp
+++ b/UI/slider-absoluteset-style.hpp
@@ -6,7 +6,7 @@ class SliderAbsoluteSetStyle : public QProxyStyle
{
public:
SliderAbsoluteSetStyle(const QString& baseStyle);
- SliderAbsoluteSetStyle(QStyle* baseStyle);
+ SliderAbsoluteSetStyle(QStyle* baseStyle = Q_NULLPTR);
int styleHint(QStyle::StyleHint hint, const QStyleOption* option,
const QWidget* widget, QStyleHintReturn* returnData) const;
};
diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp
new file mode 100644
index 0000000..1652988
--- /dev/null
+++ b/UI/source-tree.cpp
@@ -0,0 +1,1390 @@
+#include "window-basic-main.hpp"
+#include "obs-app.hpp"
+#include "source-tree.hpp"
+#include "qt-wrappers.hpp"
+#include "visibility-checkbox.hpp"
+#include "locked-checkbox.hpp"
+#include "expand-checkbox.hpp"
+
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+static inline OBSScene GetCurrentScene()
+{
+ OBSBasic *main = reinterpret_cast(App()->GetMainWindow());
+ return main->GetCurrentScene();
+}
+
+/* ========================================================================= */
+
+SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_)
+ : tree (tree_),
+ sceneitem (sceneitem_)
+{
+ setAttribute(Qt::WA_TranslucentBackground);
+
+ obs_source_t *source = obs_sceneitem_get_source(sceneitem);
+ const char *name = obs_source_get_name(source);
+
+ obs_data_t *privData = obs_sceneitem_get_private_settings(sceneitem);
+ int preset = obs_data_get_int(privData, "color-preset");
+
+ if (preset == 1) {
+ const char *color = obs_data_get_string(privData, "color");
+ std::string col = "background: ";
+ col += color;
+ setStyleSheet(col.c_str());
+ } else if (preset > 1) {
+ setStyleSheet("");
+ setProperty("bgColor", preset - 1);
+ } else {
+ setStyleSheet("background: none");
+ }
+
+ obs_data_release(privData);
+
+ vis = new VisibilityCheckBox();
+ vis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+ vis->setMaximumSize(16, 16);
+ vis->setChecked(obs_sceneitem_visible(sceneitem));
+
+ lock = new LockedCheckBox();
+ lock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+ lock->setMaximumSize(16, 16);
+ lock->setChecked(obs_sceneitem_locked(sceneitem));
+
+ label = new QLabel(QT_UTF8(name));
+ label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ label->setAttribute(Qt::WA_TranslucentBackground);
+
+#ifdef __APPLE__
+ vis->setAttribute(Qt::WA_LayoutUsesWidgetRect);
+ lock->setAttribute(Qt::WA_LayoutUsesWidgetRect);
+#endif
+
+ boxLayout = new QHBoxLayout();
+ boxLayout->setContentsMargins(1, 1, 2, 1);
+ boxLayout->setSpacing(1);
+ boxLayout->addWidget(label);
+ boxLayout->addWidget(vis);
+ boxLayout->addWidget(lock);
+#ifdef __APPLE__
+ /* Hack: Fixes a bug where scrollbars would be above the lock icon */
+ boxLayout->addSpacing(16);
+#endif
+
+ Update(false);
+
+ setLayout(boxLayout);
+
+ /* --------------------------------------------------------- */
+
+ auto setItemVisible = [this] (bool checked)
+ {
+ SignalBlocker sourcesSignalBlocker(this);
+ obs_sceneitem_set_visible(sceneitem, checked);
+ };
+
+ auto setItemLocked = [this] (bool checked)
+ {
+ SignalBlocker sourcesSignalBlocker(this);
+ obs_sceneitem_set_locked(sceneitem, checked);
+ };
+
+ connect(vis, &QAbstractButton::clicked, setItemVisible);
+ connect(lock, &QAbstractButton::clicked, setItemLocked);
+}
+
+void SourceTreeItem::paintEvent(QPaintEvent *event)
+{
+ QStyleOption opt;
+ opt.init(this);
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+
+ QWidget::paintEvent(event);
+}
+
+void SourceTreeItem::DisconnectSignals()
+{
+ sceneRemoveSignal.Disconnect();
+ itemRemoveSignal.Disconnect();
+ deselectSignal.Disconnect();
+ visibleSignal.Disconnect();
+ renameSignal.Disconnect();
+ removeSignal.Disconnect();
+}
+
+void SourceTreeItem::ReconnectSignals()
+{
+ if (!sceneitem)
+ return;
+
+ DisconnectSignals();
+
+ /* --------------------------------------------------------- */
+
+ auto removeItem = [] (void *data, calldata_t *cd)
+ {
+ SourceTreeItem *this_ = reinterpret_cast(data);
+ obs_sceneitem_t *curItem =
+ (obs_sceneitem_t*)calldata_ptr(cd, "item");
+
+ if (curItem == this_->sceneitem) {
+ QMetaObject::invokeMethod(this_->tree,
+ "Remove",
+ Q_ARG(OBSSceneItem, curItem));
+ curItem = nullptr;
+ }
+ if (!curItem) {
+ this_->DisconnectSignals();
+ this_->sceneitem = nullptr;
+ }
+ };
+
+ auto itemVisible = [] (void *data, calldata_t *cd)
+ {
+ SourceTreeItem *this_ = reinterpret_cast(data);
+ obs_sceneitem_t *curItem =
+ (obs_sceneitem_t*)calldata_ptr(cd, "item");
+ bool visible = calldata_bool(cd, "visible");
+
+ if (curItem == this_->sceneitem)
+ QMetaObject::invokeMethod(this_, "VisibilityChanged",
+ Q_ARG(bool, visible));
+ };
+
+ auto itemDeselect = [] (void *data, calldata_t *cd)
+ {
+ SourceTreeItem *this_ = reinterpret_cast(data);
+ obs_sceneitem_t *curItem =
+ (obs_sceneitem_t*)calldata_ptr(cd, "item");
+
+ if (curItem == this_->sceneitem)
+ QMetaObject::invokeMethod(this_, "Deselect");
+ };
+
+ auto reorderGroup = [] (void *data, calldata_t*)
+ {
+ SourceTreeItem *this_ = reinterpret_cast(data);
+ QMetaObject::invokeMethod(this_->tree, "ReorderItems");
+ };
+
+ obs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);
+ obs_source_t *sceneSource = obs_scene_get_source(scene);
+ signal_handler_t *signal = obs_source_get_signal_handler(sceneSource);
+
+ sceneRemoveSignal.Connect(signal, "remove", removeItem, this);
+ itemRemoveSignal.Connect(signal, "item_remove", removeItem, this);
+ visibleSignal.Connect(signal, "item_visible", itemVisible, this);
+
+ if (obs_sceneitem_is_group(sceneitem)) {
+ obs_source_t *source = obs_sceneitem_get_source(sceneitem);
+ signal = obs_source_get_signal_handler(source);
+
+ groupReorderSignal.Connect(signal, "reorder", reorderGroup,
+ this);
+ }
+
+ if (scene != GetCurrentScene())
+ deselectSignal.Connect(signal, "item_deselect", itemDeselect,
+ this);
+
+ /* --------------------------------------------------------- */
+
+ 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);
+ this_->DisconnectSignals();
+ this_->sceneitem = nullptr;
+ };
+
+ obs_source_t *source = obs_sceneitem_get_source(sceneitem);
+ signal = obs_source_get_signal_handler(source);
+ renameSignal.Connect(signal, "rename", renamed, this);
+ removeSignal.Connect(signal, "remove", removeSource, this);
+}
+
+void SourceTreeItem::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ QWidget::mouseDoubleClickEvent(event);
+
+ if (expand) {
+ expand->setChecked(!expand->isChecked());
+ } else {
+ obs_source_t *source = obs_sceneitem_get_source(sceneitem);
+ OBSBasic *main =
+ reinterpret_cast(App()->GetMainWindow());
+ if (source) {
+ main->CreatePropertiesWindow(source);
+ }
+ }
+}
+
+void SourceTreeItem::EnterEditMode()
+{
+ setFocusPolicy(Qt::StrongFocus);
+ boxLayout->removeWidget(label);
+ editor = new QLineEdit(label->text());
+ editor->installEventFilter(this);
+ boxLayout->insertWidget(1, editor);
+ setFocusProxy(editor);
+}
+
+void SourceTreeItem::ExitEditMode(bool save)
+{
+ if (!editor)
+ return;
+
+ OBSBasic *main = reinterpret_cast(App()->GetMainWindow());
+ OBSScene scene = main->GetCurrentScene();
+
+ std::string newName = QT_TO_UTF8(editor->text());
+
+ setFocusProxy(nullptr);
+ boxLayout->removeWidget(editor);
+ delete editor;
+ editor = nullptr;
+ setFocusPolicy(Qt::NoFocus);
+ boxLayout->insertWidget(1, label);
+
+ /* ----------------------------------------- */
+ /* check for empty string */
+
+ if (!save)
+ return;
+
+ if (newName.empty()) {
+ OBSMessageBox::information(main,
+ QTStr("NoNameEntered.Title"),
+ QTStr("NoNameEntered.Text"));
+ return;
+ }
+
+ /* ----------------------------------------- */
+ /* Check for same name */
+
+ obs_source_t *source = obs_sceneitem_get_source(sceneitem);
+ if (newName == obs_source_get_name(source))
+ return;
+
+ /* ----------------------------------------- */
+ /* check for existing source */
+
+ obs_source_t *existingSource =
+ obs_get_source_by_name(newName.c_str());
+ obs_source_release(existingSource);
+ bool exists = !!existingSource;
+
+ if (exists) {
+ OBSMessageBox::information(main,
+ QTStr("NameExists.Title"),
+ QTStr("NameExists.Text"));
+ return;
+ }
+
+ /* ----------------------------------------- */
+ /* rename */
+
+ SignalBlocker sourcesSignalBlocker(this);
+ obs_source_set_name(source, newName.c_str());
+ label->setText(QT_UTF8(newName.c_str()));
+}
+
+bool SourceTreeItem::eventFilter(QObject *object, QEvent *event)
+{
+ if (editor != object)
+ return false;
+
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *keyEvent = static_cast(event);
+
+ switch (keyEvent->key()) {
+ case Qt::Key_Escape:
+ QMetaObject::invokeMethod(this, "ExitEditMode",
+ Qt::QueuedConnection,
+ Q_ARG(bool, false));
+ return true;
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ QMetaObject::invokeMethod(this, "ExitEditMode",
+ Qt::QueuedConnection,
+ Q_ARG(bool, true));
+ return true;
+ }
+ } else if (event->type() == QEvent::FocusOut) {
+ QMetaObject::invokeMethod(this, "ExitEditMode",
+ Qt::QueuedConnection,
+ Q_ARG(bool, true));
+ return true;
+ }
+
+ return false;
+}
+
+void SourceTreeItem::VisibilityChanged(bool visible)
+{
+ vis->setChecked(visible);
+}
+
+void SourceTreeItem::Renamed(const QString &name)
+{
+ label->setText(name);
+}
+
+void SourceTreeItem::Update(bool force)
+{
+ OBSScene scene = GetCurrentScene();
+ obs_scene_t *itemScene = obs_sceneitem_get_scene(sceneitem);
+
+ Type newType;
+
+ /* ------------------------------------------------- */
+ /* if it's a group item, insert group checkbox */
+
+ if (obs_sceneitem_is_group(sceneitem)) {
+ newType = Type::Group;
+
+ /* ------------------------------------------------- */
+ /* if it's a group sub-item */
+
+ } else if (itemScene != scene) {
+ newType = Type::SubItem;
+
+ /* ------------------------------------------------- */
+ /* if it's a regular item */
+
+ } else {
+ newType = Type::Item;
+ }
+
+ /* ------------------------------------------------- */
+
+ if (!force && newType == type) {
+ return;
+ }
+
+ /* ------------------------------------------------- */
+
+ ReconnectSignals();
+
+ if (spacer) {
+ boxLayout->removeItem(spacer);
+ delete spacer;
+ spacer = nullptr;
+ }
+
+ if (type == Type::Group) {
+ boxLayout->removeWidget(expand);
+ expand->deleteLater();
+ expand = nullptr;
+ }
+
+ type = newType;
+
+ if (type == Type::SubItem) {
+ spacer = new QSpacerItem(16, 1);
+ boxLayout->insertItem(0, spacer);
+
+ } else if (type == Type::Group) {
+ expand = new SourceTreeSubItemCheckBox();
+ expand->setSizePolicy(
+ QSizePolicy::Maximum,
+ QSizePolicy::Maximum);
+ expand->setMaximumSize(10, 16);
+ expand->setMinimumSize(10, 0);
+#ifdef __APPLE__
+ expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
+#endif
+ boxLayout->insertWidget(0, expand);
+
+ obs_data_t *data = obs_sceneitem_get_private_settings(sceneitem);
+ expand->blockSignals(true);
+ expand->setChecked(obs_data_get_bool(data, "collapsed"));
+ expand->blockSignals(false);
+ obs_data_release(data);
+
+ connect(expand, &QPushButton::toggled,
+ this, &SourceTreeItem::ExpandClicked);
+
+ } else {
+ spacer = new QSpacerItem(3, 1);
+ boxLayout->insertItem(0, spacer);
+ }
+}
+
+void SourceTreeItem::ExpandClicked(bool checked)
+{
+ OBSData data = obs_sceneitem_get_private_settings(sceneitem);
+ obs_data_release(data);
+
+ obs_data_set_bool(data, "collapsed", checked);
+
+ if (!checked)
+ tree->GetStm()->ExpandGroup(sceneitem);
+ else
+ tree->GetStm()->CollapseGroup(sceneitem);
+}
+
+void SourceTreeItem::Deselect()
+{
+ tree->SelectItem(sceneitem, false);
+}
+
+/* ========================================================================= */
+
+void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)
+{
+ SourceTreeModel *stm = reinterpret_cast(ptr);
+
+ switch ((int)event) {
+ case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
+ stm->SceneChanged();
+ break;
+ case OBS_FRONTEND_EVENT_EXIT:
+ case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
+ stm->Clear();
+ break;
+ }
+}
+
+void SourceTreeModel::Clear()
+{
+ beginResetModel();
+ items.clear();
+ endResetModel();
+
+ hasGroups = false;
+}
+
+static bool enumItem(obs_scene_t*, obs_sceneitem_t *item, void *ptr)
+{
+ QVector &items =
+ *reinterpret_cast*>(ptr);
+
+ if (obs_sceneitem_is_group(item)) {
+ obs_data_t *data = obs_sceneitem_get_private_settings(item);
+
+ bool collapse = obs_data_get_bool(data, "collapsed");
+ if (!collapse) {
+ obs_scene_t *scene =
+ obs_sceneitem_group_get_scene(item);
+
+ obs_scene_enum_items(scene, enumItem, &items);
+ }
+
+ obs_data_release(data);
+ }
+
+ items.insert(0, item);
+ return true;
+}
+
+void SourceTreeModel::SceneChanged()
+{
+ OBSScene scene = GetCurrentScene();
+
+ beginResetModel();
+ items.clear();
+ obs_scene_enum_items(scene, enumItem, &items);
+ endResetModel();
+
+ UpdateGroupState(false);
+ st->ResetWidgets();
+
+ for (int i = 0; i < items.count(); i++) {
+ bool select = obs_sceneitem_selected(items[i]);
+ QModelIndex index = createIndex(i, 0);
+
+ st->selectionModel()->select(index, select
+ ? QItemSelectionModel::Select
+ : QItemSelectionModel::Deselect);
+ }
+}
+
+/* moves a scene item index (blame linux distros for using older Qt builds) */
+static inline void MoveItem(QVector &items, int oldIdx, int newIdx)
+{
+ OBSSceneItem item = items[oldIdx];
+ items.remove(oldIdx);
+ items.insert(newIdx, item);
+}
+
+/* reorders list optimally with model reorder funcs */
+void SourceTreeModel::ReorderItems()
+{
+ OBSScene scene = GetCurrentScene();
+
+ QVector newitems;
+ obs_scene_enum_items(scene, enumItem, &newitems);
+
+ /* if item list has changed size, do full reset */
+ if (newitems.count() != items.count()) {
+ SceneChanged();
+ return;
+ }
+
+ for (;;) {
+ int idx1Old = 0;
+ int idx1New = 0;
+ int count;
+ int i;
+
+ /* find first starting changed item index */
+ for (i = 0; i < newitems.count(); i++) {
+ obs_sceneitem_t *oldItem = items[i];
+ obs_sceneitem_t *newItem = newitems[i];
+ if (oldItem != newItem) {
+ idx1Old = i;
+ break;
+ }
+ }
+
+ /* if everything is the same, break */
+ if (i == newitems.count()) {
+ break;
+ }
+
+ /* find new starting index */
+ for (i = idx1Old + 1; i < newitems.count(); i++) {
+ obs_sceneitem_t *oldItem = items[idx1Old];
+ obs_sceneitem_t *newItem = newitems[i];
+
+ if (oldItem == newItem) {
+ idx1New = i;
+ break;
+ }
+ }
+
+ /* if item could not be found, do full reset */
+ if (i == newitems.count()) {
+ SceneChanged();
+ return;
+ }
+
+ /* get move count */
+ for (count = 1; (idx1New + count) < newitems.count(); count++) {
+ int oldIdx = idx1Old + count;
+ int newIdx = idx1New + count;
+
+ obs_sceneitem_t *oldItem = items[oldIdx];
+ obs_sceneitem_t *newItem = newitems[newIdx];
+
+ if (oldItem != newItem) {
+ break;
+ }
+ }
+
+ /* move items */
+ beginMoveRows(QModelIndex(), idx1Old, idx1Old + count - 1,
+ QModelIndex(), idx1New + count);
+ for (i = 0; i < count; i++) {
+ int to = idx1New + count;
+ if (to > idx1Old)
+ to--;
+ MoveItem(items, idx1Old, to);
+ }
+ endMoveRows();
+ }
+}
+
+void SourceTreeModel::Add(obs_sceneitem_t *item)
+{
+ if (obs_sceneitem_is_group(item)) {
+ SceneChanged();
+ } else {
+ beginInsertRows(QModelIndex(), 0, 0);
+ items.insert(0, item);
+ endInsertRows();
+
+ st->UpdateWidget(createIndex(0, 0, nullptr), item);
+ }
+}
+
+void SourceTreeModel::Remove(obs_sceneitem_t *item)
+{
+ int idx = -1;
+ for (int i = 0; i < items.count(); i++) {
+ if (items[i] == item) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx == -1)
+ return;
+
+ int startIdx = idx;
+ int endIdx = idx;
+
+ bool is_group = obs_sceneitem_is_group(item);
+ if (is_group) {
+ obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
+
+ for (int i = endIdx + 1; i < items.count(); i++) {
+ obs_sceneitem_t *subitem = items[i];
+ obs_scene_t *subscene =
+ obs_sceneitem_get_scene(subitem);
+
+ if (subscene == scene)
+ endIdx = i;
+ else
+ break;
+ }
+ }
+
+ beginRemoveRows(QModelIndex(), startIdx, endIdx);
+ items.remove(idx, endIdx - startIdx + 1);
+ endRemoveRows();
+
+ if (is_group)
+ UpdateGroupState(true);
+}
+
+OBSSceneItem SourceTreeModel::Get(int idx)
+{
+ if (idx == -1 || idx >= items.count())
+ return OBSSceneItem();
+ return items[idx];
+}
+
+SourceTreeModel::SourceTreeModel(SourceTree *st_)
+ : QAbstractListModel (st_),
+ st (st_)
+{
+ obs_frontend_add_event_callback(OBSFrontendEvent, this);
+}
+
+SourceTreeModel::~SourceTreeModel()
+{
+ obs_frontend_remove_event_callback(OBSFrontendEvent, this);
+}
+
+int SourceTreeModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : items.count();
+}
+
+QVariant SourceTreeModel::data(const QModelIndex &, int) const
+{
+ return QVariant();
+}
+
+Qt::ItemFlags SourceTreeModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;
+
+ obs_sceneitem_t *item = items[index.row()];
+ bool is_group = obs_sceneitem_is_group(item);
+
+ return QAbstractListModel::flags(index) |
+ Qt::ItemIsEditable |
+ Qt::ItemIsDragEnabled |
+ (is_group ? Qt::ItemIsDropEnabled : Qt::NoItemFlags);
+}
+
+Qt::DropActions SourceTreeModel::supportedDropActions() const
+{
+ return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
+}
+
+QString SourceTreeModel::GetNewGroupName()
+{
+ OBSScene scene = GetCurrentScene();
+ QString name = QTStr("Group");
+
+ int i = 2;
+ for (;;) {
+ obs_source_t *group = obs_get_source_by_name(QT_TO_UTF8(name));
+ obs_source_release(group);
+ if (!group)
+ break;
+ name = QTStr("Basic.Main.Group").arg(QString::number(i++));
+ }
+
+ return name;
+}
+
+void SourceTreeModel::AddGroup()
+{
+ QString name = GetNewGroupName();
+ obs_sceneitem_t *group = obs_scene_add_group(GetCurrentScene(),
+ QT_TO_UTF8(name));
+ if (!group)
+ return;
+
+ beginInsertRows(QModelIndex(), 0, 0);
+ items.insert(0, group);
+ endInsertRows();
+
+ st->UpdateWidget(createIndex(0, 0, nullptr), group);
+ UpdateGroupState(true);
+
+ QMetaObject::invokeMethod(st, "Edit", Qt::QueuedConnection,
+ Q_ARG(int, 0));
+}
+
+void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)
+{
+ if (indices.count() == 0)
+ return;
+
+ OBSScene scene = GetCurrentScene();
+ QString name = GetNewGroupName();
+
+ QVector item_order;
+
+ for (int i = indices.count() - 1; i >= 0; i--) {
+ obs_sceneitem_t *item = items[indices[i].row()];
+ item_order << item;
+ }
+
+ obs_sceneitem_t *item = obs_scene_insert_group(
+ scene, QT_TO_UTF8(name),
+ item_order.data(), item_order.size());
+ if (!item) {
+ return;
+ }
+
+ for (obs_sceneitem_t *item : item_order)
+ obs_sceneitem_select(item, false);
+
+ int newIdx = indices[0].row();
+
+ beginInsertRows(QModelIndex(), newIdx, newIdx);
+ items.insert(newIdx, item);
+ endInsertRows();
+
+ for (int i = 0; i < indices.size(); i++) {
+ int fromIdx = indices[i].row() + 1;
+ int toIdx = newIdx + i + 1;
+ if (fromIdx != toIdx) {
+ beginMoveRows(QModelIndex(), fromIdx, fromIdx,
+ QModelIndex(), toIdx);
+ MoveItem(items, fromIdx, toIdx);
+ endMoveRows();
+ }
+ }
+
+ hasGroups = true;
+ st->UpdateWidgets(true);
+
+ obs_sceneitem_select(item, true);
+
+ QMetaObject::invokeMethod(st, "Edit", Qt::QueuedConnection,
+ Q_ARG(int, newIdx));
+}
+
+void SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
+{
+ if (indices.count() == 0)
+ return;
+
+ for (int i = indices.count() - 1; i >= 0; i--) {
+ obs_sceneitem_t *item = items[indices[i].row()];
+ obs_sceneitem_group_ungroup(item);
+ }
+
+ SceneChanged();
+}
+
+void SourceTreeModel::ExpandGroup(obs_sceneitem_t *item)
+{
+ int itemIdx = items.indexOf(item);
+ if (itemIdx == -1)
+ return;
+
+ itemIdx++;
+
+ obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
+
+ QVector subItems;
+ obs_scene_enum_items(scene, enumItem, &subItems);
+
+ if (!subItems.size())
+ return;
+
+ beginInsertRows(QModelIndex(), itemIdx, itemIdx + subItems.size() - 1);
+ for (int i = 0; i < subItems.size(); i++)
+ items.insert(i + itemIdx, subItems[i]);
+ endInsertRows();
+
+ st->UpdateWidgets();
+}
+
+void SourceTreeModel::CollapseGroup(obs_sceneitem_t *item)
+{
+ int startIdx = -1;
+ int endIdx = -1;
+
+ obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
+
+ for (int i = 0; i < items.size(); i++) {
+ obs_scene_t *itemScene = obs_sceneitem_get_scene(items[i]);
+
+ if (itemScene == scene) {
+ if (startIdx == -1)
+ startIdx = i;
+ endIdx = i;
+ }
+ }
+
+ if (startIdx == -1)
+ return;
+
+ beginRemoveRows(QModelIndex(), startIdx, endIdx);
+ items.remove(startIdx, endIdx - startIdx + 1);
+ endRemoveRows();
+}
+
+void SourceTreeModel::UpdateGroupState(bool update)
+{
+ bool nowHasGroups = false;
+ for (auto &item : items) {
+ if (obs_sceneitem_is_group(item)) {
+ nowHasGroups = true;
+ break;
+ }
+ }
+
+ if (nowHasGroups != hasGroups) {
+ hasGroups = nowHasGroups;
+ if (update) {
+ st->UpdateWidgets(true);
+ }
+ }
+}
+
+/* ========================================================================= */
+
+SourceTree::SourceTree(QWidget *parent_) : QListView(parent_)
+{
+ SourceTreeModel *stm_ = new SourceTreeModel(this);
+ setModel(stm_);
+ setStyleSheet(QString(
+ "*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}" \
+ "*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}" \
+ "*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}" \
+ "*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}" \
+ "*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}" \
+ "*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}" \
+ "*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}" \
+ "*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
+}
+
+void SourceTree::ResetWidgets()
+{
+ OBSScene scene = GetCurrentScene();
+
+ SourceTreeModel *stm = GetStm();
+ stm->UpdateGroupState(false);
+
+ for (int i = 0; i < stm->items.count(); i++) {
+ QModelIndex index = stm->createIndex(i, 0, nullptr);
+ setIndexWidget(index, new SourceTreeItem(this, stm->items[i]));
+ }
+}
+
+void SourceTree::UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item)
+{
+ setIndexWidget(idx, new SourceTreeItem(this, item));
+}
+
+void SourceTree::UpdateWidgets(bool force)
+{
+ SourceTreeModel *stm = GetStm();
+
+ for (int i = 0; i < stm->items.size(); i++) {
+ obs_sceneitem_t *item = stm->items[i];
+ SourceTreeItem *widget = GetItemWidget(i);
+
+ if (!widget) {
+ UpdateWidget(stm->createIndex(i, 0), item);
+ } else {
+ widget->Update(force);
+ }
+ }
+}
+
+void SourceTree::SelectItem(obs_sceneitem_t *sceneitem, bool select)
+{
+ SourceTreeModel *stm = GetStm();
+ int i = 0;
+
+ for (; i < stm->items.count(); i++) {
+ if (stm->items[i] == sceneitem)
+ break;
+ }
+
+ if (i == stm->items.count())
+ return;
+
+ QModelIndex index = stm->createIndex(i, 0);
+ if (index.isValid())
+ selectionModel()->select(index, select
+ ? QItemSelectionModel::Select
+ : QItemSelectionModel::Deselect);
+}
+
+Q_DECLARE_METATYPE(OBSSceneItem);
+
+void SourceTree::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ QListView::mouseDoubleClickEvent(event);
+}
+
+void SourceTree::dropEvent(QDropEvent *event)
+{
+ if (event->source() != this) {
+ QListView::dropEvent(event);
+ return;
+ }
+
+ OBSScene scene = GetCurrentScene();
+ SourceTreeModel *stm = GetStm();
+ auto &items = stm->items;
+ QModelIndexList indices = selectedIndexes();
+
+ DropIndicatorPosition indicator = dropIndicatorPosition();
+ int row = indexAt(event->pos()).row();
+ bool emptyDrop = row == -1;
+
+ if (emptyDrop) {
+ if (!items.size()) {
+ QListView::dropEvent(event);
+ return;
+ }
+
+ row = items.size() - 1;
+ indicator = QAbstractItemView::BelowItem;
+ }
+
+ /* --------------------------------------- */
+ /* store destination group if moving to a */
+ /* group */
+
+ obs_sceneitem_t *dropItem = items[row]; /* item being dropped on */
+ bool itemIsGroup = obs_sceneitem_is_group(dropItem);
+
+ obs_sceneitem_t *dropGroup = itemIsGroup
+ ? dropItem
+ : obs_sceneitem_get_group(scene, dropItem);
+
+ /* not a group if moving above the group */
+ if (indicator == QAbstractItemView::AboveItem && itemIsGroup)
+ dropGroup = nullptr;
+ if (emptyDrop)
+ dropGroup = nullptr;
+
+ /* --------------------------------------- */
+ /* remember to remove list items if */
+ /* dropping on collapsed group */
+
+ bool dropOnCollapsed = false;
+ if (dropGroup) {
+ obs_data_t *data = obs_sceneitem_get_private_settings(dropGroup);
+ dropOnCollapsed = obs_data_get_bool(data, "collapsed");
+ obs_data_release(data);
+ }
+
+ if (indicator == QAbstractItemView::BelowItem ||
+ indicator == QAbstractItemView::OnItem)
+ row++;
+
+ if (row < 0 || row > stm->items.count()) {
+ QListView::dropEvent(event);
+ return;
+ }
+
+ /* --------------------------------------- */
+ /* determine if any base group is selected */
+
+ bool hasGroups = false;
+ for (int i = 0; i < indices.size(); i++) {
+ obs_sceneitem_t *item = items[indices[i].row()];
+ if (obs_sceneitem_is_group(item)) {
+ hasGroups = true;
+ break;
+ }
+ }
+
+ /* --------------------------------------- */
+ /* if dropping a group, detect if it's */
+ /* below another group */
+
+ obs_sceneitem_t *itemBelow = row == stm->items.count()
+ ? nullptr
+ : stm->items[row];
+ if (hasGroups) {
+ if (!itemBelow ||
+ obs_sceneitem_get_group(scene, itemBelow) != dropGroup) {
+ indicator = QAbstractItemView::BelowItem;
+ dropGroup = nullptr;
+ dropOnCollapsed = false;
+ }
+ }
+
+ /* --------------------------------------- */
+ /* if dropping groups on other groups, */
+ /* disregard as invalid drag/drop */
+
+ if (dropGroup && hasGroups) {
+ QListView::dropEvent(event);
+ return;
+ }
+
+ /* --------------------------------------- */
+ /* if selection includes base group items, */
+ /* include all group sub-items and treat */
+ /* them all as one */
+
+ if (hasGroups) {
+ /* remove sub-items if selected */
+ for (int i = indices.size() - 1; i >= 0; i--) {
+ obs_sceneitem_t *item = items[indices[i].row()];
+ obs_scene_t *itemScene = obs_sceneitem_get_scene(item);
+
+ if (itemScene != scene) {
+ indices.removeAt(i);
+ }
+ }
+
+ /* add all sub-items of selected groups */
+ for (int i = indices.size() - 1; i >= 0; i--) {
+ obs_sceneitem_t *item = items[indices[i].row()];
+
+ if (obs_sceneitem_is_group(item)) {
+ for (int j = items.size() - 1; j >= 0; j--) {
+ obs_sceneitem_t *subitem = items[j];
+ obs_sceneitem_t *subitemGroup =
+ obs_sceneitem_get_group(scene,
+ subitem);
+
+ if (subitemGroup == item) {
+ QModelIndex idx =
+ stm->createIndex(j, 0);
+ indices.insert(i + 1, idx);
+ }
+ }
+ }
+ }
+ }
+
+ /* --------------------------------------- */
+ /* build persistent indices */
+
+ QList persistentIndices;
+ persistentIndices.reserve(indices.count());
+ for (QModelIndex &index : indices)
+ persistentIndices.append(index);
+ std::sort(persistentIndices.begin(), persistentIndices.end());
+
+ /* --------------------------------------- */
+ /* move all items to destination index */
+
+ int r = row;
+ for (auto &persistentIdx : persistentIndices) {
+ int from = persistentIdx.row();
+ int to = r;
+ int itemTo = to;
+
+ if (itemTo > from)
+ itemTo--;
+
+ if (itemTo != from) {
+ stm->beginMoveRows(QModelIndex(), from, from,
+ QModelIndex(), to);
+ MoveItem(items, from, itemTo);
+ stm->endMoveRows();
+ }
+
+ r = persistentIdx.row() + 1;
+ }
+
+ std::sort(persistentIndices.begin(), persistentIndices.end());
+ int firstIdx = persistentIndices.front().row();
+ int lastIdx = persistentIndices.back().row();
+
+ /* --------------------------------------- */
+ /* reorder scene items in back-end */
+
+ QVector orderList;
+ obs_sceneitem_t *lastGroup = nullptr;
+ int insertCollapsedIdx = 0;
+
+ auto insertCollapsed = [&] (obs_sceneitem_t *item)
+ {
+ struct obs_sceneitem_order_info info;
+ info.group = lastGroup;
+ info.item = item;
+
+ orderList.insert(insertCollapsedIdx++, info);
+ };
+
+ using insertCollapsed_t = decltype(insertCollapsed);
+
+ auto preInsertCollapsed = [] (obs_scene_t *, obs_sceneitem_t *item,
+ void *param)
+ {
+ (*reinterpret_cast(param))(item);
+ return true;
+ };
+
+ auto insertLastGroup = [&] ()
+ {
+ obs_data_t *data = obs_sceneitem_get_private_settings(lastGroup);
+ bool collapsed = obs_data_get_bool(data, "collapsed");
+ obs_data_release(data);
+
+ if (collapsed) {
+ insertCollapsedIdx = 0;
+ obs_sceneitem_group_enum_items(
+ lastGroup,
+ preInsertCollapsed,
+ &insertCollapsed);
+ }
+
+ struct obs_sceneitem_order_info info;
+ info.group = nullptr;
+ info.item = lastGroup;
+ orderList.insert(0, info);
+ };
+
+ auto updateScene = [&] ()
+ {
+ struct obs_sceneitem_order_info info;
+
+ for (int i = 0; i < items.size(); i++) {
+ obs_sceneitem_t *item = items[i];
+ obs_sceneitem_t *group;
+
+ if (obs_sceneitem_is_group(item)) {
+ if (lastGroup) {
+ insertLastGroup();
+ }
+ lastGroup = item;
+ continue;
+ }
+
+ if (!hasGroups && i >= firstIdx && i <= lastIdx)
+ group = dropGroup;
+ else
+ group = obs_sceneitem_get_group(scene, item);
+
+ if (lastGroup && lastGroup != group) {
+ insertLastGroup();
+ }
+
+ lastGroup = group;
+
+ info.group = group;
+ info.item = item;
+ orderList.insert(0, info);
+ }
+
+ if (lastGroup) {
+ insertLastGroup();
+ }
+
+ obs_scene_reorder_items2(scene,
+ orderList.data(), orderList.size());
+ };
+
+ using updateScene_t = decltype(updateScene);
+
+ auto preUpdateScene = [] (void *data, obs_scene_t *)
+ {
+ (*reinterpret_cast(data))();
+ };
+
+ ignoreReorder = true;
+ obs_scene_atomic_update(scene, preUpdateScene, &updateScene);
+ ignoreReorder = false;
+
+ /* --------------------------------------- */
+ /* remove items if dropped in to collapsed */
+ /* group */
+
+ if (dropOnCollapsed) {
+ stm->beginRemoveRows(QModelIndex(), firstIdx, lastIdx);
+ items.remove(firstIdx, lastIdx - firstIdx + 1);
+ stm->endRemoveRows();
+ }
+
+ /* --------------------------------------- */
+ /* update widgets and accept event */
+
+ UpdateWidgets(true);
+
+ event->accept();
+ event->setDropAction(Qt::CopyAction);
+
+ QListView::dropEvent(event);
+}
+
+void SourceTree::selectionChanged(
+ const QItemSelection &selected,
+ const QItemSelection &deselected)
+{
+ {
+ SignalBlocker sourcesSignalBlocker(this);
+ SourceTreeModel *stm = GetStm();
+
+ QModelIndexList selectedIdxs = selected.indexes();
+ QModelIndexList deselectedIdxs = deselected.indexes();
+
+ for (int i = 0; i < selectedIdxs.count(); i++) {
+ int idx = selectedIdxs[i].row();
+ obs_sceneitem_select(stm->items[idx], true);
+ }
+
+ for (int i = 0; i < deselectedIdxs.count(); i++) {
+ int idx = deselectedIdxs[i].row();
+ obs_sceneitem_select(stm->items[idx], false);
+ }
+ }
+ QListView::selectionChanged(selected, deselected);
+}
+
+void SourceTree::Edit(int row)
+{
+ SourceTreeModel *stm = GetStm();
+ if (row < 0 || row >= stm->items.count())
+ return;
+
+ QWidget *widget = indexWidget(stm->createIndex(row, 0));
+ SourceTreeItem *itemWidget = reinterpret_cast(widget);
+ itemWidget->EnterEditMode();
+ edit(stm->createIndex(row, 0));
+}
+
+bool SourceTree::MultipleBaseSelected() const
+{
+ SourceTreeModel *stm = GetStm();
+ QModelIndexList selectedIndices = selectedIndexes();
+
+ OBSScene scene = GetCurrentScene();
+
+ if (selectedIndices.size() < 1) {
+ return false;
+ }
+
+ for (auto &idx : selectedIndices) {
+ obs_sceneitem_t *item = stm->items[idx.row()];
+ if (obs_sceneitem_is_group(item)) {
+ return false;
+ }
+
+ obs_scene *itemScene = obs_sceneitem_get_scene(item);
+ if (itemScene != scene) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SourceTree::GroupsSelected() const
+{
+ SourceTreeModel *stm = GetStm();
+ QModelIndexList selectedIndices = selectedIndexes();
+
+ OBSScene scene = GetCurrentScene();
+
+ if (selectedIndices.size() < 1) {
+ return false;
+ }
+
+ for (auto &idx : selectedIndices) {
+ obs_sceneitem_t *item = stm->items[idx.row()];
+ if (!obs_sceneitem_is_group(item)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SourceTree::GroupedItemsSelected() const
+{
+ SourceTreeModel *stm = GetStm();
+ QModelIndexList selectedIndices = selectedIndexes();
+ OBSScene scene = GetCurrentScene();
+
+ if (!selectedIndices.size()) {
+ return false;
+ }
+
+ for (auto &idx : selectedIndices) {
+ obs_sceneitem_t *item = stm->items[idx.row()];
+ obs_scene *itemScene = obs_sceneitem_get_scene(item);
+
+ if (itemScene != scene) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SourceTree::Remove(OBSSceneItem item)
+{
+ OBSBasic *main = reinterpret_cast(App()->GetMainWindow());
+ GetStm()->Remove(item);
+ main->SaveProject();
+
+ if (!main->SavingDisabled()) {
+ obs_scene_t *scene = obs_sceneitem_get_scene(item);
+ obs_source_t *sceneSource = obs_scene_get_source(scene);
+ obs_source_t *itemSource = obs_sceneitem_get_source(item);
+ blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'",
+ obs_source_get_name(itemSource),
+ obs_source_get_id(itemSource),
+ obs_source_get_name(sceneSource));
+ }
+}
+
+void SourceTree::GroupSelectedItems()
+{
+ QModelIndexList indices = selectedIndexes();
+ std::sort(indices.begin(), indices.end());
+ GetStm()->GroupSelectedItems(indices);
+}
+
+void SourceTree::UngroupSelectedGroups()
+{
+ QModelIndexList indices = selectedIndexes();
+ GetStm()->UngroupSelectedGroups(indices);
+}
+
+void SourceTree::AddGroup()
+{
+ GetStm()->AddGroup();
+}
diff --git a/UI/source-tree.hpp b/UI/source-tree.hpp
new file mode 100644
index 0000000..f77ae90
--- /dev/null
+++ b/UI/source-tree.hpp
@@ -0,0 +1,177 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+class QLabel;
+class QCheckBox;
+class QLineEdit;
+class SourceTree;
+class QSpacerItem;
+class QHBoxLayout;
+class LockedCheckBox;
+class VisibilityCheckBox;
+class VisibilityItemWidget;
+
+class SourceTreeSubItemCheckBox : public QCheckBox {
+ Q_OBJECT
+};
+
+class SourceTreeItem : public QWidget {
+ Q_OBJECT
+
+ friend class SourceTree;
+ friend class SourceTreeModel;
+
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+
+ virtual bool eventFilter(QObject *object, QEvent *event) override;
+
+ void Update(bool force);
+
+ enum class Type {
+ Unknown,
+ Item,
+ Group,
+ SubItem,
+ };
+
+ void DisconnectSignals();
+ void ReconnectSignals();
+
+ Type type = Type::Unknown;
+
+public:
+ explicit SourceTreeItem(SourceTree *tree, OBSSceneItem sceneitem);
+
+private:
+ QSpacerItem *spacer = nullptr;
+ QCheckBox *expand = nullptr;
+ VisibilityCheckBox *vis = nullptr;
+ LockedCheckBox *lock = nullptr;
+ QHBoxLayout *boxLayout = nullptr;
+ QLabel *label = nullptr;
+
+ QLineEdit *editor = nullptr;
+
+ SourceTree *tree;
+ OBSSceneItem sceneitem;
+ OBSSignal sceneRemoveSignal;
+ OBSSignal itemRemoveSignal;
+ OBSSignal groupReorderSignal;
+ OBSSignal deselectSignal;
+ OBSSignal visibleSignal;
+ OBSSignal renameSignal;
+ OBSSignal removeSignal;
+
+ virtual void paintEvent(QPaintEvent* event) override;
+
+private slots:
+ void EnterEditMode();
+ void ExitEditMode(bool save);
+
+ void VisibilityChanged(bool visible);
+ void Renamed(const QString &name);
+
+ void ExpandClicked(bool checked);
+
+ void Deselect();
+};
+
+class SourceTreeModel : public QAbstractListModel {
+ Q_OBJECT
+
+ friend class SourceTree;
+ friend class SourceTreeItem;
+
+ SourceTree *st;
+ QVector items;
+ bool hasGroups = false;
+
+ static void OBSFrontendEvent(enum obs_frontend_event event, void *ptr);
+ void Clear();
+ void SceneChanged();
+ void ReorderItems();
+
+ void Add(obs_sceneitem_t *item);
+ void Remove(obs_sceneitem_t *item);
+ OBSSceneItem Get(int idx);
+ QString GetNewGroupName();
+ void AddGroup();
+
+ void GroupSelectedItems(QModelIndexList &indices);
+ void UngroupSelectedGroups(QModelIndexList &indices);
+
+ void ExpandGroup(obs_sceneitem_t *item);
+ void CollapseGroup(obs_sceneitem_t *item);
+
+ void UpdateGroupState(bool update);
+
+public:
+ explicit SourceTreeModel(SourceTree *st);
+ ~SourceTreeModel();
+
+ virtual int rowCount(const QModelIndex &parent) const override;
+ virtual QVariant data(const QModelIndex &index, int role) const override;
+
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
+ virtual Qt::DropActions supportedDropActions() const override;
+};
+
+class SourceTree : public QListView {
+ Q_OBJECT
+
+ bool ignoreReorder = false;
+
+ friend class SourceTreeModel;
+ friend class SourceTreeItem;
+
+ void ResetWidgets();
+ void UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item);
+ void UpdateWidgets(bool force = false);
+
+ inline SourceTreeModel *GetStm() const
+ {
+ return reinterpret_cast(model());
+ }
+
+public:
+ inline SourceTreeItem *GetItemWidget(int idx)
+ {
+ QWidget *widget = indexWidget(GetStm()->createIndex(idx, 0));
+ return reinterpret_cast(widget);
+ }
+
+ explicit SourceTree(QWidget *parent = nullptr);
+
+ inline bool IgnoreReorder() const {return ignoreReorder;}
+ inline void Clear() {GetStm()->Clear();}
+
+ inline void Add(obs_sceneitem_t *item) {GetStm()->Add(item);}
+ inline OBSSceneItem Get(int idx) {return GetStm()->Get(idx);}
+ inline QString GetNewGroupName() {return GetStm()->GetNewGroupName();}
+
+ void SelectItem(obs_sceneitem_t *sceneitem, bool select);
+
+ bool MultipleBaseSelected() const;
+ bool GroupsSelected() const;
+ bool GroupedItemsSelected() const;
+
+public slots:
+ inline void ReorderItems() {GetStm()->ReorderItems();}
+ void Remove(OBSSceneItem item);
+ void GroupSelectedItems();
+ void UngroupSelectedGroups();
+ void AddGroup();
+ void Edit(int idx);
+
+protected:
+ virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
+ virtual void dropEvent(QDropEvent *event) override;
+
+ virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
+};
diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp
index 47019cc..80fbe1f 100644
--- a/UI/volume-control.cpp
+++ b/UI/volume-control.cpp
@@ -3,24 +3,17 @@
#include "obs-app.hpp"
#include "mute-checkbox.hpp"
#include "slider-absoluteset-style.hpp"
-#include
-#include
-#include
#include
#include
-#include
#include
-#include
#include
#include
#include
-#include
-#include
-#include
+#include
using namespace std;
-#define CLAMP(x, min, max) ((x) < min ? min : ((x) > max ? max : (x)))
+#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
QWeakPointer VolumeMeter::updateTimer;
@@ -33,9 +26,9 @@ void VolControl::OBSVolumeChanged(void *data, float db)
}
void VolControl::OBSVolumeLevel(void *data,
- const float magnitude[MAX_AUDIO_CHANNELS],
- const float peak[MAX_AUDIO_CHANNELS],
- const float inputPeak[MAX_AUDIO_CHANNELS])
+ const float magnitude[MAX_AUDIO_CHANNELS],
+ const float peak[MAX_AUDIO_CHANNELS],
+ const float inputPeak[MAX_AUDIO_CHANNELS])
{
VolControl *volControl = static_cast(data);
@@ -114,55 +107,24 @@ void VolControl::SetMeterDecayRate(qreal q)
volMeter->setPeakDecayRate(q);
}
-VolControl::VolControl(OBSSource source_, bool showConfig)
- : source (source_),
- levelTotal (0.0f),
- levelCount (0.0f),
- obs_fader (obs_fader_create(OBS_FADER_CUBIC)),
- obs_volmeter (obs_volmeter_create(OBS_FADER_LOG))
+void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
{
- QHBoxLayout *volLayout = new QHBoxLayout();
- QVBoxLayout *mainLayout = new QVBoxLayout();
- QHBoxLayout *textLayout = new QHBoxLayout();
- QHBoxLayout *botLayout = new QHBoxLayout();
+ volMeter->setPeakMeterType(peakMeterType);
+}
+VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
+ : source (std::move(source_)),
+ levelTotal (0.0f),
+ levelCount (0.0f),
+ obs_fader (obs_fader_create(OBS_FADER_CUBIC)),
+ obs_volmeter (obs_volmeter_create(OBS_FADER_LOG)),
+ vertical (vertical)
+{
nameLabel = new QLabel();
volLabel = new QLabel();
- volMeter = new VolumeMeter(0, obs_volmeter);
mute = new MuteCheckBox();
- slider = new QSlider(Qt::Horizontal);
-
- QFont font = nameLabel->font();
- font.setPointSize(font.pointSize()-1);
-
QString sourceName = obs_source_get_name(source);
-
- nameLabel->setText(sourceName);
- nameLabel->setFont(font);
- volLabel->setFont(font);
- slider->setMinimum(0);
- slider->setMaximum(100);
-
-// slider->setMaximumHeight(13);
-
- textLayout->setContentsMargins(0, 0, 0, 0);
- textLayout->addWidget(nameLabel);
- textLayout->addWidget(volLabel);
- textLayout->setAlignment(nameLabel, Qt::AlignLeft);
- textLayout->setAlignment(volLabel, Qt::AlignRight);
-
- bool muted = obs_source_muted(source);
- mute->setChecked(muted);
- mute->setAccessibleName(
- QTStr("VolControl.Mute").arg(sourceName));
-
- volLayout->addWidget(slider);
- volLayout->addWidget(mute);
- volLayout->setSpacing(5);
-
- botLayout->setContentsMargins(0, 0, 0, 0);
- botLayout->setSpacing(0);
- botLayout->addLayout(volLayout);
+ setObjectName(sourceName);
if (showConfig) {
config = new QPushButton(this);
@@ -178,18 +140,99 @@ VolControl::VolControl(OBSSource source_, bool showConfig)
connect(config, &QAbstractButton::clicked,
this, &VolControl::EmitConfigClicked);
-
- botLayout->addWidget(config);
}
+ QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(4, 4, 4, 4);
mainLayout->setSpacing(2);
- mainLayout->addItem(textLayout);
- mainLayout->addWidget(volMeter);
- mainLayout->addItem(botLayout);
+
+ if (vertical) {
+ QHBoxLayout *nameLayout = new QHBoxLayout;
+ QHBoxLayout *controlLayout = new QHBoxLayout;
+ QHBoxLayout *volLayout = new QHBoxLayout;
+ QHBoxLayout *meterLayout = new QHBoxLayout;
+
+ volMeter = new VolumeMeter(nullptr, obs_volmeter, true);
+ slider = new QSlider(Qt::Vertical);
+
+ nameLayout->setAlignment(Qt::AlignCenter);
+ meterLayout->setAlignment(Qt::AlignCenter);
+ controlLayout->setAlignment(Qt::AlignCenter);
+ volLayout->setAlignment(Qt::AlignCenter);
+
+ nameLayout->setContentsMargins(0, 0, 0, 0);
+ nameLayout->setSpacing(0);
+ nameLayout->addWidget(nameLabel);
+
+ controlLayout->setContentsMargins(0, 0, 0, 0);
+ controlLayout->setSpacing(0);
+
+ if (showConfig)
+ controlLayout->addWidget(config);
+
+ controlLayout->addItem(new QSpacerItem(3, 0));
+ // Add Headphone (audio monitoring) widget here
+ controlLayout->addWidget(mute);
+
+ meterLayout->setContentsMargins(0, 0, 0, 0);
+ meterLayout->setSpacing(0);
+ meterLayout->addWidget(volMeter);
+ meterLayout->addWidget(slider);
+
+ volLayout->setContentsMargins(0, 0, 0, 0);
+ volLayout->setSpacing(0);
+ volLayout->addWidget(volLabel);
+
+ mainLayout->addItem(nameLayout);
+ mainLayout->addItem(volLayout);
+ mainLayout->addItem(meterLayout);
+ mainLayout->addItem(controlLayout);
+
+ setMaximumWidth(110);
+ } else {
+ QHBoxLayout *volLayout = new QHBoxLayout;
+ QHBoxLayout *textLayout = new QHBoxLayout;
+ QHBoxLayout *botLayout = new QHBoxLayout;
+
+ volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
+ slider = new QSlider(Qt::Horizontal);
+
+ textLayout->setContentsMargins(0, 0, 0, 0);
+ textLayout->addWidget(nameLabel);
+ textLayout->addWidget(volLabel);
+ textLayout->setAlignment(nameLabel, Qt::AlignLeft);
+ textLayout->setAlignment(volLabel, Qt::AlignRight);
+
+ volLayout->addWidget(slider);
+ volLayout->addWidget(mute);
+ volLayout->setSpacing(5);
+
+ botLayout->setContentsMargins(0, 0, 0, 0);
+ botLayout->setSpacing(0);
+ botLayout->addLayout(volLayout);
+
+ if (showConfig)
+ botLayout->addWidget(config);
+
+ mainLayout->addItem(textLayout);
+ mainLayout->addWidget(volMeter);
+ mainLayout->addItem(botLayout);
+ }
setLayout(mainLayout);
+ QFont font = nameLabel->font();
+ font.setPointSize(font.pointSize()-1);
+
+ nameLabel->setText(sourceName);
+ nameLabel->setFont(font);
+ volLabel->setFont(font);
+ slider->setMinimum(0);
+ slider->setMaximum(100);
+
+ bool muted = obs_source_muted(source);
+ mute->setChecked(muted);
+ mute->setAccessibleName(QTStr("VolControl.Mute").arg(sourceName));
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
@@ -204,7 +247,17 @@ VolControl::VolControl(OBSSource source_, bool showConfig)
obs_fader_attach_source(obs_fader, source);
obs_volmeter_attach_source(obs_volmeter, source);
- slider->setStyle(new SliderAbsoluteSetStyle(slider->style()));
+ 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();
@@ -229,7 +282,7 @@ QColor VolumeMeter::getBackgroundNominalColor() const
void VolumeMeter::setBackgroundNominalColor(QColor c)
{
- backgroundNominalColor = c;
+ backgroundNominalColor = std::move(c);
}
QColor VolumeMeter::getBackgroundWarningColor() const
@@ -239,7 +292,7 @@ QColor VolumeMeter::getBackgroundWarningColor() const
void VolumeMeter::setBackgroundWarningColor(QColor c)
{
- backgroundWarningColor = c;
+ backgroundWarningColor = std::move(c);
}
QColor VolumeMeter::getBackgroundErrorColor() const
@@ -249,7 +302,7 @@ QColor VolumeMeter::getBackgroundErrorColor() const
void VolumeMeter::setBackgroundErrorColor(QColor c)
{
- backgroundErrorColor = c;
+ backgroundErrorColor = std::move(c);
}
QColor VolumeMeter::getForegroundNominalColor() const
@@ -259,7 +312,7 @@ QColor VolumeMeter::getForegroundNominalColor() const
void VolumeMeter::setForegroundNominalColor(QColor c)
{
- foregroundNominalColor = c;
+ foregroundNominalColor = std::move(c);
}
QColor VolumeMeter::getForegroundWarningColor() const
@@ -269,7 +322,7 @@ QColor VolumeMeter::getForegroundWarningColor() const
void VolumeMeter::setForegroundWarningColor(QColor c)
{
- foregroundWarningColor = c;
+ foregroundWarningColor = std::move(c);
}
QColor VolumeMeter::getForegroundErrorColor() const
@@ -279,7 +332,7 @@ QColor VolumeMeter::getForegroundErrorColor() const
void VolumeMeter::setForegroundErrorColor(QColor c)
{
- foregroundErrorColor = c;
+ foregroundErrorColor = std::move(c);
}
QColor VolumeMeter::getClipColor() const
@@ -289,7 +342,7 @@ QColor VolumeMeter::getClipColor() const
void VolumeMeter::setClipColor(QColor c)
{
- clipColor = c;
+ clipColor = std::move(c);
}
QColor VolumeMeter::getMagnitudeColor() const
@@ -299,7 +352,7 @@ QColor VolumeMeter::getMagnitudeColor() const
void VolumeMeter::setMagnitudeColor(QColor c)
{
- magnitudeColor = c;
+ magnitudeColor = std::move(c);
}
QColor VolumeMeter::getMajorTickColor() const
@@ -309,7 +362,7 @@ QColor VolumeMeter::getMajorTickColor() const
void VolumeMeter::setMajorTickColor(QColor c)
{
- majorTickColor = c;
+ majorTickColor = std::move(c);
}
QColor VolumeMeter::getMinorTickColor() const
@@ -319,7 +372,7 @@ QColor VolumeMeter::getMinorTickColor() const
void VolumeMeter::setMinorTickColor(QColor c)
{
- minorTickColor = c;
+ minorTickColor = std::move(c);
}
qreal VolumeMeter::getMinimumLevel() const
@@ -412,8 +465,43 @@ void VolumeMeter::setInputPeakHoldDuration(qreal v)
inputPeakHoldDuration = v;
}
-VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter)
- : QWidget(parent), obs_volmeter(obs_volmeter)
+void VolumeMeter::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
+{
+ obs_volmeter_set_peak_meter_type(obs_volmeter, peakMeterType);
+ switch (peakMeterType) {
+ case TRUE_PEAK_METER:
+ // For true-peak meters EBU has defined the Permitted Maximum,
+ // taking into account the accuracy of the meter and further
+ // processing required by lossy audio compression.
+ //
+ // The alignment level was not specified, but I've adjusted
+ // it compared to a sample-peak meter. Incidently Youtube
+ // uses this new Alignment Level as the maximum integrated
+ // loudness of a video.
+ //
+ // * Permitted Maximum Level (PML) = -2.0 dBTP
+ // * Alignment Level (AL) = -13 dBTP
+ setErrorLevel(-2.0);
+ setWarningLevel(-13.0);
+ break;
+
+ case SAMPLE_PEAK_METER:
+ default:
+ // For a sample Peak Meter EBU has the following level
+ // definitions, taking into account inaccuracies of this meter:
+ //
+ // * Permitted Maximum Level (PML) = -9.0 dBFS
+ // * Alignment Level (AL) = -20.0 dBFS
+ setErrorLevel(-9.0);
+ setWarningLevel(-20.0);
+ break;
+ }
+}
+
+VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
+ bool vertical)
+ : QWidget(parent), obs_volmeter(obs_volmeter),
+ vertical(vertical)
{
// Use a font that can be rendered small.
tickFont = QFont("Arial");
@@ -454,12 +542,12 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter)
VolumeMeter::~VolumeMeter()
{
updateTimerRef->RemoveVolControl(this);
+ delete tickPaintCache;
}
-void VolumeMeter::setLevels(
- const float magnitude[MAX_AUDIO_CHANNELS],
- const float peak[MAX_AUDIO_CHANNELS],
- const float inputPeak[MAX_AUDIO_CHANNELS])
+void VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
+ const float peak[MAX_AUDIO_CHANNELS],
+ const float inputPeak[MAX_AUDIO_CHANNELS])
{
uint64_t ts = os_gettime_ns();
QMutexLocker locker(&dataMutex);
@@ -502,9 +590,12 @@ inline void VolumeMeter::handleChannelCofigurationChange()
if (displayNrAudioChannels != currentNrAudioChannels) {
displayNrAudioChannels = currentNrAudioChannels;
- // Make room for 3 pixels high meter, with one pixel between
- // each. Then 9 pixels below it for ticks and numbers.
- setMinimumSize(130, displayNrAudioChannels * 4 + 8);
+ // Make room for 3 pixels meter, with one pixel between each.
+ // Then 9/13 pixels for ticks and numbers.
+ if (vertical)
+ setMinimumSize(displayNrAudioChannels * 4 + 14, 130);
+ else
+ setMinimumSize(130, displayNrAudioChannels * 4 + 8);
resetLevels();
}
@@ -512,7 +603,7 @@ inline void VolumeMeter::handleChannelCofigurationChange()
inline bool VolumeMeter::detectIdle(uint64_t ts)
{
- float timeSinceLastUpdate = (ts - currentLastUpdateTime) * 0.000000001;
+ double timeSinceLastUpdate = (ts - currentLastUpdateTime) * 0.000000001;
if (timeSinceLastUpdate > 0.5) {
resetLevels();
return true;
@@ -522,7 +613,7 @@ inline bool VolumeMeter::detectIdle(uint64_t ts)
}
inline void VolumeMeter::calculateBallisticsForChannel(int channelNr,
- uint64_t ts, qreal timeSinceLastRedraw)
+ uint64_t ts, qreal timeSinceLastRedraw)
{
if (currentPeak[channelNr] >= displayPeak[channelNr] ||
isnan(displayPeak[channelNr])) {
@@ -532,7 +623,7 @@ inline void VolumeMeter::calculateBallisticsForChannel(int channelNr,
// Decay of peak is 40 dB / 1.7 seconds for Fast Profile
// 20 dB / 1.7 seconds for Medium Profile (Type I PPM)
// 24 dB / 2.8 seconds for Slow Profile (Type II PPM)
- qreal decay = peakDecayRate * timeSinceLastRedraw;
+ float decay = float(peakDecayRate * timeSinceLastRedraw);
displayPeak[channelNr] = CLAMP(displayPeak[channelNr] - decay,
currentPeak[channelNr], 0);
}
@@ -576,53 +667,52 @@ inline void VolumeMeter::calculateBallisticsForChannel(int channelNr,
if (!isfinite(displayMagnitude[channelNr])) {
// The statements in the else-leg do not work with
// NaN and infinite displayMagnitude.
- displayMagnitude[channelNr] =
- currentMagnitude[channelNr];
+ displayMagnitude[channelNr] = currentMagnitude[channelNr];
} else {
// A VU meter will integrate to the new value to 99% in 300 ms.
// The calculation here is very simplified and is more accurate
// with higher frame-rate.
- qreal attack = (currentMagnitude[channelNr] -
+ float attack = float((currentMagnitude[channelNr] -
displayMagnitude[channelNr]) *
(timeSinceLastRedraw /
- magnitudeIntegrationTime) * 0.99;
- displayMagnitude[channelNr] = CLAMP(
- displayMagnitude[channelNr] + attack,
- minimumLevel, 0);
+ magnitudeIntegrationTime) * 0.99);
+ displayMagnitude[channelNr] = CLAMP(displayMagnitude[channelNr]
+ + attack, (float)minimumLevel, 0);
}
}
inline void VolumeMeter::calculateBallistics(uint64_t ts,
- qreal timeSinceLastRedraw)
+ qreal timeSinceLastRedraw)
{
QMutexLocker locker(&dataMutex);
- for (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++) {
+ for (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++)
calculateBallisticsForChannel(channelNr, ts,
- timeSinceLastRedraw);
- }
+ timeSinceLastRedraw);
}
-void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y,
- int width, int height, float peakHold)
+void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, int width,
+ int height, float peakHold)
{
QMutexLocker locker(&dataMutex);
+ QColor color;
- if (peakHold < minimumInputLevel) {
- painter.fillRect(x, y, width, height, backgroundNominalColor);
- } else if (peakHold < warningLevel) {
- painter.fillRect(x, y, width, height, foregroundNominalColor);
- } else if (peakHold < errorLevel) {
- painter.fillRect(x, y, width, height, foregroundWarningColor);
- } else if (peakHold <= clipLevel) {
- painter.fillRect(x, y, width, height, foregroundErrorColor);
- } else {
- painter.fillRect(x, y, width, height, clipColor);
- }
+ if (peakHold < minimumInputLevel)
+ color = backgroundNominalColor;
+ else if (peakHold < warningLevel)
+ color = foregroundNominalColor;
+ else if (peakHold < errorLevel)
+ color = foregroundWarningColor;
+ else if (peakHold <= clipLevel)
+ color = foregroundErrorColor;
+ else
+ color = clipColor;
+
+ painter.fillRect(x, y, width, height, color);
}
-void VolumeMeter::paintTicks(QPainter &painter, int x, int y,
- int width, int height)
+void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width,
+ int height)
{
qreal scale = width / minimumLevel;
@@ -631,184 +721,257 @@ void VolumeMeter::paintTicks(QPainter &painter, int x, int y,
// Draw major tick lines and numeric indicators.
for (int i = 0; i >= minimumLevel; i-= 5) {
- int position = x + width - (i * scale) - 1;
+ int position = int(x + width - (i * scale) - 1);
QString str = QString::number(i);
- if (i == 0 || i == -5) {
+ if (i == 0 || i == -5)
painter.drawText(position - 3, height, str);
- } else {
+ else
painter.drawText(position - 5, height, str);
- }
painter.drawLine(position, y, position, y + 2);
}
// Draw minor tick lines.
painter.setPen(minorTickColor);
for (int i = 0; i >= minimumLevel; i--) {
- int position = x + width - (i * scale) - 1;
-
- if (i % 5 != 0) {
+ int position = int(x + width - (i * scale) - 1);
+ if (i % 5 != 0)
painter.drawLine(position, y, position, y + 1);
- }
}
}
-void VolumeMeter::paintMeter(QPainter &painter, int x, int y,
- int width, int height, float magnitude, float peak, float peakHold)
+void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height)
+{
+ qreal scale = height / minimumLevel;
+
+ painter.setFont(tickFont);
+ painter.setPen(majorTickColor);
+
+ // Draw major tick lines and numeric indicators.
+ for (int i = 0; i >= minimumLevel; i-= 5) {
+ int position = y + int((i * scale) - 1);
+ QString str = QString::number(i);
+
+ if (i == 0)
+ painter.drawText(x + 5, position + 4, str);
+ else if (i == -60)
+ painter.drawText(x + 4, position, str);
+ else
+ painter.drawText(x + 4, position + 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) - 1);
+ if (i % 5 != 0)
+ painter.drawLine(x, position, x + 1, position);
+ }
+}
+
+#define CLIP_FLASH_DURATION_MS 1000
+
+void VolumeMeter::ClipEnding()
+{
+ clipping = false;
+}
+
+void VolumeMeter::paintHMeter(QPainter &painter, int x, int y, int width,
+ int height, float magnitude, float peak, float peakHold)
{
qreal scale = width / minimumLevel;
QMutexLocker locker(&dataMutex);
int minimumPosition = x + 0;
int maximumPosition = x + width;
- int magnitudePosition = x + width - (magnitude * scale);
- int peakPosition = x + width - (peak * scale);
- int peakHoldPosition = x + width - (peakHold * scale);
- int warningPosition = x + width - (warningLevel * scale);
- int errorPosition = x + width - (errorLevel * scale);
+ int magnitudePosition = int(x + width - (magnitude * scale));
+ int peakPosition = int(x + width - (peak * scale));
+ int peakHoldPosition = int(x + width - (peakHold * scale));
+ int warningPosition = int(x + width - (warningLevel * scale));
+ int errorPosition = int(x + width - (errorLevel * scale));
int nominalLength = warningPosition - minimumPosition;
int warningLength = errorPosition - warningPosition;
int errorLength = maximumPosition - errorPosition;
locker.unlock();
+ if (clipping) {
+ peakPosition = maximumPosition;
+ }
+
if (peakPosition < minimumPosition) {
- painter.fillRect(
- minimumPosition, y,
- nominalLength, height,
- backgroundNominalColor);
- painter.fillRect(
- warningPosition, y,
- warningLength, height,
- backgroundWarningColor);
- painter.fillRect(
- errorPosition, y,
- errorLength, height,
- backgroundErrorColor);
-
+ painter.fillRect(minimumPosition, y, nominalLength, height,
+ backgroundNominalColor);
+ painter.fillRect(warningPosition, y, warningLength, height,
+ backgroundWarningColor);
+ painter.fillRect(errorPosition, y, errorLength, height,
+ backgroundErrorColor);
} else if (peakPosition < warningPosition) {
- painter.fillRect(
- minimumPosition, y,
- peakPosition - minimumPosition, height,
- foregroundNominalColor);
- painter.fillRect(
- peakPosition, y,
- warningPosition - peakPosition, height,
- backgroundNominalColor);
- painter.fillRect(
- warningPosition, y,
- warningLength, height,
- backgroundWarningColor);
- painter.fillRect(errorPosition, y,
- errorLength, height,
- backgroundErrorColor);
-
+ painter.fillRect(minimumPosition, y, peakPosition -
+ minimumPosition, height,
+ foregroundNominalColor);
+ painter.fillRect(peakPosition, y, warningPosition -
+ peakPosition, height, backgroundNominalColor);
+ painter.fillRect(warningPosition, y, warningLength, height,
+ backgroundWarningColor);
+ painter.fillRect(errorPosition, y, errorLength, height,
+ backgroundErrorColor);
} else if (peakPosition < errorPosition) {
- painter.fillRect(
- minimumPosition, y,
- nominalLength, height,
- foregroundNominalColor);
- painter.fillRect(
- warningPosition, y,
- peakPosition - warningPosition, height,
- foregroundWarningColor);
- painter.fillRect(
- peakPosition, y,
- errorPosition - peakPosition, height,
- backgroundWarningColor);
- painter.fillRect(
- errorPosition, y,
- errorLength, height,
- backgroundErrorColor);
-
+ painter.fillRect(minimumPosition, y, nominalLength, height,
+ foregroundNominalColor);
+ painter.fillRect(warningPosition, y,
+ peakPosition - warningPosition, height,
+ foregroundWarningColor);
+ painter.fillRect(peakPosition, y, errorPosition -
+ peakPosition, height, backgroundWarningColor);
+ painter.fillRect(errorPosition, y, errorLength, height,
+ backgroundErrorColor);
} else if (peakPosition < maximumPosition) {
- painter.fillRect(
- minimumPosition, y,
- nominalLength, height,
- foregroundNominalColor);
- painter.fillRect(
- warningPosition, y,
- warningLength, height,
- foregroundWarningColor);
- painter.fillRect(
- errorPosition, y,
- peakPosition - errorPosition, height,
- foregroundErrorColor);
- painter.fillRect(
- peakPosition, y,
- maximumPosition - peakPosition, height,
- backgroundErrorColor);
-
+ painter.fillRect(minimumPosition, y, nominalLength, height,
+ foregroundNominalColor);
+ painter.fillRect(warningPosition, y, warningLength, height,
+ foregroundWarningColor);
+ painter.fillRect(errorPosition, y, peakPosition - errorPosition,
+ height, foregroundErrorColor);
+ painter.fillRect(peakPosition, y,
+ maximumPosition - peakPosition, height,
+ backgroundErrorColor);
} else {
- qreal end = errorLength + warningLength + nominalLength;
- painter.fillRect(
- minimumPosition, y,
- end, height,
- QBrush(foregroundErrorColor));
+ if (!clipping) {
+ QTimer::singleShot(CLIP_FLASH_DURATION_MS, this,
+ SLOT(ClipEnding()));
+ clipping = true;
+ }
+
+ int end = errorLength + warningLength + nominalLength;
+ painter.fillRect(minimumPosition, y, end, height,
+ QBrush(foregroundErrorColor));
}
- if (peakHoldPosition - 3 < minimumPosition) {
- // Peak-hold below minimum, no drawing.
+ if (peakHoldPosition - 3 < minimumPosition)
+ ;// Peak-hold below minimum, no drawing.
+ else if (peakHoldPosition < warningPosition)
+ painter.fillRect(peakHoldPosition - 3, y, 3, height,
+ foregroundNominalColor);
+ else if (peakHoldPosition < errorPosition)
+ painter.fillRect(peakHoldPosition - 3, y, 3, height,
+ foregroundWarningColor);
+ else
+ painter.fillRect(peakHoldPosition - 3, y, 3, height,
+ foregroundErrorColor);
- } else if (peakHoldPosition < warningPosition) {
- painter.fillRect(
- peakHoldPosition - 3, y,
- 3, height,
- foregroundNominalColor);
+ if (magnitudePosition - 3 >= minimumPosition)
+ painter.fillRect(magnitudePosition - 3, y, 3, height,
+ magnitudeColor);
+}
- } else if (peakHoldPosition < errorPosition) {
- painter.fillRect(
- peakHoldPosition - 3, y,
- 3, height,
- foregroundWarningColor);
+void VolumeMeter::paintVMeter(QPainter &painter, int x, int y, int width,
+ int height, float magnitude, float peak, float peakHold)
+{
+ qreal scale = height / minimumLevel;
- } else {
- painter.fillRect(
- peakHoldPosition - 3, y,
- 3, height,
- foregroundErrorColor);
+ QMutexLocker locker(&dataMutex);
+ int minimumPosition = y + 0;
+ int maximumPosition = y + height;
+ int magnitudePosition = int(y + height - (magnitude * scale));
+ int peakPosition = int(y + height - (peak * scale));
+ int peakHoldPosition = int(y + height - (peakHold * scale));
+ int warningPosition = int(y + height - (warningLevel * scale));
+ int errorPosition = int(y + height - (errorLevel * scale));
+
+ int nominalLength = warningPosition - minimumPosition;
+ int warningLength = errorPosition - warningPosition;
+ int errorLength = maximumPosition - errorPosition;
+ locker.unlock();
+
+ if (clipping) {
+ peakPosition = maximumPosition;
}
- if (magnitudePosition - 3 < minimumPosition) {
- // Magnitude below minimum, no drawing.
-
- } else if (magnitudePosition < warningPosition) {
- painter.fillRect(
- magnitudePosition - 3, y,
- 3, height,
- magnitudeColor);
-
- } else if (magnitudePosition < errorPosition) {
- painter.fillRect(
- magnitudePosition - 3, y,
- 3, height,
- magnitudeColor);
-
+ if (peakPosition < minimumPosition) {
+ painter.fillRect(x, minimumPosition, width, nominalLength,
+ backgroundNominalColor);
+ painter.fillRect(x, warningPosition, width, warningLength,
+ backgroundWarningColor);
+ painter.fillRect(x, errorPosition, width, errorLength,
+ backgroundErrorColor);
+ } else if (peakPosition < warningPosition) {
+ painter.fillRect(x, minimumPosition, width, peakPosition -
+ minimumPosition, foregroundNominalColor);
+ painter.fillRect(x, peakPosition, width, warningPosition -
+ peakPosition, backgroundNominalColor);
+ painter.fillRect(x, warningPosition, width, warningLength,
+ backgroundWarningColor);
+ painter.fillRect(x, errorPosition, width, errorLength,
+ backgroundErrorColor);
+ } else if (peakPosition < errorPosition) {
+ painter.fillRect(x,minimumPosition, width, nominalLength,
+ foregroundNominalColor);
+ painter.fillRect(x, warningPosition, width, peakPosition -
+ warningPosition, foregroundWarningColor);
+ painter.fillRect(x, peakPosition, width, errorPosition -
+ peakPosition, backgroundWarningColor);
+ painter.fillRect(x, errorPosition, width, errorLength,
+ backgroundErrorColor);
+ } else if (peakPosition < maximumPosition) {
+ painter.fillRect(x, minimumPosition, width, nominalLength,
+ foregroundNominalColor);
+ painter.fillRect(x, warningPosition, width, warningLength,
+ foregroundWarningColor);
+ painter.fillRect(x, errorPosition, width, peakPosition -
+ errorPosition, foregroundErrorColor);
+ painter.fillRect(x, peakPosition, width, maximumPosition -
+ peakPosition, backgroundErrorColor);
} else {
- painter.fillRect(
- magnitudePosition - 3, y,
- 3, height,
- magnitudeColor);
+ if (!clipping) {
+ QTimer::singleShot(CLIP_FLASH_DURATION_MS, this,
+ SLOT(ClipEnding()));
+ clipping = true;
+ }
+
+ int end = errorLength + warningLength + nominalLength;
+ painter.fillRect(x, minimumPosition, width, end,
+ QBrush(foregroundErrorColor));
}
+
+ if (peakHoldPosition - 3 < minimumPosition)
+ ;// Peak-hold below minimum, no drawing.
+ else if (peakHoldPosition < warningPosition)
+ painter.fillRect(x, peakHoldPosition - 3, width, 3,
+ foregroundNominalColor);
+ else if (peakHoldPosition < errorPosition)
+ painter.fillRect(x, peakHoldPosition - 3, width, 3,
+ foregroundWarningColor);
+ else
+ painter.fillRect(x, peakHoldPosition - 3, width, 3,
+ foregroundErrorColor);
+
+ if (magnitudePosition - 3 >= minimumPosition)
+ painter.fillRect(x, magnitudePosition - 3, width, 3,
+ magnitudeColor);
}
void VolumeMeter::paintEvent(QPaintEvent *event)
{
- UNUSED_PARAMETER(event);
-
uint64_t ts = os_gettime_ns();
qreal timeSinceLastRedraw = (ts - lastRedrawTime) * 0.000000001;
- int width = size().width();
- int height = size().height();
+ const QRect rect = event->region().boundingRect();
+ int width = rect.width();
+ int height = rect.height();
handleChannelCofigurationChange();
calculateBallistics(ts, timeSinceLastRedraw);
bool idle = detectIdle(ts);
// Draw the ticks in a off-screen buffer when the widget changes size.
- QSize tickPaintCacheSize = QSize(width, 9);
- if (tickPaintCache == NULL ||
+ QSize tickPaintCacheSize;
+ if (vertical)
+ tickPaintCacheSize = QSize(14, height);
+ else
+ tickPaintCacheSize = QSize(width, 9);
+ if (tickPaintCache == nullptr ||
tickPaintCache->size() != tickPaintCacheSize) {
delete tickPaintCache;
tickPaintCache = new QPixmap(tickPaintCacheSize);
@@ -817,30 +980,56 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
tickPaintCache->fill(clearColor);
QPainter tickPainter(tickPaintCache);
- paintTicks(tickPainter, 6, 0, tickPaintCacheSize.width() - 6,
- tickPaintCacheSize.height());
+ if (vertical) {
+ tickPainter.translate(0, height);
+ tickPainter.scale(1, -1);
+ paintVTicks(tickPainter, 0, 11,
+ tickPaintCacheSize.height() - 11);
+ } else {
+ paintHTicks(tickPainter, 6, 0,
+ tickPaintCacheSize.width() - 6,
+ tickPaintCacheSize.height());
+ }
tickPainter.end();
}
// Actual painting of the widget starts here.
QPainter painter(this);
- painter.drawPixmap(0, height - 9, *tickPaintCache);
+ if (vertical) {
+ // Invert the Y axis to ease the math
+ painter.translate(0, height);
+ painter.scale(1, -1);
+ painter.drawPixmap(displayNrAudioChannels * 4 - 1, 7,
+ *tickPaintCache);
+ } else {
+ painter.drawPixmap(0, height - 9, *tickPaintCache);
+ }
for (int channelNr = 0; channelNr < displayNrAudioChannels;
channelNr++) {
- paintMeter(painter,
- 5, channelNr * 4, width - 5, 3,
- displayMagnitude[channelNr], displayPeak[channelNr],
- displayPeakHold[channelNr]);
+ if (vertical)
+ paintVMeter(painter, channelNr * 4, 8, 3, height - 10,
+ displayMagnitude[channelNr],
+ displayPeak[channelNr],
+ displayPeakHold[channelNr]);
+ else
+ paintHMeter(painter, 5, channelNr * 4, width - 5, 3,
+ displayMagnitude[channelNr],
+ displayPeak[channelNr],
+ displayPeakHold[channelNr]);
- if (!idle) {
- // By not drawing the input meter boxes the user can
- // see that the audio stream has been stopped, without
- // having too much visual impact.
- paintInputMeter(painter,
- 0, channelNr * 4, 3, 3,
- displayInputPeakHold[channelNr]);
- }
+ if (idle)
+ continue;
+
+ // By not drawing the input meter boxes the user can
+ // see that the audio stream has been stopped, without
+ // having too much visual impact.
+ if (vertical)
+ paintInputMeter(painter, channelNr * 4, 3, 3, 3,
+ displayInputPeakHold[channelNr]);
+ else
+ paintInputMeter(painter, 0, channelNr * 4, 3, 3,
+ displayInputPeakHold[channelNr]);
}
lastRedrawTime = ts;
diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp
index 390e684..fde0d12 100644
--- a/UI/volume-control.hpp
+++ b/UI/volume-control.hpp
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
#include
#include
@@ -79,6 +80,9 @@ class VolumeMeter : public QWidget
READ getInputPeakHoldDuration
WRITE setInputPeakHoldDuration DESIGNABLE true)
+private slots:
+ void ClipEnding();
+
private:
obs_volmeter_t *obs_volmeter;
static QWeakPointer updateTimer;
@@ -92,12 +96,15 @@ private:
inline void calculateBallisticsForChannel(int channelNr,
uint64_t ts, qreal timeSinceLastRedraw);
- void paintInputMeter(QPainter &painter, int x, int y,
- int width, int height, float peakHold);
- void paintMeter(QPainter &painter, int x, int y,
- int width, int height,
- float magnitude, float peak, float peakHold);
- void paintTicks(QPainter &painter, int x, int y, int width, int height);
+ void paintInputMeter(QPainter &painter, int x, int y, int width,
+ int height, float peakHold);
+ void paintHMeter(QPainter &painter, int x, int y, int width, int height,
+ float magnitude, float peak, float peakHold);
+ void paintHTicks(QPainter &painter, int x, int y, int width,
+ int height);
+ void paintVMeter(QPainter &painter, int x, int y, int width, int height,
+ float magnitude, float peak, float peakHold);
+ void paintVTicks(QPainter &painter, int x, int y, int height);
QMutex dataMutex;
@@ -106,7 +113,7 @@ private:
float currentPeak[MAX_AUDIO_CHANNELS];
float currentInputPeak[MAX_AUDIO_CHANNELS];
- QPixmap *tickPaintCache = NULL;
+ QPixmap *tickPaintCache = nullptr;
int displayNrAudioChannels = 0;
float displayMagnitude[MAX_AUDIO_CHANNELS];
float displayPeak[MAX_AUDIO_CHANNELS];
@@ -137,10 +144,13 @@ private:
qreal inputPeakHoldDuration;
uint64_t lastRedrawTime = 0;
+ bool clipping = false;
+ bool vertical;
public:
- explicit VolumeMeter(QWidget *parent = 0,
- obs_volmeter_t *obs_volmeter = 0);
+ explicit VolumeMeter(QWidget *parent = nullptr,
+ obs_volmeter_t *obs_volmeter = nullptr,
+ bool vertical = false);
~VolumeMeter();
void setLevels(
@@ -186,9 +196,10 @@ public:
void setPeakHoldDuration(qreal v);
qreal getInputPeakHoldDuration() const;
void setInputPeakHoldDuration(qreal v);
+ void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
protected:
- void paintEvent(QPaintEvent *event);
+ void paintEvent(QPaintEvent *event) override;
};
class VolumeMeterTimer : public QTimer {
@@ -201,7 +212,7 @@ public:
void RemoveVolControl(VolumeMeter *meter);
protected:
- virtual void timerEvent(QTimerEvent *event) override;
+ void timerEvent(QTimerEvent *event) override;
QList volumeMeters;
};
@@ -224,6 +235,7 @@ private:
float levelCount;
obs_fader_t *obs_fader;
obs_volmeter_t *obs_volmeter;
+ bool vertical;
static void OBSVolumeChanged(void *param, float db);
static void OBSVolumeLevel(void *data,
@@ -246,7 +258,8 @@ signals:
void ConfigClicked();
public:
- VolControl(OBSSource source, bool showConfig = false);
+ explicit VolControl(OBSSource source, bool showConfig = false,
+ bool vertical = false);
~VolControl();
inline obs_source_t *GetSource() const {return source;}
@@ -255,4 +268,5 @@ public:
void SetName(const QString &newName);
void SetMeterDecayRate(qreal q);
+ void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
};
diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp
index 41c34ab..f3735cb 100644
--- a/UI/win-update/updater/updater.cpp
+++ b/UI/win-update/updater/updater.cpp
@@ -324,6 +324,8 @@ struct update_t {
memcpy(hash, from.hash, sizeof(hash));
memcpy(downloadhash, from.downloadhash, sizeof(downloadhash));
memcpy(my_hash, from.my_hash, sizeof(my_hash));
+
+ return *this;
}
};
@@ -356,11 +358,11 @@ bool DownloadWorkerThread()
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS,
(LPVOID)&tlsProtocols, sizeof(tlsProtocols));
- HttpHandle hConnect = WinHttpConnect(hSession, L"obsproject.com",
+ HttpHandle hConnect = WinHttpConnect(hSession, L"cdn-fastly.obsproject.com",
INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
downloadThreadFailure = true;
- Status(L"Update failed: Couldn't connect to obsproject.com");
+ Status(L"Update failed: Couldn't connect to cdn-fastly.obsproject.com");
return false;
}
@@ -612,7 +614,7 @@ static inline bool has_str(const char *file, const char *str)
#define UTF8ToWideBuf(wide, utf8) UTF8ToWide(wide, _countof(wide), utf8)
#define WideToUTF8Buf(utf8, wide) WideToUTF8(utf8, _countof(utf8), wide)
-#define UPDATE_URL L"https://obsproject.com/update_studio"
+#define UPDATE_URL L"https://cdn-fastly.obsproject.com/update_studio"
static bool AddPackageUpdateFiles(json_t *root, size_t idx,
const wchar_t *tempPath)
@@ -748,7 +750,7 @@ static void UpdateWithPatchIfAvailable(const char *name, const char *hash,
wchar_t sourceURL[1024];
wchar_t patchHashStr[BLAKE2_HASH_STR_LENGTH];
- if (strncmp(source, "https://obsproject.com/", 23) != 0)
+ if (strncmp(source, "https://cdn-fastly.obsproject.com/", 34) != 0)
return;
string patchPackageName = name;
@@ -957,10 +959,10 @@ static bool UpdateVS2017Redists(json_t *root)
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS,
(LPVOID)&tlsProtocols, sizeof(tlsProtocols));
- HttpHandle hConnect = WinHttpConnect(hSession, L"obsproject.com",
+ HttpHandle hConnect = WinHttpConnect(hSession, L"cdn-fastly.obsproject.com",
INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
- Status(L"Update failed: Couldn't connect to obsproject.com");
+ Status(L"Update failed: Couldn't connect to cdn-fastly.obsproject.com");
return false;
}
@@ -981,7 +983,7 @@ static bool UpdateVS2017Redists(json_t *root)
: L"vc2017redist_x64.exe";
wstring sourceURL;
- sourceURL += L"https://obsproject.com/downloads/";
+ sourceURL += L"https://cdn-fastly.obsproject.com/downloads/";
sourceURL += file;
wstring destPath;
diff --git a/UI/win-update/win-update.cpp b/UI/win-update/win-update.cpp
index 510fddf..2bd77b8 100644
--- a/UI/win-update/win-update.cpp
+++ b/UI/win-update/win-update.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
@@ -27,11 +28,15 @@ using namespace std;
#define WIN_MANIFEST_URL "https://obsproject.com/update_studio/manifest.json"
#endif
+#ifndef WIN_WHATSNEW_URL
+#define WIN_WHATSNEW_URL "https://obsproject.com/update_studio/whatsnew.json"
+#endif
+
#ifndef WIN_UPDATER_URL
#define WIN_UPDATER_URL "https://obsproject.com/update_studio/updater.exe"
#endif
-static HCRYPTPROV provider = 0;
+static __declspec(thread) HCRYPTPROV provider = 0;
#pragma pack(push, r1, 1)
@@ -478,6 +483,32 @@ void GenerateGUID(string &guid)
HashToString(junk, &guid[0]);
}
+string GetProgramGUID()
+{
+ static mutex m;
+ lock_guard lock(m);
+
+ /* 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");
+ string guid;
+ if (pguid)
+ guid = pguid;
+
+ if (guid.empty()) {
+ GenerateGUID(guid);
+
+ if (!guid.empty())
+ config_set_string(GetGlobalConfig(),
+ "General", "InstallGUID",
+ guid.c_str());
+ }
+
+ return guid;
+}
+
void AutoUpdateThread::infoMsg(const QString &title, const QString &text)
{
OBSMessageBox::information(App()->GetMainWindow(), title, text);
@@ -605,24 +636,7 @@ try {
/* ----------------------------------- *
* get current install GUID */
- /* 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");
- string guid;
- if (pguid)
- guid = pguid;
-
- if (guid.empty()) {
- GenerateGUID(guid);
-
- if (!guid.empty())
- config_set_string(GetGlobalConfig(),
- "General", "InstallGUID",
- guid.c_str());
- }
-
+ string guid = GetProgramGUID();
if (!guid.empty()) {
string header = "X-OBS2-GUID: ";
header += guid;
@@ -780,3 +794,101 @@ try {
} catch (string text) {
blog(LOG_WARNING, "%s: %s", __FUNCTION__, text.c_str());
}
+
+/* ------------------------------------------------------------------------ */
+
+void WhatsNewInfoThread::run()
+try {
+ long responseCode;
+ vector extraHeaders;
+ string text;
+ string error;
+ string signature;
+ CryptProvider localProvider;
+ BYTE whatsnewHash[BLAKE2_HASH_LENGTH];
+ bool success;
+
+ BPtr whatsnewPath = GetConfigPathPtr(
+ "obs-studio\\updates\\whatsnew.json");
+
+ /* ----------------------------------- *
+ * create signature provider */
+
+ if (!CryptAcquireContext(&localProvider,
+ nullptr,
+ MS_ENH_RSA_AES_PROV,
+ PROV_RSA_AES,
+ CRYPT_VERIFYCONTEXT))
+ throw strprintf("CryptAcquireContext failed: %lu",
+ GetLastError());
+
+ provider = localProvider;
+
+ /* ----------------------------------- *
+ * avoid downloading json again */
+
+ if (CalculateFileHash(whatsnewPath, whatsnewHash)) {
+ char hashString[BLAKE2_HASH_STR_LENGTH];
+ HashToString(whatsnewHash, hashString);
+
+ string header = "If-None-Match: ";
+ header += hashString;
+ extraHeaders.push_back(move(header));
+ }
+
+ /* ----------------------------------- *
+ * get current install GUID */
+
+ string guid = GetProgramGUID();
+
+ if (!guid.empty()) {
+ string header = "X-OBS2-GUID: ";
+ header += guid;
+ extraHeaders.push_back(move(header));
+ }
+
+ /* ----------------------------------- *
+ * get json from server */
+
+ success = GetRemoteFile(WIN_WHATSNEW_URL, text, error, &responseCode,
+ nullptr, nullptr, extraHeaders, &signature);
+
+ if (!success || (responseCode != 200 && responseCode != 304)) {
+ if (responseCode == 404)
+ return;
+
+ throw strprintf("Failed to fetch whatsnew file: %s",
+ error.c_str());
+ }
+
+ /* ----------------------------------- *
+ * verify file signature */
+
+ if (responseCode == 200) {
+ success = CheckDataSignature(text, "whatsnew",
+ signature.data(), signature.size());
+ if (!success)
+ throw string("Invalid whatsnew signature");
+ }
+
+ /* ----------------------------------- *
+ * write or load json */
+
+ if (responseCode == 200) {
+ if (!QuickWriteFile(whatsnewPath, text.data(), text.size()))
+ throw strprintf("Could not write file '%s'",
+ whatsnewPath.Get());
+ } else {
+ if (!QuickReadFile(whatsnewPath, text))
+ throw strprintf("Could not read file '%s'",
+ whatsnewPath.Get());
+ }
+
+ /* ----------------------------------- *
+ * success */
+
+ emit Result(QString::fromUtf8(text.c_str()));
+
+} catch (string text) {
+ blog(LOG_WARNING, "%s: %s", __FUNCTION__, text.c_str());
+}
diff --git a/UI/win-update/win-update.hpp b/UI/win-update/win-update.hpp
index 47bdd03..c1bd8fb 100644
--- a/UI/win-update/win-update.hpp
+++ b/UI/win-update/win-update.hpp
@@ -21,3 +21,15 @@ private slots:
public:
AutoUpdateThread(bool manualUpdate_) : manualUpdate(manualUpdate_) {}
};
+
+class WhatsNewInfoThread : public QThread {
+ Q_OBJECT
+
+ virtual void run() override;
+
+signals:
+ void Result(const QString &text);
+
+public:
+ inline WhatsNewInfoThread() {}
+};
diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp
index 2894f6b..e6b47f7 100644
--- a/UI/window-basic-adv-audio.cpp
+++ b/UI/window-basic-adv-audio.cpp
@@ -6,6 +6,7 @@
#include
#include "window-basic-adv-audio.hpp"
#include "window-basic-main.hpp"
+#include "item-widget-helpers.hpp"
#include "adv-audio-control.hpp"
#include "obs-app.hpp"
#include "qt-wrappers.hpp"
@@ -133,7 +134,12 @@ void OBSBasicAdvAudio::OBSSourceRemoved(void *param, calldata_t *calldata)
inline void OBSBasicAdvAudio::AddAudioSource(obs_source_t *source)
{
OBSAdvAudioCtrl *control = new OBSAdvAudioCtrl(mainLayout, source);
- controls.push_back(control);
+
+ InsertQObjectByName(controls, control);
+
+ for (auto control : controls) {
+ control->ShowAudioControl(mainLayout);
+ }
}
void OBSBasicAdvAudio::SourceAdded(OBSSource source)
diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp
index 43e4c9a..7ddf2af 100644
--- a/UI/window-basic-auto-config.cpp
+++ b/UI/window-basic-auto-config.cpp
@@ -578,7 +578,7 @@ AutoConfig::AutoConfig(QWidget *parent)
setPage(VideoPage, new AutoConfigVideoPage());
setPage(StreamPage, streamPage);
setPage(TestPage, new AutoConfigTestPage());
- setWindowTitle(QTStr("Basic.AutoConfig.Beta"));
+ setWindowTitle(QTStr("Basic.AutoConfig"));
obs_video_info ovi;
obs_get_video_info(&ovi);
diff --git a/UI/window-basic-filters.cpp b/UI/window-basic-filters.cpp
index d792b1e..564deac 100644
--- a/UI/window-basic-filters.cpp
+++ b/UI/window-basic-filters.cpp
@@ -56,7 +56,8 @@ OBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_)
OBSBasicFilters::SourceRemoved, this),
renameSourceSignal (obs_source_get_signal_handler(source),
"rename",
- OBSBasicFilters::SourceRenamed, this)
+ OBSBasicFilters::SourceRenamed, this),
+ noPreviewMargin (13)
{
main = reinterpret_cast(parent);
@@ -97,10 +98,10 @@ OBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_)
connect(ui->buttonBox->button(QDialogButtonBox::Reset),
SIGNAL(clicked()), this, SLOT(ResetFilters()));
- uint32_t flags = obs_source_get_output_flags(source);
- bool audio = (flags & OBS_SOURCE_AUDIO) != 0;
- bool audioOnly = (flags & OBS_SOURCE_VIDEO) == 0;
- bool async = (flags & OBS_SOURCE_ASYNC) != 0;
+ uint32_t caps = obs_source_get_output_flags(source);
+ bool audio = (caps & OBS_SOURCE_AUDIO) != 0;
+ bool audioOnly = (caps & OBS_SOURCE_VIDEO) == 0;
+ bool async = (caps & OBS_SOURCE_ASYNC) != 0;
if (!async && !audio) {
ui->asyncWidget->setVisible(false);
@@ -121,13 +122,20 @@ OBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_)
};
enum obs_source_type type = obs_source_get_type(source);
- uint32_t caps = obs_source_get_output_flags(source);
bool drawable_type = type == OBS_SOURCE_TYPE_INPUT ||
type == OBS_SOURCE_TYPE_SCENE;
- if (drawable_type && (caps & OBS_SOURCE_VIDEO) != 0)
- connect(ui->preview, &OBSQTDisplay::DisplayCreated,
- addDrawCallback);
+ if ((caps & OBS_SOURCE_VIDEO) != 0) {
+ ui->rightLayout->setContentsMargins(0, 0, 0, 0);
+ ui->preview->show();
+ if (drawable_type)
+ connect(ui->preview, &OBSQTDisplay::DisplayCreated,
+ addDrawCallback);
+ } else {
+ ui->rightLayout->setContentsMargins(0, noPreviewMargin, 0, 0);
+ ui->rightContainerLayout->insertStretch(1);
+ ui->preview->hide();
+ }
}
OBSBasicFilters::~OBSBasicFilters()
@@ -569,7 +577,7 @@ static bool QueryRemove(QWidget *parent, obs_source_t *source)
void OBSBasicFilters::on_addAsyncFilter_clicked()
{
- QPointer popup = CreateAddFilterPopupMenu(true);
+ QScopedPointer popup(CreateAddFilterPopupMenu(true));
if (popup)
popup->exec(QCursor::pos());
}
@@ -611,7 +619,7 @@ void OBSBasicFilters::on_asyncFilters_currentRowChanged(int row)
void OBSBasicFilters::on_addEffectFilter_clicked()
{
- QPointer popup = CreateAddFilterPopupMenu(false);
+ QScopedPointer popup(CreateAddFilterPopupMenu(false));
if (popup)
popup->exec(QCursor::pos());
}
diff --git a/UI/window-basic-filters.hpp b/UI/window-basic-filters.hpp
index cc655a4..9fdbce3 100644
--- a/UI/window-basic-filters.hpp
+++ b/UI/window-basic-filters.hpp
@@ -72,6 +72,8 @@ private:
bool isAsync;
+ int noPreviewMargin;
+
private slots:
void AddFilter(OBSSource filter);
void RemoveFilter(OBSSource filter);
diff --git a/UI/window-basic-main-dropfiles.cpp b/UI/window-basic-main-dropfiles.cpp
index 2622ab1..cd5c481 100644
--- a/UI/window-basic-main-dropfiles.cpp
+++ b/UI/window-basic-main-dropfiles.cpp
@@ -107,8 +107,10 @@ void OBSBasic::AddDropSource(const char *data, DropType image)
break;
}
- if (!obs_source_get_display_name(type))
+ if (!obs_source_get_display_name(type)) {
+ obs_data_release(settings);
return;
+ }
if (name.isEmpty())
name = obs_source_get_display_name(type);
diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp
index 16039a3..2f9bfa8 100644
--- a/UI/window-basic-main-outputs.cpp
+++ b/UI/window-basic-main-outputs.cpp
@@ -895,6 +895,7 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
obs_data_set_string(settings, "directory", path);
obs_data_set_string(settings, "format", f.c_str());
obs_data_set_string(settings, "extension", format);
+ obs_data_set_bool(settings, "allow_spaces", !noSpace);
obs_data_set_int(settings, "max_time_sec", rbTime);
obs_data_set_int(settings, "max_size_mb",
usingRecordingPreset ? rbSize : 0);
@@ -1032,19 +1033,32 @@ struct AdvancedOutput : BasicOutputHandler {
static OBSData GetDataFromJsonFile(const char *jsonFile)
{
char fullPath[512];
+ obs_data_t *data = nullptr;
int ret = GetProfilePath(fullPath, sizeof(fullPath), jsonFile);
if (ret > 0) {
BPtr jsonData = os_quick_read_utf8_file(fullPath);
if (!!jsonData) {
- obs_data_t *data = obs_data_create_from_json(jsonData);
- OBSData dataRet(data);
- obs_data_release(data);
- return dataRet;
+ data = obs_data_create_from_json(jsonData);
}
}
- return nullptr;
+ if (!data)
+ data = obs_data_create();
+ OBSData dataRet(data);
+ obs_data_release(data);
+ return dataRet;
+}
+
+static void ApplyEncoderDefaults(OBSData &settings,
+ const obs_encoder_t *encoder)
+{
+ OBSData dataRet = obs_encoder_get_defaults(encoder);
+ obs_data_release(dataRet);
+
+ if (!!settings)
+ obs_data_apply(dataRet, settings);
+ settings = std::move(dataRet);
}
AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
@@ -1157,6 +1171,7 @@ void AdvancedOutput::UpdateStreamSettings()
"ApplyServiceSettings");
OBSData settings = GetDataFromJsonFile("streamEncoder.json");
+ ApplyEncoderDefaults(settings, h264Streaming);
if (applyServiceSettings)
obs_service_apply_encoder_settings(main->GetService(),
@@ -1717,6 +1732,7 @@ bool AdvancedOutput::StartReplayBuffer()
obs_data_set_string(settings, "directory", path);
obs_data_set_string(settings, "format", f.c_str());
obs_data_set_string(settings, "extension", recFormat);
+ obs_data_set_bool(settings, "allow_spaces", !noSpace);
obs_data_set_int(settings, "max_time_sec", rbTime);
obs_data_set_int(settings, "max_size_mb",
usesBitrate ? 0 : rbSize);
diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp
index 160e13c..f5db4cd 100644
--- a/UI/window-basic-main-profiles.cpp
+++ b/UI/window-basic-main-profiles.cpp
@@ -346,6 +346,19 @@ void OBSBasic::ResetProfileData()
ResetOutputs();
ClearHotkeys();
CreateHotkeys();
+
+ /* load audio monitoring */
+#if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO
+ const char *device_name = config_get_string(basicConfig, "Audio",
+ "MonitoringDeviceName");
+ const char *device_id = config_get_string(basicConfig, "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);
+#endif
}
void OBSBasic::on_actionNewProfile_triggered()
diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp
index 113f848..9759a27 100644
--- a/UI/window-basic-main-scene-collections.cpp
+++ b/UI/window-basic-main-scene-collections.cpp
@@ -154,13 +154,19 @@ static bool GetSceneCollectionName(QWidget *parent, std::string &name,
return true;
}
-void OBSBasic::AddSceneCollection(bool create_new)
+bool OBSBasic::AddSceneCollection(bool create_new, const QString &qname)
{
std::string name;
std::string file;
- if (!GetSceneCollectionName(this, name, file))
- return;
+ if (qname.isEmpty()) {
+ if (!GetSceneCollectionName(this, name, file))
+ return false;
+ } else {
+ name = QT_TO_UTF8(qname);
+ if (SceneCollectionExists(name.c_str()))
+ return false;
+ }
SaveProjectNow();
@@ -185,6 +191,8 @@ void OBSBasic::AddSceneCollection(bool create_new)
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);
api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);
}
+
+ return true;
}
void OBSBasic::RefreshSceneCollections()
@@ -237,7 +245,6 @@ void OBSBasic::RefreshSceneCollections()
OBSBasic *main = reinterpret_cast(App()->GetMainWindow());
- main->OpenSavedProjectors();
main->ui->actionPasteFilters->setEnabled(false);
main->ui->actionPasteRef->setEnabled(false);
main->ui->actionPasteDup->setEnabled(false);
diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp
index 726eea1..952f5d4 100644
--- a/UI/window-basic-main-transitions.cpp
+++ b/UI/window-basic-main-transitions.cpp
@@ -26,6 +26,8 @@
#include "menu-button.hpp"
#include "qt-wrappers.hpp"
+#include "obs-hotkey.h"
+
using namespace std;
Q_DECLARE_METATYPE(OBSScene);
@@ -98,6 +100,18 @@ void OBSBasic::AddQuickTransitionHotkey(QuickTransition *qt)
(void*)(uintptr_t)qt->id);
}
+void QuickTransition::SourceRenamed(void *param, calldata_t *data)
+{
+ QuickTransition *qt = reinterpret_cast(param);
+
+ QString hotkeyName = QTStr("QuickTransitions.HotkeyName")
+ .arg(MakeQuickTransitionText(qt));
+
+ obs_hotkey_set_description(qt->hotkey, QT_TO_UTF8(hotkeyName));
+
+ UNUSED_PARAMETER(data);
+}
+
void OBSBasic::TriggerQuickTransition(int id)
{
QuickTransition *qt = GetQuickTransition(id);
@@ -545,6 +559,10 @@ void OBSBasic::RenameTransition()
int idx = ui->transitions->findData(variant);
if (idx != -1) {
ui->transitions->setItemText(idx, QT_UTF8(name.c_str()));
+
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);
+
ClearQuickTransitionWidgets();
RefreshQuickTransitions();
}
@@ -639,12 +657,11 @@ void OBSBasic::SetCurrentScene(OBSSource scene, bool force, bool direct)
ui->scenes->blockSignals(true);
ui->scenes->setCurrentItem(item);
ui->scenes->blockSignals(false);
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
break;
}
}
-
- if (api && IsPreviewProgramMode())
- api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
}
UpdateSceneSelection(scene);
@@ -733,7 +750,7 @@ void OBSBasic::CreateProgramOptions()
programOptions->setLayout(layout);
auto onAdd = [this] () {
- QPointer menu = CreateTransitionMenu(this, nullptr);
+ QScopedPointer menu(CreateTransitionMenu(this, nullptr));
menu->exec(QCursor::pos());
};
diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp
index e729b69..b2cebfe 100644
--- a/UI/window-basic-main.cpp
+++ b/UI/window-basic-main.cpp
@@ -27,6 +27,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
#include
@@ -54,12 +57,14 @@
#include "display-helpers.hpp"
#include "volume-control.hpp"
#include "remote-text.hpp"
+#include "source-tree.hpp"
#ifdef _WIN32
#include "win-update/win-update.hpp"
#endif
#include "ui_OBSBasic.h"
+#include "ui_ColorSelect.h"
#include
#include
@@ -67,8 +72,19 @@
#include
#include
+#ifdef _WIN32
+#include
+#endif
+
+#include
+
+using namespace json11;
using namespace std;
+#ifdef _WIN32
+static CREATE_BROWSER_WIDGET_PROC create_browser_widget = nullptr;
+#endif
+
namespace {
template
@@ -129,7 +145,27 @@ static void AddExtraModulePaths()
#endif
}
-static QList DeleteKeys;
+extern obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main);
+
+static int CountVideoSources()
+{
+ int count = 0;
+
+ auto countSources = [] (void *param, obs_source_t *source)
+ {
+ if (!source)
+ return true;
+
+ uint32_t flags = obs_source_get_output_flags(source);
+ if ((flags & OBS_SOURCE_VIDEO) != 0)
+ (*reinterpret_cast(param))++;
+
+ return true;
+ };
+
+ obs_enum_sources(countSources, &count);
+ return count;
+}
OBSBasic::OBSBasic(QWidget *parent)
: OBSMainWindow (parent),
@@ -137,13 +173,10 @@ OBSBasic::OBSBasic(QWidget *parent)
{
setAttribute(Qt::WA_NativeWindow);
- projectorArray.resize(10, "");
- previewProjectorArray.resize(10, 0);
- multiviewProjectorArray.resize(10, 0);
- studioProgramProjectorArray.resize(10, 0);
-
setAcceptDrops(true);
+ api = InitializeAPIInterface(this);
+
ui->setupUi(this);
ui->previewDisabledLabel->setVisible(false);
@@ -151,8 +184,6 @@ OBSBasic::OBSBasic(QWidget *parent)
copyActionsDynamicProperties();
- ui->sources->setItemDelegate(new VisibilityItemDelegate(ui->sources));
-
char styleSheetPath[512];
int ret = GetProfilePath(styleSheetPath, sizeof(styleSheetPath),
"stylesheet.qss");
@@ -204,28 +235,15 @@ OBSBasic::OBSBasic(QWidget *parent)
SLOT(SceneNameEdited(QWidget*,
QAbstractItemDelegate::EndEditHint)));
- connect(ui->sources->itemDelegate(),
- SIGNAL(closeEditor(QWidget*,
- QAbstractItemDelegate::EndEditHint)),
- this,
- SLOT(SceneItemNameEdited(QWidget*,
- QAbstractItemDelegate::EndEditHint)));
-
cpuUsageInfo = os_cpu_usage_info_start();
cpuUsageTimer = new QTimer(this);
- connect(cpuUsageTimer, SIGNAL(timeout()),
+ connect(cpuUsageTimer.data(), SIGNAL(timeout()),
ui->statusbar, SLOT(UpdateCPUUsage()));
cpuUsageTimer->start(3000);
- DeleteKeys =
#ifdef __APPLE__
- QList{{Qt::Key_Backspace}} <<
-#endif
- QKeySequence::keyBindings(QKeySequence::Delete);
-
-#ifdef __APPLE__
- ui->actionRemoveSource->setShortcuts(DeleteKeys);
- ui->actionRemoveScene->setShortcuts(DeleteKeys);
+ ui->actionRemoveSource->setShortcuts({Qt::Key_Backspace});
+ ui->actionRemoveScene->setShortcuts({Qt::Key_Backspace});
ui->action_Settings->setMenuRole(QAction::PreferencesRole);
ui->actionE_xit->setMenuRole(QAction::QuitRole);
@@ -245,7 +263,7 @@ OBSBasic::OBSBasic(QWidget *parent)
addNudge(Qt::Key_Left, SLOT(NudgeLeft()));
addNudge(Qt::Key_Right, SLOT(NudgeRight()));
- auto assignDockToggle = [this](QDockWidget *dock, QAction *action)
+ auto assignDockToggle = [] (QDockWidget *dock, QAction *action)
{
auto handleWindowToggle = [action] (bool vis)
{
@@ -319,10 +337,7 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
obs_data_array_t *quickTransitionData, int transitionDuration,
obs_data_array_t *transitions,
OBSScene &scene, OBSSource &curProgramScene,
- obs_data_array_t *savedProjectorList,
- obs_data_array_t *savedPreviewProjectorList,
- obs_data_array_t *savedStudioProgramProjectorList,
- obs_data_array_t *savedMultiviewProjectorList)
+ obs_data_array_t *savedProjectorList)
{
obs_data_t *saveData = obs_data_create();
@@ -335,8 +350,14 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
SaveAudioDevice(AUX_AUDIO_2, 4, saveData, audioSources);
SaveAudioDevice(AUX_AUDIO_3, 5, saveData, audioSources);
+ /* -------------------------------- */
+ /* save non-group sources */
+
auto FilterAudioSources = [&](obs_source_t *source)
{
+ if (obs_source_is_group(source))
+ return false;
+
return find(begin(audioSources), end(audioSources), source) ==
end(audioSources);
};
@@ -348,6 +369,18 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
return (*static_cast(data))(source);
}, static_cast(&FilterAudioSources));
+ /* -------------------------------- */
+ /* save group sources separately */
+
+ /* saving separately ensures they won't be loaded in older versions */
+ obs_data_array_t *groupsArray = obs_save_sources_filtered(
+ [](void*, obs_source_t *source)
+ {
+ return obs_source_is_group(source);
+ }, nullptr);
+
+ /* -------------------------------- */
+
obs_source_t *transition = obs_get_output_source(0);
obs_source_t *currentScene = obs_scene_get_source(scene);
const char *sceneName = obs_source_get_name(currentScene);
@@ -361,16 +394,12 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
obs_data_set_array(saveData, "scene_order", sceneOrder);
obs_data_set_string(saveData, "name", sceneCollection);
obs_data_set_array(saveData, "sources", sourcesArray);
+ obs_data_set_array(saveData, "groups", groupsArray);
obs_data_set_array(saveData, "quick_transitions", quickTransitionData);
obs_data_set_array(saveData, "transitions", transitions);
obs_data_set_array(saveData, "saved_projectors", savedProjectorList);
- obs_data_set_array(saveData, "saved_preview_projectors",
- savedPreviewProjectorList);
- obs_data_set_array(saveData, "saved_studio_preview_projectors",
- savedStudioProgramProjectorList);
- obs_data_set_array(saveData, "saved_multiview_projectors",
- savedMultiviewProjectorList);
obs_data_array_release(sourcesArray);
+ obs_data_array_release(groupsArray);
obs_data_set_string(saveData, "current_transition",
obs_source_get_name(transition));
@@ -410,14 +439,33 @@ void OBSBasic::UpdateVolumeControlsDecayRate()
}
}
-void OBSBasic::ClearVolumeControls()
+void OBSBasic::UpdateVolumeControlsPeakMeterType()
{
- VolControl *control;
+ uint32_t peakMeterTypeIdx = config_get_uint(basicConfig, "Audio",
+ "PeakMeterType");
+
+ enum obs_peak_meter_type peakMeterType;
+ switch (peakMeterTypeIdx) {
+ case 0:
+ peakMeterType = SAMPLE_PEAK_METER;
+ break;
+ case 1:
+ peakMeterType = TRUE_PEAK_METER;
+ break;
+ default:
+ peakMeterType = SAMPLE_PEAK_METER;
+ break;
+ }
for (size_t i = 0; i < volumes.size(); i++) {
- control = volumes[i];
- delete control;
+ volumes[i]->setPeakMeterType(peakMeterType);
}
+}
+
+void OBSBasic::ClearVolumeControls()
+{
+ for (VolControl *vol : volumes)
+ delete vol;
volumes.clear();
}
@@ -439,62 +487,41 @@ obs_data_array_t *OBSBasic::SaveSceneListOrder()
obs_data_array_t *OBSBasic::SaveProjectors()
{
- obs_data_array_t *saveProjector = obs_data_array_create();
+ obs_data_array_t *savedProjectors = obs_data_array_create();
+
+ auto saveProjector = [savedProjectors](OBSProjector *projector) {
+ if (!projector)
+ return;
- for (size_t i = 0; i < projectorArray.size(); i++) {
obs_data_t *data = obs_data_create();
- obs_data_set_string(data, "saved_projectors",
- projectorArray.at(i).c_str());
- obs_data_array_push_back(saveProjector, data);
+ ProjectorType type = projector->GetProjectorType();
+ switch (type) {
+ case ProjectorType::Scene:
+ case ProjectorType::Source: {
+ obs_source_t *source = projector->GetSource();
+ const char *name = obs_source_get_name(source);
+ obs_data_set_string(data, "name", name);
+ break;
+ }
+ default:
+ break;
+ }
+ obs_data_set_int(data, "monitor", projector->GetMonitor());
+ obs_data_set_int(data, "type", static_cast(type));
+ obs_data_set_string(data, "geometry",
+ projector->saveGeometry().toBase64()
+ .constData());
+ obs_data_array_push_back(savedProjectors, data);
obs_data_release(data);
- }
+ };
- return saveProjector;
-}
+ for (QPointer &proj : projectors)
+ saveProjector(static_cast(proj.data()));
-obs_data_array_t *OBSBasic::SavePreviewProjectors()
-{
- obs_data_array_t *saveProjector = obs_data_array_create();
+ for (QPointer &proj : windowProjectors)
+ saveProjector(static_cast(proj.data()));
- for (size_t i = 0; i < previewProjectorArray.size(); i++) {
- obs_data_t *data = obs_data_create();
- obs_data_set_int(data, "saved_preview_projectors",
- previewProjectorArray.at(i));
- obs_data_array_push_back(saveProjector, data);
- obs_data_release(data);
- }
-
- return saveProjector;
-}
-
-obs_data_array_t *OBSBasic::SaveStudioProgramProjectors()
-{
- obs_data_array_t *saveProjector = obs_data_array_create();
-
- for (size_t i = 0; i < studioProgramProjectorArray.size(); i++) {
- obs_data_t *data = obs_data_create();
- obs_data_set_int(data, "saved_studio_preview_projectors",
- studioProgramProjectorArray.at(i));
- obs_data_array_push_back(saveProjector, data);
- obs_data_release(data);
- }
-
- return saveProjector;
-}
-
-obs_data_array_t *OBSBasic::SaveMultiviewProjectors()
-{
- obs_data_array_t *saveProjector = obs_data_array_create();
-
- for (size_t i = 0; i < multiviewProjectorArray.size(); i++) {
- obs_data_t *data = obs_data_create();
- obs_data_set_int(data, "saved_multiview_projectors",
- multiviewProjectorArray.at(i));
- obs_data_array_push_back(saveProjector, data);
- obs_data_release(data);
- }
-
- return saveProjector;
+ return savedProjectors;
}
void OBSBasic::Save(const char *file)
@@ -508,17 +535,9 @@ void OBSBasic::Save(const char *file)
obs_data_array_t *transitions = SaveTransitions();
obs_data_array_t *quickTrData = SaveQuickTransitions();
obs_data_array_t *savedProjectorList = SaveProjectors();
- obs_data_array_t *savedPreviewProjectorList = SavePreviewProjectors();
- obs_data_array_t *savedStudioProgramProjectorList =
- SaveStudioProgramProjectors();
- obs_data_array_t *savedMultiviewProjectorList =
- SaveMultiviewProjectors();
obs_data_t *saveData = GenerateSaveData(sceneOrder, quickTrData,
ui->transitionDuration->value(), transitions,
- scene, curProgramScene, savedProjectorList,
- savedPreviewProjectorList,
- savedStudioProgramProjectorList,
- savedMultiviewProjectorList);
+ scene, curProgramScene, savedProjectorList);
obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked());
obs_data_set_bool(saveData, "scaling_enabled",
@@ -545,9 +564,19 @@ void OBSBasic::Save(const char *file)
obs_data_array_release(quickTrData);
obs_data_array_release(transitions);
obs_data_array_release(savedProjectorList);
- obs_data_array_release(savedPreviewProjectorList);
- obs_data_array_release(savedStudioProgramProjectorList);
- obs_data_array_release(savedMultiviewProjectorList);
+}
+
+void OBSBasic::DeferSaveBegin()
+{
+ os_atomic_inc_long(&disableSaving);
+}
+
+void OBSBasic::DeferSaveEnd()
+{
+ long result = os_atomic_dec_long(&disableSaving);
+ if (result == 0) {
+ SaveProject();
+ }
}
static void LoadAudioDevice(const char *name, int channel, obs_data_t *parent)
@@ -611,7 +640,6 @@ void OBSBasic::CreateDefaultScene(bool firstStart)
if (firstStart)
CreateFirstRunSources();
- AddScene(obs_scene_get_source(scene));
SetCurrentScene(scene, true);
obs_scene_release(scene);
@@ -653,47 +681,15 @@ void OBSBasic::LoadSavedProjectors(obs_data_array_t *array)
for (size_t i = 0; i < num; i++) {
obs_data_t *data = obs_data_array_item(array, i);
- projectorArray.at(i) = obs_data_get_string(data,
- "saved_projectors");
- obs_data_release(data);
- }
-}
-
-void OBSBasic::LoadSavedPreviewProjectors(obs_data_array_t *array)
-{
- size_t num = obs_data_array_count(array);
-
- for (size_t i = 0; i < num; i++) {
- obs_data_t *data = obs_data_array_item(array, i);
- previewProjectorArray.at(i) = obs_data_get_int(data,
- "saved_preview_projectors");
-
- obs_data_release(data);
- }
-}
-
-void OBSBasic::LoadSavedStudioProgramProjectors(obs_data_array_t *array)
-{
- size_t num = obs_data_array_count(array);
-
- for (size_t i = 0; i < num; i++) {
- obs_data_t *data = obs_data_array_item(array, i);
- studioProgramProjectorArray.at(i) = obs_data_get_int(data,
- "saved_studio_preview_projectors");
-
- obs_data_release(data);
- }
-}
-
-void OBSBasic::LoadSavedMultiviewProjectors(obs_data_array_t *array)
-{
- size_t num = obs_data_array_count(array);
-
- for (size_t i = 0; i < num; i++) {
- obs_data_t *data = obs_data_array_item(array, i);
- multiviewProjectorArray.at(i) = obs_data_get_int(data,
- "saved_multiview_projectors");
+ SavedProjectorInfo *info = new SavedProjectorInfo();
+ info->monitor = obs_data_get_int(data, "monitor");
+ info->type = static_cast(obs_data_get_int(data,
+ "type"));
+ info->geometry = std::string(
+ obs_data_get_string(data, "geometry"));
+ info->name = std::string(obs_data_get_string(data, "name"));
+ savedProjectorsArray.emplace_back(info);
obs_data_release(data);
}
@@ -778,6 +774,7 @@ void OBSBasic::Load(const char *file)
obs_data_array_t *sceneOrder = obs_data_get_array(data, "scene_order");
obs_data_array_t *sources = obs_data_get_array(data, "sources");
+ obs_data_array_t *groups = obs_data_get_array(data, "groups");
obs_data_array_t *transitions= obs_data_get_array(data, "transitions");
const char *sceneName = obs_data_get_string(data,
"current_scene");
@@ -818,7 +815,14 @@ void OBSBasic::Load(const char *file)
LoadAudioDevice(AUX_AUDIO_2, 4, data);
LoadAudioDevice(AUX_AUDIO_3, 5, data);
- obs_load_sources(sources, OBSBasic::SourceLoaded, this);
+ if (!sources) {
+ sources = groups;
+ groups = nullptr;
+ } else {
+ obs_data_array_push_back_array(sources, groups);
+ }
+
+ obs_load_sources(sources, nullptr, nullptr);
if (transitions)
LoadTransitions(transitions);
@@ -834,47 +838,6 @@ void OBSBasic::Load(const char *file)
ui->transitionDuration->setValue(newDuration);
SetTransition(curTransition);
- /* ------------------- */
-
- obs_data_array_t *savedProjectors = obs_data_get_array(data,
- "saved_projectors");
-
- if (savedProjectors)
- LoadSavedProjectors(savedProjectors);
-
- obs_data_array_release(savedProjectors);
-
- /* ------------------- */
-
- obs_data_array_t *savedPreviewProjectors = obs_data_get_array(data,
- "saved_preview_projectors");
-
- if (savedPreviewProjectors)
- LoadSavedPreviewProjectors(savedPreviewProjectors);
-
- obs_data_array_release(savedPreviewProjectors);
-
- /* ------------------- */
-
- obs_data_array_t *savedStudioProgramProjectors = obs_data_get_array(data,
- "saved_studio_preview_projectors");
-
- if (savedStudioProgramProjectors)
- LoadSavedStudioProgramProjectors(savedStudioProgramProjectors);
-
- obs_data_array_release(savedStudioProgramProjectors);
-
- /* ------------------- */
-
- obs_data_array_t *savedMultiviewProjectors = obs_data_get_array(data,
- "saved_multiview_projectors");
-
- if (savedMultiviewProjectors)
- LoadSavedMultiviewProjectors(savedMultiviewProjectors);
-
- obs_data_array_release(savedMultiviewProjectors);
-
-
retryScene:
curScene = obs_get_source_by_name(sceneName);
curProgramScene = obs_get_source_by_name(programSceneName);
@@ -903,8 +866,29 @@ retryScene:
obs_source_release(curProgramScene);
obs_data_array_release(sources);
+ obs_data_array_release(groups);
obs_data_array_release(sceneOrder);
+ /* ------------------- */
+
+ bool projectorSave = config_get_bool(GetGlobalConfig(), "BasicWindow",
+ "SaveProjectors");
+
+ if (projectorSave) {
+ obs_data_array_t *savedProjectors = obs_data_get_array(data,
+ "saved_projectors");
+
+ if (savedProjectors) {
+ LoadSavedProjectors(savedProjectors);
+ OpenSavedProjectors();
+ activateWindow();
+ }
+
+ obs_data_array_release(savedProjectors);
+ }
+
+ /* ------------------- */
+
std::string file_base = strrchr(file, '/') + 1;
file_base.erase(file_base.size() - 5, 5);
@@ -972,8 +956,10 @@ retryScene:
disableSaving--;
- if (api)
+ if (api) {
api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED);
+ api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
+ }
}
#define SERVICE_PATH "service.json"
@@ -1247,6 +1233,7 @@ bool OBSBasic::InitBasicConfigDefaults()
"Stereo");
config_set_default_double(basicConfig, "Audio", "MeterDecayRate",
VOLUME_METER_DECAY_FAST);
+ config_set_default_uint (basicConfig, "Audio", "PeakMeterType", 0);
return true;
}
@@ -1296,6 +1283,8 @@ void OBSBasic::InitOBSCallbacks()
ProfileScope("OBSBasic::InitOBSCallbacks");
signalHandlers.reserve(signalHandlers.size() + 6);
+ signalHandlers.emplace_back(obs_get_signal_handler(), "source_create",
+ OBSBasic::SourceCreated, this);
signalHandlers.emplace_back(obs_get_signal_handler(), "source_remove",
OBSBasic::SourceRemoved, this);
signalHandlers.emplace_back(obs_get_signal_handler(), "source_activate",
@@ -1377,6 +1366,7 @@ void OBSBasic::ResetOutputs()
replayBufferButton = new QPushButton(
QTStr("Basic.Main.StartReplayBuffer"),
this);
+ replayBufferButton->setCheckable(true);
connect(replayBufferButton.data(),
&QPushButton::clicked,
this,
@@ -1402,8 +1392,6 @@ static void AddProjectorMenuMonitors(QMenu *parent, QObject *target,
#define SHUTDOWN_SEPARATOR \
"==== Shutting down =================================================="
-extern obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main);
-
#define UNSUPPORTED_ERROR \
"Failed to initialize video:\n\nRequired graphics API functionality " \
"not found. Your GPU may not be supported."
@@ -1469,8 +1457,6 @@ void OBSBasic::OBSInit()
InitOBSCallbacks();
InitHotkeys();
- api = InitializeAPIInterface(this);
-
AddExtraModulePaths();
blog(LOG_INFO, "---------------------------------");
obs_load_all_modules();
@@ -1479,6 +1465,10 @@ void OBSBasic::OBSInit()
blog(LOG_INFO, "---------------------------------");
obs_post_load_modules();
+#ifdef _WIN32
+ create_browser_widget = obs_browser_init_panel();
+#endif
+
CheckForSimpleModeX264Fallback();
blog(LOG_INFO, STARTUP_SEPARATOR);
@@ -1520,12 +1510,14 @@ void OBSBasic::OBSInit()
SET_VISIBILITY("ShowStatusBar", toggleStatusBar);
#undef SET_VISIBILITY
+#ifndef __APPLE__
{
ProfileScope("OBSBasic::Load");
disableSaving--;
Load(savePath);
disableSaving++;
}
+#endif
TimedCheckForUpdates();
loaded = true;
@@ -1547,7 +1539,9 @@ void OBSBasic::OBSInit()
}
#endif
+#ifndef __APPLE__
RefreshSceneCollections();
+#endif
RefreshProfiles();
disableSaving--;
@@ -1600,9 +1594,9 @@ void OBSBasic::OBSInit()
ui->lockUI->setChecked(docksLocked);
ui->lockUI->blockSignals(false);
+#ifndef __APPLE__
SystemTray(true);
-
- OpenSavedProjectors();
+#endif
if (windowState().testFlag(Qt::WindowFullScreen))
fullscreenInterface = true;
@@ -1621,8 +1615,6 @@ void OBSBasic::OBSInit()
if (!first_run && !has_last_version && !Active()) {
QString msg;
msg = QTStr("Basic.FirstStartup.RunWizard");
- msg += "\n\n";
- msg += QTStr("Basic.FirstStartup.RunWizard.BetaWarning");
QMessageBox::StandardButton button =
OBSMessageBox::question(this, QTStr("Basic.AutoConfig"),
@@ -1637,6 +1629,9 @@ void OBSBasic::OBSInit()
}
}
+ ToggleMixerLayout(config_get_bool(App()->GlobalConfig(), "BasicWindow",
+ "VerticalVolControl"));
+
if (config_get_bool(basicConfig, "General", "OpenStatsOnStartup"))
on_stats_triggered();
@@ -1664,6 +1659,174 @@ void OBSBasic::OBSInit()
ui->menuCrashLogs = nullptr;
ui->actionCheckForUpdates = nullptr;
#endif
+
+#ifdef __APPLE__
+ /* This is an incredibly unpleasant hack for macOS to isolate CEF
+ * initialization until after all tasks related to Qt startup and main
+ * window initialization have completed. There is a macOS-specific bug
+ * within either CEF and/or Qt that can cause a crash if both Qt and
+ * CEF are loading at the same time.
+ *
+ * CEF will typically load fine after about two iterations from this
+ * point, and all Qt tasks are typically fully completed after about
+ * four or five iterations, but to be "ultra" safe, an arbitrarily
+ * large number such as 10 is used. This hack is extremely unpleasant,
+ * but is worth doing instead of being forced to isolate the entire
+ * browser plugin in to a separate process as before.
+ *
+ * Again, this hack is specific to macOS only. Fortunately, on other
+ * operating systems, such issues do not occur. */
+ QMetaObject::invokeMethod(this, "DeferredLoad",
+ Qt::QueuedConnection,
+ Q_ARG(QString, QT_UTF8(savePath)),
+ Q_ARG(int, 10));
+#else
+ OnFirstLoad();
+#endif
+}
+
+void OBSBasic::OnFirstLoad()
+{
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_FINISHED_LOADING);
+
+#ifdef _WIN32
+ /* Attempt to load init screen if available */
+ if (create_browser_widget) {
+ WhatsNewInfoThread *wnit = new WhatsNewInfoThread();
+ if (wnit) {
+ connect(wnit, &WhatsNewInfoThread::Result,
+ this, &OBSBasic::ReceivedIntroJson);
+ }
+ if (wnit) {
+ introCheckThread.reset(wnit);
+ introCheckThread->start();
+ }
+ }
+#endif
+}
+
+void OBSBasic::DeferredLoad(const QString &file, int requeueCount)
+{
+ if (--requeueCount > 0) {
+ QMetaObject::invokeMethod(this, "DeferredLoad",
+ Qt::QueuedConnection,
+ Q_ARG(QString, file),
+ Q_ARG(int, requeueCount));
+ return;
+ }
+
+ Load(QT_TO_UTF8(file));
+ RefreshSceneCollections();
+ OnFirstLoad();
+
+ /* Minimizng to tray on initial startup does not work on mac
+ * unless it is done in the deferred load */
+ SystemTray(true);
+}
+
+/* shows a "what's new" page on startup of new versions using CEF */
+void OBSBasic::ReceivedIntroJson(const QString &text)
+{
+#ifdef _WIN32
+ std::string err;
+ Json json = Json::parse(QT_TO_UTF8(text), err);
+ if (!err.empty())
+ return;
+
+ std::string info_url;
+ int info_increment = -1;
+
+ /* check to see if there's an info page for this version */
+ const Json::array &items = json.array_items();
+ for (const Json &item : items) {
+ const std::string &version = item["version"].string_value();
+ const std::string &url = item["url"].string_value();
+ int increment = item["increment"].int_value();
+ int rc = item["RC"].int_value();
+
+ int major = 0;
+ int minor = 0;
+
+ sscanf(version.c_str(), "%d.%d", &major, &minor);
+#if OBS_RELEASE_CANDIDATE > 0
+ if (major == OBS_RELEASE_CANDIDATE_MAJOR &&
+ minor == OBS_RELEASE_CANDIDATE_MINOR &&
+ rc == OBS_RELEASE_CANDIDATE) {
+#else
+ if (major == LIBOBS_API_MAJOR_VER &&
+ minor == LIBOBS_API_MINOR_VER &&
+ rc == 0) {
+#endif
+ info_url = url;
+ info_increment = increment;
+ }
+ }
+
+ /* this version was not found, or no info for this version */
+ if (info_increment == -1) {
+ return;
+ }
+
+#if OBS_RELEASE_CANDIDATE > 0
+ uint32_t lastVersion = config_get_int(App()->GlobalConfig(), "General",
+ "LastRCVersion");
+#else
+ uint32_t lastVersion = config_get_int(App()->GlobalConfig(), "General",
+ "LastVersion");
+#endif
+
+ int current_version_increment = -1;
+
+#if OBS_RELEASE_CANDIDATE > 0
+ if (lastVersion < OBS_RELEASE_CANDIDATE_VER) {
+#else
+ if (lastVersion < LIBOBS_API_VER) {
+#endif
+ config_set_int(App()->GlobalConfig(), "General",
+ "InfoIncrement", -1);
+ } else {
+ current_version_increment = config_get_int(
+ App()->GlobalConfig(), "General",
+ "InfoIncrement");
+ }
+
+ if (info_increment <= current_version_increment) {
+ return;
+ }
+
+ config_set_int(App()->GlobalConfig(), "General",
+ "InfoIncrement", info_increment);
+
+ QDialog dlg(this);
+ dlg.setWindowTitle("What's New");
+ dlg.resize(700, 600);
+
+ QCefWidget *cefWidget = create_browser_widget(nullptr, info_url);
+ if (!cefWidget) {
+ return;
+ }
+
+ connect(cefWidget, SIGNAL(titleChanged(const QString &)),
+ &dlg, SLOT(setWindowTitle(const QString &)));
+
+ QPushButton *close = new QPushButton(QTStr("Close"));
+ connect(close, &QAbstractButton::clicked,
+ &dlg, &QDialog::accept);
+
+ QHBoxLayout *bottomLayout = new QHBoxLayout();
+ bottomLayout->addStretch();
+ bottomLayout->addWidget(close);
+ bottomLayout->addStretch();
+
+ QVBoxLayout *topLayout = new QVBoxLayout(&dlg);
+ topLayout->addWidget(cefWidget);
+ topLayout->addLayout(bottomLayout);
+
+ dlg.exec();
+#else
+ UNUSED_PARAMETER(text);
+#endif
}
void OBSBasic::UpdateMultiviewProjectorMenu()
@@ -1786,7 +1949,7 @@ void OBSBasic::CreateHotkeys()
[](void *data, obs_hotkey_pair_id, obs_hotkey_t*, bool pressed) \
{ \
OBSBasic &basic = *static_cast(data); \
- if (pred && pressed) { \
+ if ((pred) && pressed) { \
blog(LOG_INFO, log_action " due to hotkey"); \
method(); \
return true; \
@@ -1799,9 +1962,11 @@ void OBSBasic::CreateHotkeys()
Str("Basic.Main.StartStreaming"),
"OBSBasic.StopStreaming",
Str("Basic.Main.StopStreaming"),
- MAKE_CALLBACK(!basic.outputHandler->StreamingActive(),
+ MAKE_CALLBACK(!basic.outputHandler->StreamingActive() &&
+ basic.ui->streamButton->isEnabled(),
basic.StartStreaming, "Starting stream"),
- MAKE_CALLBACK(basic.outputHandler->StreamingActive(),
+ MAKE_CALLBACK(basic.outputHandler->StreamingActive() &&
+ basic.ui->streamButton->isEnabled(),
basic.StopStreaming, "Stopping stream"),
this, this);
LoadHotkeyPair(streamingHotkeys,
@@ -1827,9 +1992,11 @@ void OBSBasic::CreateHotkeys()
Str("Basic.Main.StartRecording"),
"OBSBasic.StopRecording",
Str("Basic.Main.StopRecording"),
- MAKE_CALLBACK(!basic.outputHandler->RecordingActive(),
+ MAKE_CALLBACK(!basic.outputHandler->RecordingActive() &&
+ !basic.ui->recordButton->isChecked(),
basic.StartRecording, "Starting recording"),
- MAKE_CALLBACK(basic.outputHandler->RecordingActive(),
+ MAKE_CALLBACK(basic.outputHandler->RecordingActive() &&
+ basic.ui->recordButton->isChecked(),
basic.StopRecording, "Stopping recording"),
this, this);
LoadHotkeyPair(recordingHotkeys,
@@ -1894,6 +2061,8 @@ OBSBasic::~OBSBasic()
if (updateCheckThread && updateCheckThread->isRunning())
updateCheckThread->wait();
+ delete multiviewProjectorMenu;
+ delete trayMenu;
delete programOptions;
delete program;
@@ -1950,6 +2119,10 @@ OBSBasic::~OBSBasic()
config_set_int(App()->GlobalConfig(), "General", "LastVersion",
LIBOBS_API_VER);
+#if OBS_RELEASE_CANDIDATE > 0
+ config_set_int(App()->GlobalConfig(), "General", "LastRCVersion",
+ OBS_RELEASE_CANDIDATE_VER);
+#endif
bool alwaysOnTop = IsAlwaysOnTop(this);
@@ -2049,7 +2222,7 @@ OBSSceneItem OBSBasic::GetSceneItem(QListWidgetItem *item)
OBSSceneItem OBSBasic::GetCurrentSceneItem()
{
- return GetSceneItem(GetTopSelectedSourceItem());
+ return ui->sources->Get(GetTopSelectedSourceItem());
}
void OBSBasic::UpdatePreviewScalingMenu()
@@ -2072,32 +2245,6 @@ void OBSBasic::UpdatePreviewScalingMenu()
scalingAmount == float(ovi.output_width) / float(ovi.base_width));
}
-void OBSBasic::UpdateSources(OBSScene scene)
-{
- ClearListItems(ui->sources);
-
- obs_scene_enum_items(scene,
- [] (obs_scene_t *scene, obs_sceneitem_t *item, void *p)
- {
- OBSBasic *window = static_cast(p);
- window->InsertSceneItem(item);
-
- UNUSED_PARAMETER(scene);
- return true;
- }, this);
-}
-
-void OBSBasic::InsertSceneItem(obs_sceneitem_t *item)
-{
- QListWidgetItem *listItem = new QListWidgetItem();
- SetOBSRef(listItem, OBSSceneItem(item));
-
- ui->sources->insertItem(0, listItem);
- ui->sources->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
-
- SetupVisibilityItem(ui->sources, listItem, item);
-}
-
void OBSBasic::CreateInteractionWindow(obs_source_t *source)
{
if (interaction)
@@ -2161,8 +2308,6 @@ void OBSBasic::AddScene(OBSSource source)
container.handlers.assign({
std::make_shared(handler, "item_add",
OBSBasic::SceneItemAdded, this),
- std::make_shared(handler, "item_remove",
- OBSBasic::SceneItemRemoved, this),
std::make_shared(handler, "item_select",
OBSBasic::SceneItemSelected, this),
std::make_shared(handler, "item_deselect",
@@ -2224,7 +2369,7 @@ void OBSBasic::RemoveScene(OBSSource source)
if (sel != nullptr) {
if (sel == ui->scenes->currentItem())
- ClearListItems(ui->sources);
+ ui->sources->Clear();
delete sel;
}
@@ -2241,12 +2386,25 @@ void OBSBasic::RemoveScene(OBSSource source)
api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
}
+static bool select_one(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
+{
+ obs_sceneitem_t *selectedItem =
+ reinterpret_cast(param);
+ if (obs_sceneitem_is_group(item))
+ obs_sceneitem_group_enum_items(item, select_one, param);
+
+ obs_sceneitem_select(item, (selectedItem == item));
+
+ UNUSED_PARAMETER(scene);
+ return true;
+}
+
void OBSBasic::AddSceneItem(OBSSceneItem item)
{
obs_scene_t *scene = obs_sceneitem_get_scene(item);
if (GetCurrentScene() == scene)
- InsertSceneItem(item);
+ ui->sources->Add(item);
SaveProject();
@@ -2257,30 +2415,8 @@ void OBSBasic::AddSceneItem(OBSSceneItem item)
obs_source_get_name(itemSource),
obs_source_get_id(itemSource),
obs_source_get_name(sceneSource));
- }
-}
-
-void OBSBasic::RemoveSceneItem(OBSSceneItem item)
-{
- for (int i = 0; i < ui->sources->count(); i++) {
- QListWidgetItem *listItem = ui->sources->item(i);
-
- if (GetOBSRef(listItem) == item) {
- DeleteListItem(ui->sources, listItem);
- break;
- }
- }
-
- SaveProject();
-
- if (!disableSaving) {
- obs_scene_t *scene = obs_sceneitem_get_scene(item);
- obs_source_t *sceneSource = obs_scene_get_source(scene);
- obs_source_t *itemSource = obs_sceneitem_get_source(item);
- blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'",
- obs_source_get_name(itemSource),
- obs_source_get_id(itemSource),
- obs_source_get_name(sceneSource));
+
+ obs_scene_enum_items(scene, select_one, (obs_sceneitem_t*)item);
}
}
@@ -2301,7 +2437,10 @@ void OBSBasic::UpdateSceneSelection(OBSSource source)
ui->scenes->setCurrentItem(items.first());
sceneChanging = false;
- UpdateSources(scene);
+ OBSScene curScene =
+ GetOBSRef(ui->scenes->currentItem());
+ if (api && scene != curScene)
+ api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
}
}
}
@@ -2326,13 +2465,7 @@ void OBSBasic::RenameSources(OBSSource source, QString newName,
volumes[i]->SetName(newName);
}
- std::string newText = newName.toUtf8().constData();
- std::string prevText = prevName.toUtf8().constData();
-
- for (size_t j = 0; j < projectorArray.size(); j++) {
- if (projectorArray.at(j) == prevText)
- projectorArray.at(j) = newText;
- }
+ OBSProjector::RenameProjector(prevName, newName);
SaveProject();
@@ -2348,19 +2481,7 @@ void OBSBasic::SelectSceneItem(OBSScene scene, OBSSceneItem item, bool select)
if (scene != GetCurrentScene() || ignoreSelectionUpdate)
return;
- for (int i = 0; i < ui->sources->count(); i++) {
- QListWidgetItem *witem = ui->sources->item(i);
- QVariant data =
- witem->data(static_cast(QtDataRole::OBSRef));
- if (!data.canConvert())
- continue;
-
- if (item != data.value())
- continue;
-
- witem->setSelected(select);
- break;
- }
+ ui->sources->SelectItem(item, select);
}
static inline bool SourceMixerHidden(obs_source_t *source)
@@ -2501,6 +2622,11 @@ void OBSBasic::VolControlContextMenu()
QAction propertiesAction(QTStr("Properties"), this);
QAction advPropAction(QTStr("Basic.MainMenu.Edit.AdvAudio"), this);
+ QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this);
+ toggleControlLayoutAction.setCheckable(true);
+ toggleControlLayoutAction.setChecked(config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "VerticalVolControl"));
+
/* ------------------- */
connect(&hideAction, &QAction::triggered,
@@ -2525,6 +2651,12 @@ void OBSBasic::VolControlContextMenu()
/* ------------------- */
+ connect(&toggleControlLayoutAction, &QAction::changed, this,
+ &OBSBasic::ToggleVolControlLayout,
+ Qt::DirectConnection);
+
+ /* ------------------- */
+
hideAction.setProperty("volControl",
QVariant::fromValue(vol));
mixerRenameAction.setProperty("volControl",
@@ -2537,23 +2669,40 @@ void OBSBasic::VolControlContextMenu()
/* ------------------- */
- QMenu popup(this);
+ QMenu popup;
popup.addAction(&unhideAllAction);
popup.addAction(&hideAction);
popup.addAction(&mixerRenameAction);
popup.addSeparator();
+ popup.addAction(&toggleControlLayoutAction);
+ popup.addSeparator();
popup.addAction(&filtersAction);
popup.addAction(&propertiesAction);
popup.addAction(&advPropAction);
popup.exec(QCursor::pos());
}
-void OBSBasic::on_mixerScrollArea_customContextMenuRequested()
+void OBSBasic::on_hMixerScrollArea_customContextMenuRequested()
+{
+ StackedMixerAreaContextMenuRequested();
+}
+
+void OBSBasic::on_vMixerScrollArea_customContextMenuRequested()
+{
+ StackedMixerAreaContextMenuRequested();
+}
+
+void OBSBasic::StackedMixerAreaContextMenuRequested()
{
QAction unhideAllAction(QTStr("UnhideAll"), this);
QAction advPropAction(QTStr("Basic.MainMenu.Edit.AdvAudio"), this);
+ QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this);
+ toggleControlLayoutAction.setCheckable(true);
+ toggleControlLayoutAction.setChecked(config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "VerticalVolControl"));
+
/* ------------------- */
connect(&unhideAllAction, &QAction::triggered,
@@ -2566,23 +2715,83 @@ void OBSBasic::on_mixerScrollArea_customContextMenuRequested()
/* ------------------- */
- QMenu popup(this);
+ connect(&toggleControlLayoutAction, &QAction::changed, this,
+ &OBSBasic::ToggleVolControlLayout,
+ Qt::DirectConnection);
+
+ /* ------------------- */
+
+ QMenu popup;
popup.addAction(&unhideAllAction);
popup.addSeparator();
+ popup.addAction(&toggleControlLayoutAction);
+ popup.addSeparator();
popup.addAction(&advPropAction);
popup.exec(QCursor::pos());
}
+void OBSBasic::ToggleMixerLayout(bool vertical)
+{
+ if (vertical) {
+ ui->stackedMixerArea->setMinimumSize(180, 220);
+ ui->stackedMixerArea->setCurrentIndex(1);
+ } else {
+ ui->stackedMixerArea->setMinimumSize(220, 0);
+ ui->stackedMixerArea->setCurrentIndex(0);
+ }
+}
+
+void OBSBasic::ToggleVolControlLayout()
+{
+ bool vertical = !config_get_bool(GetGlobalConfig(), "BasicWindow",
+ "VerticalVolControl");
+ config_set_bool(GetGlobalConfig(), "BasicWindow", "VerticalVolControl",
+ vertical);
+ ToggleMixerLayout(vertical);
+
+ // We need to store it so we can delete current and then add
+ // at the right order
+ 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);
+}
+
void OBSBasic::ActivateAudioSource(OBSSource source)
{
if (SourceMixerHidden(source))
return;
- VolControl *vol = new VolControl(source, true);
+ bool vertical = config_get_bool(GetGlobalConfig(), "BasicWindow",
+ "VerticalVolControl");
+ VolControl *vol = new VolControl(source, true, vertical);
double meterDecayRate = config_get_double(basicConfig, "Audio",
"MeterDecayRate");
vol->SetMeterDecayRate(meterDecayRate);
+
+ uint32_t peakMeterTypeIdx = config_get_uint(basicConfig, "Audio",
+ "PeakMeterType");
+
+ enum obs_peak_meter_type peakMeterType;
+ switch (peakMeterTypeIdx) {
+ case 0:
+ peakMeterType = SAMPLE_PEAK_METER;
+ break;
+ case 1:
+ peakMeterType = TRUE_PEAK_METER;
+ break;
+ default:
+ peakMeterType = SAMPLE_PEAK_METER;
+ break;
+ }
+
+ vol->setPeakMeterType(peakMeterType);
+
vol->setContextMenuPolicy(Qt::CustomContextMenu);
connect(vol, &QWidget::customContextMenuRequested,
@@ -2590,8 +2799,14 @@ void OBSBasic::ActivateAudioSource(OBSSource source)
connect(vol, &VolControl::ConfigClicked,
this, &OBSBasic::VolControlContextMenu);
- volumes.push_back(vol);
- ui->volumeWidgets->layout()->addWidget(vol);
+ InsertQObjectByName(volumes, vol);
+
+ for (auto volume : volumes) {
+ if (vertical)
+ ui->vVolControlLayout->addWidget(volume);
+ else
+ ui->hVolControlLayout->addWidget(volume);
+ }
}
void OBSBasic::DeactivateAudioSource(OBSSource source)
@@ -2607,7 +2822,8 @@ void OBSBasic::DeactivateAudioSource(OBSSource source)
bool OBSBasic::QueryRemoveSource(obs_source_t *source)
{
- if (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE) {
+ if (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE &&
+ !obs_source_is_group(source)) {
int count = ui->scenes->count();
if (count == 1) {
@@ -2681,7 +2897,7 @@ void OBSBasic::CheckForUpdates(bool manualUpdate)
if (updateCheckThread && updateCheckThread->isRunning())
return;
- updateCheckThread = new AutoUpdateThread(manualUpdate);
+ updateCheckThread.reset(new AutoUpdateThread(manualUpdate));
updateCheckThread->start();
#endif
@@ -2742,7 +2958,6 @@ void OBSBasic::DuplicateSelectedScene()
obs_scene_t *scene = obs_scene_duplicate(curScene,
name.c_str(), OBS_SCENE_DUP_REFS);
source = obs_scene_get_source(scene);
- AddScene(source);
SetCurrentScene(source, true);
obs_scene_release(scene);
@@ -2774,62 +2989,12 @@ void OBSBasic::RemoveSelectedSceneItem()
}
}
-struct ReorderInfo {
- int idx = 0;
- OBSBasic *window;
-
- inline ReorderInfo(OBSBasic *window_) : window(window_) {}
-};
-
-void OBSBasic::ReorderSceneItem(obs_sceneitem_t *item, size_t idx)
-{
- int count = ui->sources->count();
- int idx_inv = count - (int)idx - 1;
-
- for (int i = 0; i < count; i++) {
- QListWidgetItem *listItem = ui->sources->item(i);
- OBSSceneItem sceneItem = GetOBSRef(listItem);
-
- if (sceneItem == item) {
- if ((int)idx_inv != i) {
- bool sel = (ui->sources->currentRow() == i);
-
- listItem = TakeListItem(ui->sources, i);
- if (listItem) {
- ui->sources->insertItem(idx_inv,
- listItem);
- SetupVisibilityItem(ui->sources,
- listItem, item);
-
- if (sel)
- ui->sources->setCurrentRow(
- idx_inv);
- }
- }
-
- break;
- }
- }
-}
-
void OBSBasic::ReorderSources(OBSScene scene)
{
- ReorderInfo info(this);
-
if (scene != GetCurrentScene() || ui->sources->IgnoreReorder())
return;
- obs_scene_enum_items(scene,
- [] (obs_scene_t*, obs_sceneitem_t *item, void *p)
- {
- ReorderInfo *info =
- reinterpret_cast(p);
-
- info->window->ReorderSceneItem(item,
- info->idx++);
- return true;
- }, &info);
-
+ ui->sources->ReorderItems();
SaveProject();
}
@@ -2855,16 +3020,6 @@ void OBSBasic::SceneItemAdded(void *data, calldata_t *params)
Q_ARG(OBSSceneItem, OBSSceneItem(item)));
}
-void OBSBasic::SceneItemRemoved(void *data, calldata_t *params)
-{
- OBSBasic *window = static_cast(data);
-
- obs_sceneitem_t *item = (obs_sceneitem_t*)calldata_ptr(params, "item");
-
- QMetaObject::invokeMethod(window, "RemoveSceneItem",
- Q_ARG(OBSSceneItem, OBSSceneItem(item)));
-}
-
void OBSBasic::SceneItemSelected(void *data, calldata_t *params)
{
OBSBasic *window = static_cast(data);
@@ -2890,13 +3045,13 @@ void OBSBasic::SceneItemDeselected(void *data, calldata_t *params)
}
-void OBSBasic::SourceLoaded(void *data, obs_source_t *source)
+void OBSBasic::SourceCreated(void *data, calldata_t *params)
{
- OBSBasic *window = static_cast(data);
+ obs_source_t *source = (obs_source_t*)calldata_ptr(params, "source");
if (obs_scene_from_source(source) != NULL)
- QMetaObject::invokeMethod(window,
- "AddScene",
+ QMetaObject::invokeMethod(static_cast(data),
+ "AddScene", WaitConnection(),
Q_ARG(OBSSource, OBSSource(source)));
}
@@ -3195,8 +3350,10 @@ int OBSBasic::ResetVideo()
ResizeProgram(ovi.base_width, ovi.base_height);
}
- if (ret == OBS_VIDEO_SUCCESS)
+ if (ret == OBS_VIDEO_SUCCESS) {
OBSBasicStats::InitializeValues();
+ OBSProjector::UpdateMultiviewProjectors();
+ }
return ret;
}
@@ -3318,6 +3475,7 @@ void OBSBasic::CloseDialogs()
}
if (!stats.isNull()) stats->close(); //call close to save Stats geometry
+ if (!remux.isNull()) remux->close();
}
void OBSBasic::EnumDialogs()
@@ -3351,7 +3509,7 @@ void OBSBasic::ClearSceneData()
ClearVolumeControls();
ClearListItems(ui->scenes);
- ClearListItems(ui->sources);
+ ui->sources->Clear();
ClearQuickTransitions();
ui->transitions->clear();
@@ -3413,6 +3571,8 @@ void OBSBasic::closeEvent(QCloseEvent *event)
blog(LOG_INFO, SHUTDOWN_SEPARATOR);
+ if (introCheckThread)
+ introCheckThread->wait();
if (updateCheckThread)
updateCheckThread->wait();
if (logUploadThread)
@@ -3449,20 +3609,33 @@ 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 *adv_path = strcmp(type, "Standard") ?
+ config_get_string(basicConfig, "AdvOut", "FFFilePath") :
+ config_get_string(basicConfig, "AdvOut", "RecFilePath");
const char *path = strcmp(mode, "Advanced") ?
- config_get_string(basicConfig, "SimpleOutput", "FilePath") :
- config_get_string(basicConfig, "AdvOut", "RecFilePath");
+ config_get_string(basicConfig, "SimpleOutput", "FilePath") :
+ adv_path;
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
void OBSBasic::on_actionRemux_triggered()
{
+ if (!remux.isNull()) {
+ remux->show();
+ remux->raise();
+ return;
+ }
+
const char *mode = config_get_string(basicConfig, "Output", "Mode");
const char *path = strcmp(mode, "Advanced") ?
config_get_string(basicConfig, "SimpleOutput", "FilePath") :
config_get_string(basicConfig, "AdvOut", "RecFilePath");
- OBSRemux remux(path, this);
- remux.exec();
+
+ OBSRemux *remuxDlg;
+ remuxDlg = new OBSRemux(path, this);
+ remuxDlg->show();
+ remux = remuxDlg;
}
void OBSBasic::on_action_Settings_triggered()
@@ -3514,6 +3687,9 @@ void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current,
SetCurrentScene(source);
+ if (api)
+ api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
+
UNUSED_PARAMETER(prev);
}
@@ -3564,8 +3740,7 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
popup.addAction(QTStr("Rename"),
this, SLOT(EditSceneName()));
popup.addAction(QTStr("Remove"),
- this, SLOT(RemoveSelectedScene()),
- DeleteKeys.front());
+ this, SLOT(RemoveSelectedScene()));
popup.addSeparator();
order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveUp"),
@@ -3616,7 +3791,7 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
multiviewAction->setCheckable(true);
multiviewAction->setChecked(show);
- auto showInMultiview = [this] (OBSData data)
+ auto showInMultiview = [] (OBSData data)
{
bool show = obs_data_get_bool(data,
"show_in_multiview");
@@ -3673,7 +3848,6 @@ void OBSBasic::on_actionAddScene_triggered()
obs_scene_t *scene = obs_scene_create(name.c_str());
source = obs_scene_get_source(scene);
- AddScene(source);
SetCurrentScene(source);
obs_scene_release(scene);
}
@@ -3706,6 +3880,8 @@ void OBSBasic::ChangeSceneIndex(bool relative, int offset, int invalidIdx)
item->setSelected(true);
sceneChanging = false;
+
+ OBSProjector::UpdateMultiviewProjectors();
}
void OBSBasic::on_actionSceneUp_triggered()
@@ -3729,44 +3905,10 @@ void OBSBasic::MoveSceneToBottom()
ui->scenes->count() - 1);
}
-void OBSBasic::on_sources_itemSelectionChanged()
-{
- SignalBlocker sourcesSignalBlocker(ui->sources);
-
- auto updateItemSelection = [&]()
- {
- ignoreSelectionUpdate = true;
- for (int i = 0; i < ui->sources->count(); i++)
- {
- QListWidgetItem *wItem = ui->sources->item(i);
- OBSSceneItem item = GetOBSRef(wItem);
-
- obs_sceneitem_select(item, wItem->isSelected());
- }
- ignoreSelectionUpdate = false;
- };
- using updateItemSelection_t = decltype(updateItemSelection);
-
- obs_scene_atomic_update(GetCurrentScene(),
- [](void *data, obs_scene_t *)
- {
- (*static_cast(data))();
- }, static_cast(&updateItemSelection));
-}
-
void OBSBasic::EditSceneItemName()
{
- QListWidgetItem *item = GetTopSelectedSourceItem();
- Qt::ItemFlags flags = item->flags();
- OBSSceneItem sceneItem= GetOBSRef(item);
- obs_source_t *source = obs_sceneitem_get_source(sceneItem);
- const char *name = obs_source_get_name(source);
-
- item->setText(QT_UTF8(name));
- item->setFlags(flags | Qt::ItemIsEditable);
- ui->sources->removeItemWidget(item);
- ui->sources->editItem(item);
- item->setFlags(flags);
+ int idx = GetTopSelectedSourceItem();
+ ui->sources->Edit(idx);
}
void OBSBasic::SetDeinterlacingMode()
@@ -3866,7 +4008,64 @@ QMenu *OBSBasic::AddScaleFilteringMenu(obs_sceneitem_t *item)
return menu;
}
-void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
+QMenu *OBSBasic::AddBackgroundColorMenu(obs_sceneitem_t *item)
+{
+ QMenu *menu = new QMenu(QTStr("ChangeBG"));
+ QAction *action;
+
+ menu->setStyleSheet(QString(
+ "*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}" \
+ "*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}" \
+ "*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}" \
+ "*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}" \
+ "*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}" \
+ "*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}" \
+ "*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}" \
+ "*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
+
+ obs_data_t *privData = obs_sceneitem_get_private_settings(item);
+ obs_data_release(privData);
+
+ obs_data_set_default_int(privData, "color-preset", 0);
+ int preset = obs_data_get_int(privData, "color-preset");
+
+ action = menu->addAction(QTStr("Clear"), this,
+ + SLOT(ColorChange()));
+ action->setCheckable(true);
+ action->setProperty("bgColor", 0);
+ action->setChecked(preset == 0);
+
+ action = menu->addAction(QTStr("CustomColor"), this,
+ + SLOT(ColorChange()));
+ action->setCheckable(true);
+ action->setProperty("bgColor", 1);
+ action->setChecked(preset == 1);
+
+ menu->addSeparator();
+
+ QWidgetAction *widgetAction = new QWidgetAction(menu);
+ ColorSelect *select = new ColorSelect(menu);
+ widgetAction->setDefaultWidget(select);
+
+ for (int i = 1; i < 9; i++) {
+ stringstream button;
+ button << "preset" << i;
+ QPushButton *colorButton = select->findChild(
+ button.str().c_str());
+ if (preset == i + 1)
+ colorButton->setStyleSheet("border: 2px solid black");
+
+ colorButton->setProperty("bgColor", i);
+ select->connect(colorButton, SIGNAL(released()), this,
+ SLOT(ColorChange()));
+ }
+
+ menu->addAction(widgetAction);
+
+ return menu;
+}
+
+void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
{
QMenu popup(this);
QPointer previewProjector;
@@ -3907,6 +4106,17 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
ui->actionCopyFilters->setEnabled(false);
ui->actionCopySource->setEnabled(false);
+ if (ui->sources->MultipleBaseSelected()) {
+ popup.addSeparator();
+ popup.addAction(QTStr("Basic.Main.GroupItems"),
+ ui->sources, SLOT(GroupSelectedItems()));
+
+ } else if (ui->sources->GroupsSelected()) {
+ popup.addSeparator();
+ popup.addAction(QTStr("Basic.Main.Ungroup"),
+ ui->sources, SLOT(UngroupSelectedGroups()));
+ }
+
popup.addSeparator();
popup.addAction(ui->actionCopySource);
popup.addAction(ui->actionPasteRef);
@@ -3918,11 +4128,11 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
popup.addAction(ui->actionPasteFilters);
popup.addSeparator();
- if (item) {
+ if (idx != -1) {
if (addSourceMenu)
popup.addSeparator();
- OBSSceneItem sceneItem = GetSceneItem(item);
+ OBSSceneItem sceneItem = ui->sources->Get(idx);
obs_source_t *source = obs_sceneitem_get_source(sceneItem);
uint32_t flags = obs_source_get_output_flags(source);
bool isAsyncVideo = (flags & OBS_SOURCE_ASYNC_VIDEO) ==
@@ -3931,11 +4141,11 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
OBS_SOURCE_AUDIO;
QAction *action;
+ popup.addMenu(AddBackgroundColorMenu(sceneItem));
popup.addAction(QTStr("Rename"), this,
SLOT(EditSceneItemName()));
popup.addAction(QTStr("Remove"), this,
- SLOT(on_actionRemoveSource_triggered()),
- DeleteKeys.front());
+ SLOT(on_actionRemoveSource_triggered()));
popup.addSeparator();
popup.addMenu(ui->orderMenu);
popup.addMenu(ui->transformMenu);
@@ -3985,6 +4195,8 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
ui->actionCopyFilters->setEnabled(true);
ui->actionCopySource->setEnabled(true);
+ } else {
+ ui->actionPasteFilters->setEnabled(false);
}
popup.exec(QCursor::pos());
@@ -3992,20 +4204,10 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
{
- if (ui->scenes->count())
- CreateSourcePopupMenu(ui->sources->itemAt(pos), false);
-}
-
-void OBSBasic::on_sources_itemDoubleClicked(QListWidgetItem *witem)
-{
- if (!witem)
- return;
-
- OBSSceneItem item = GetSceneItem(witem);
- OBSSource source = obs_sceneitem_get_source(item);
-
- if (source)
- CreatePropertiesWindow(source);
+ if (ui->scenes->count()) {
+ QModelIndex idx = ui->sources->indexAt(pos);
+ CreateSourcePopupMenu(idx.row(), false);
+ }
}
void OBSBasic::on_scenes_itemDoubleClicked(QListWidgetItem *witem)
@@ -4031,7 +4233,7 @@ void OBSBasic::AddSource(const char *id)
if (id && *id) {
OBSBasicSourceSelect sourceSelect(this, id);
sourceSelect.exec();
- if (sourceSelect.newSource)
+ if (sourceSelect.newSource && strcmp(id, "group") != 0)
CreatePropertiesWindow(sourceSelect.newSource);
}
}
@@ -4089,6 +4291,13 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu()
addSource(popup, "scene", Str("Basic.Scene"));
+ popup->addSeparator();
+ QAction *addGroup = new QAction(QTStr("Group"), this);
+ addGroup->setData(QT_UTF8("group"));
+ connect(addGroup, SIGNAL(triggered(bool)),
+ this, SLOT(AddSourceFromAction()));
+ popup->addAction(addGroup);
+
if (!foundDeprecated) {
delete deprecated;
deprecated = nullptr;
@@ -4099,6 +4308,7 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu()
popup = nullptr;
} else if (foundDeprecated) {
+ popup->addSeparator();
popup->addMenu(deprecated);
}
@@ -4124,7 +4334,7 @@ void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
return;
}
- QPointer popup = CreateAddSourcePopupMenu();
+ QScopedPointer popup(CreateAddSourcePopupMenu());
if (popup)
popup->exec(pos);
}
@@ -4134,20 +4344,24 @@ void OBSBasic::on_actionAddSource_triggered()
AddSourcePopupMenu(QCursor::pos());
}
+static bool remove_items(obs_scene_t *, obs_sceneitem_t *item, void *param)
+{
+ vector &items =
+ *reinterpret_cast*>(param);
+
+ if (obs_sceneitem_selected(item)) {
+ items.emplace_back(item);
+ } else if (obs_sceneitem_is_group(item)) {
+ obs_sceneitem_group_enum_items(item, remove_items, &items);
+ }
+ return true;
+};
+
void OBSBasic::on_actionRemoveSource_triggered()
{
vector items;
- auto func = [] (obs_scene_t *, obs_sceneitem_t *item, void *param)
- {
- vector &items =
- *reinterpret_cast*>(param);
- if (obs_sceneitem_selected(item))
- items.emplace_back(item);
- return true;
- };
-
- obs_scene_enum_items(GetCurrentScene(), func, &items);
+ obs_scene_enum_items(GetCurrentScene(), remove_items, &items);
if (!items.size())
return;
@@ -4274,14 +4488,13 @@ void OBSBasic::UploadLog(const char *subdir, const char *file)
if (logUploadThread) {
logUploadThread->wait();
- delete logUploadThread;
}
RemoteTextThread *thread = new RemoteTextThread(
- "https://hastebin.com/documents",
+ "https://obsproject.com/logs/upload",
"text/plain", ss.str().c_str());
- logUploadThread = thread;
+ logUploadThread.reset(thread);
connect(thread, &RemoteTextThread::Result,
this, &OBSBasic::logUploadFinished);
logUploadThread->start();
@@ -4355,8 +4568,7 @@ void OBSBasic::logUploadFinished(const QString &text, const QString &error)
}
obs_data_t *returnData = obs_data_create_from_json(QT_TO_UTF8(text));
- string resURL = "https://hastebin.com/";
- resURL += obs_data_get_string(returnData, "key");
+ string resURL = obs_data_get_string(returnData, "url");
QString logURL = resURL.c_str();
obs_data_release(returnData);
@@ -4413,26 +4625,6 @@ void OBSBasic::SceneNameEdited(QWidget *editor,
UNUSED_PARAMETER(endHint);
}
-void OBSBasic::SceneItemNameEdited(QWidget *editor,
- QAbstractItemDelegate::EndEditHint endHint)
-{
- OBSSceneItem item = GetCurrentSceneItem();
- QLineEdit *edit = qobject_cast(editor);
- string text = QT_TO_UTF8(edit->text().trimmed());
-
- if (!item)
- return;
-
- obs_source_t *source = obs_sceneitem_get_source(item);
- RenameListItem(this, ui->sources, source, text);
-
- QListWidgetItem *listItem = ui->sources->currentItem();
- listItem->setText(QString());
- SetupVisibilityItem(ui->sources, listItem, item);
-
- UNUSED_PARAMETER(endHint);
-}
-
void OBSBasic::OpenFilters()
{
OBSSceneItem item = GetCurrentSceneItem();
@@ -4770,7 +4962,9 @@ void OBSBasic::StartRecording()
api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING);
SaveProject();
- outputHandler->StartRecording();
+
+ if (!outputHandler->StartRecording())
+ ui->recordButton->setChecked(false);
}
void OBSBasic::RecordStopping()
@@ -4870,6 +5064,11 @@ void OBSBasic::StartReplayBuffer()
if (disableOutputsRef)
return;
+ if (!NoSourcesConfirmation()) {
+ replayBufferButton->setChecked(false);
+ return;
+ }
+
obs_output_t *output = outputHandler->replayBuffer;
obs_data_t *hotkeys = obs_hotkeys_save_output(output);
obs_data_array_t *bindings = obs_data_get_array(hotkeys,
@@ -4889,7 +5088,8 @@ void OBSBasic::StartReplayBuffer()
api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING);
SaveProject();
- outputHandler->StartReplayBuffer();
+ if (!outputHandler->StartReplayBuffer())
+ replayBufferButton->setChecked(false);
}
void OBSBasic::ReplayBufferStopping()
@@ -4999,6 +5199,28 @@ void OBSBasic::ReplayBufferStop(int code)
OnDeactivate();
}
+bool OBSBasic::NoSourcesConfirmation()
+{
+ if (CountVideoSources() == 0 && isVisible()) {
+ QString msg;
+ msg = QTStr("NoSources.Text");
+ msg += "\n\n";
+ msg += QTStr("NoSources.Text.AddSource");
+
+ QMessageBox messageBox(QMessageBox::Question,
+ QTStr("NoSources.title"),
+ msg,
+ QMessageBox::Yes | QMessageBox::No,
+ this);
+ messageBox.setDefaultButton(QMessageBox::No);
+
+ if (QMessageBox::No == messageBox.exec())
+ return false;
+ }
+
+ return true;
+}
+
void OBSBasic::on_streamButton_clicked()
{
if (outputHandler->StreamingActive()) {
@@ -5011,12 +5233,19 @@ void OBSBasic::on_streamButton_clicked()
QTStr("ConfirmStop.Title"),
QTStr("ConfirmStop.Text"));
- if (button == QMessageBox::No)
+ if (button == QMessageBox::No) {
+ ui->streamButton->setChecked(true);
return;
+ }
}
StopStreaming();
} else {
+ if (!NoSourcesConfirmation()) {
+ ui->streamButton->setChecked(false);
+ return;
+ }
+
bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
"WarnBeforeStartingStream");
@@ -5026,8 +5255,10 @@ void OBSBasic::on_streamButton_clicked()
QTStr("ConfirmStart.Title"),
QTStr("ConfirmStart.Text"));
- if (button == QMessageBox::No)
+ if (button == QMessageBox::No) {
+ ui->streamButton->setChecked(false);
return;
+ }
}
StartStreaming();
@@ -5036,10 +5267,16 @@ void OBSBasic::on_streamButton_clicked()
void OBSBasic::on_recordButton_clicked()
{
- if (outputHandler->RecordingActive())
+ if (outputHandler->RecordingActive()) {
StopRecording();
- else
+ } else {
+ if (!NoSourcesConfirmation()) {
+ ui->recordButton->setChecked(false);
+ return;
+ }
+
StartRecording();
+ }
}
void OBSBasic::on_settingsButton_clicked()
@@ -5059,6 +5296,12 @@ void OBSBasic::on_actionWebsite_triggered()
QDesktopServices::openUrl(url);
}
+void OBSBasic::on_actionDiscord_triggered()
+{
+ QUrl url = QUrl("https://obsproject.com/discord", QUrl::TolerantMode);
+ QDesktopServices::openUrl(url);
+}
+
void OBSBasic::on_actionShowSettingsFolder_triggered()
{
char path[512];
@@ -5079,13 +5322,11 @@ void OBSBasic::on_actionShowProfileFolder_triggered()
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
-QListWidgetItem *OBSBasic::GetTopSelectedSourceItem()
+int OBSBasic::GetTopSelectedSourceItem()
{
- QList selectedItems = ui->sources->selectedItems();
- QListWidgetItem *topItem = nullptr;
- if (selectedItems.size() != 0)
- topItem = selectedItems[0];
- return topItem;
+ QModelIndexList selectedItems =
+ ui->sources->selectionModel()->selectedIndexes();
+ return selectedItems.count() ? selectedItems[0].row() : -1;
}
void OBSBasic::on_preview_customContextMenuRequested(const QPoint &pos)
@@ -5145,12 +5386,14 @@ void OBSBasic::on_previewDisabledLabel_customContextMenuRequested(
void OBSBasic::on_actionAlwaysOnTop_triggered()
{
- CloseDialogs();
-
+#ifndef _WIN32
/* Make sure all dialogs are safely and successfully closed before
* switching the always on top mode due to the fact that windows all
* have to be recreated, so queue the actual toggle to happen after
* all events related to closing the dialogs have finished */
+ CloseDialogs();
+#endif
+
QMetaObject::invokeMethod(this, "ToggleAlwaysOnTop",
Qt::QueuedConnection);
}
@@ -5290,36 +5533,38 @@ void OBSBasic::on_actionPasteTransform_triggered()
obs_scene_enum_items(GetCurrentScene(), func, nullptr);
}
+static bool reset_tr(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
+{
+ if (obs_sceneitem_is_group(item))
+ obs_sceneitem_group_enum_items(item, reset_tr, nullptr);
+ if (!obs_sceneitem_selected(item))
+ return true;
+
+ obs_sceneitem_defer_update_begin(item);
+
+ obs_transform_info info;
+ vec2_set(&info.pos, 0.0f, 0.0f);
+ vec2_set(&info.scale, 1.0f, 1.0f);
+ info.rot = 0.0f;
+ info.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
+ info.bounds_type = OBS_BOUNDS_NONE;
+ info.bounds_alignment = OBS_ALIGN_CENTER;
+ vec2_set(&info.bounds, 0.0f, 0.0f);
+ obs_sceneitem_set_info(item, &info);
+
+ obs_sceneitem_crop crop = {};
+ obs_sceneitem_set_crop(item, &crop);
+
+ obs_sceneitem_defer_update_end(item);
+
+ UNUSED_PARAMETER(scene);
+ UNUSED_PARAMETER(param);
+ return true;
+}
+
void OBSBasic::on_actionResetTransform_triggered()
{
- auto func = [] (obs_scene_t *scene, obs_sceneitem_t *item, void *param)
- {
- if (!obs_sceneitem_selected(item))
- return true;
-
- obs_sceneitem_defer_update_begin(item);
-
- obs_transform_info info;
- vec2_set(&info.pos, 0.0f, 0.0f);
- vec2_set(&info.scale, 1.0f, 1.0f);
- info.rot = 0.0f;
- info.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
- info.bounds_type = OBS_BOUNDS_NONE;
- info.bounds_alignment = OBS_ALIGN_CENTER;
- vec2_set(&info.bounds, 0.0f, 0.0f);
- obs_sceneitem_set_info(item, &info);
-
- obs_sceneitem_crop crop = {};
- obs_sceneitem_set_crop(item, &crop);
-
- obs_sceneitem_defer_update_end(item);
-
- UNUSED_PARAMETER(scene);
- UNUSED_PARAMETER(param);
- return true;
- };
-
- obs_scene_enum_items(GetCurrentScene(), func, nullptr);
+ obs_scene_enum_items(GetCurrentScene(), reset_tr, nullptr);
}
static void GetItemBox(obs_sceneitem_t *item, vec3 &tl, vec3 &br)
@@ -5367,6 +5612,9 @@ static void SetItemTL(obs_sceneitem_t *item, const vec3 &tl)
static bool RotateSelectedSources(obs_scene_t *scene, obs_sceneitem_t *item,
void *param)
{
+ if (obs_sceneitem_is_group(item))
+ obs_sceneitem_group_enum_items(item, RotateSelectedSources,
+ param);
if (!obs_sceneitem_selected(item))
return true;
@@ -5379,10 +5627,11 @@ static bool RotateSelectedSources(obs_scene_t *scene, obs_sceneitem_t *item,
else if (rot <= -360.0f) rot += 360.0f;
obs_sceneitem_set_rot(item, rot);
+ obs_sceneitem_force_update_transform(item);
+
SetItemTL(item, tl);
UNUSED_PARAMETER(scene);
- UNUSED_PARAMETER(param);
return true;
};
@@ -5409,6 +5658,9 @@ static bool MultiplySelectedItemScale(obs_scene_t *scene, obs_sceneitem_t *item,
{
vec2 &mul = *reinterpret_cast(param);
+ if (obs_sceneitem_is_group(item))
+ obs_sceneitem_group_enum_items(item, MultiplySelectedItemScale,
+ param);
if (!obs_sceneitem_selected(item))
return true;
@@ -5419,6 +5671,8 @@ static bool MultiplySelectedItemScale(obs_scene_t *scene, obs_sceneitem_t *item,
vec2_mul(&scale, &scale, &mul);
obs_sceneitem_set_scale(item, &scale);
+ obs_sceneitem_force_update_transform(item);
+
SetItemTL(item, tl);
UNUSED_PARAMETER(scene);
@@ -5446,6 +5700,9 @@ static bool CenterAlignSelectedItems(obs_scene_t *scene, obs_sceneitem_t *item,
{
obs_bounds_type boundsType = *reinterpret_cast(param);
+ if (obs_sceneitem_is_group(item))
+ obs_sceneitem_group_enum_items(item, CenterAlignSelectedItems,
+ param);
if (!obs_sceneitem_selected(item))
return true;
@@ -5483,39 +5740,38 @@ void OBSBasic::on_actionStretchToScreen_triggered()
&boundsType);
}
+static bool center_to_scene(obs_scene_t *, obs_sceneitem_t *item, void *)
+{
+ vec3 tl, br, itemCenter, screenCenter, offset;
+ obs_video_info ovi;
+
+ if (obs_sceneitem_is_group(item))
+ obs_sceneitem_group_enum_items(item, center_to_scene, nullptr);
+ if (!obs_sceneitem_selected(item))
+ return true;
+
+ obs_get_video_info(&ovi);
+
+ vec3_set(&screenCenter, float(ovi.base_width),
+ float(ovi.base_height), 0.0f);
+ vec3_mulf(&screenCenter, &screenCenter, 0.5f);
+
+ GetItemBox(item, tl, br);
+
+ vec3_sub(&itemCenter, &br, &tl);
+ vec3_mulf(&itemCenter, &itemCenter, 0.5f);
+ vec3_add(&itemCenter, &itemCenter, &tl);
+
+ vec3_sub(&offset, &screenCenter, &itemCenter);
+ vec3_add(&tl, &tl, &offset);
+
+ SetItemTL(item, tl);
+ return true;
+};
+
void OBSBasic::on_actionCenterToScreen_triggered()
{
- auto func = [] (obs_scene_t *scene, obs_sceneitem_t *item, void *param)
- {
- vec3 tl, br, itemCenter, screenCenter, offset;
- obs_video_info ovi;
-
- if (!obs_sceneitem_selected(item))
- return true;
-
- obs_get_video_info(&ovi);
-
- vec3_set(&screenCenter, float(ovi.base_width),
- float(ovi.base_height), 0.0f);
- vec3_mulf(&screenCenter, &screenCenter, 0.5f);
-
- GetItemBox(item, tl, br);
-
- vec3_sub(&itemCenter, &br, &tl);
- vec3_mulf(&itemCenter, &itemCenter, 0.5f);
- vec3_add(&itemCenter, &itemCenter, &tl);
-
- vec3_sub(&offset, &screenCenter, &itemCenter);
- vec3_add(&tl, &tl, &offset);
-
- SetItemTL(item, tl);
-
- UNUSED_PARAMETER(scene);
- UNUSED_PARAMETER(param);
- return true;
- };
-
- obs_scene_enum_items(GetCurrentScene(), func, nullptr);
+ obs_scene_enum_items(GetCurrentScene(), center_to_scene, nullptr);
}
void OBSBasic::EnablePreviewDisplay(bool enable)
@@ -5531,44 +5787,56 @@ void OBSBasic::TogglePreview()
EnablePreviewDisplay(previewEnabled);
}
+static bool nudge_callback(obs_scene_t*, obs_sceneitem_t *item, void *param)
+{
+ if (obs_sceneitem_locked(item))
+ return true;
+
+ struct vec2 &offset = *reinterpret_cast(param);
+ struct vec2 pos;
+
+ if (!obs_sceneitem_selected(item)) {
+ if (obs_sceneitem_is_group(item)) {
+ struct vec3 offset3;
+ vec3_set(&offset3, offset.x, offset.y, 0.0f);
+
+ struct matrix4 matrix;
+ obs_sceneitem_get_draw_transform(item, &matrix);
+ vec4_set(&matrix.t, 0.0f, 0.0f, 0.0f, 1.0f);
+ matrix4_inv(&matrix, &matrix);
+ vec3_transform(&offset3, &offset3, &matrix);
+
+ struct vec2 new_offset;
+ vec2_set(&new_offset, offset3.x, offset3.y);
+ obs_sceneitem_group_enum_items(item, nudge_callback,
+ &new_offset);
+ }
+
+ return true;
+ }
+
+ obs_sceneitem_get_pos(item, &pos);
+ vec2_add(&pos, &pos, &offset);
+ obs_sceneitem_set_pos(item, &pos);
+ return true;
+}
+
void OBSBasic::Nudge(int dist, MoveDir dir)
{
if (ui->preview->Locked())
return;
- struct MoveInfo {
- float dist;
- MoveDir dir;
- } info = {(float)dist, dir};
+ struct vec2 offset;
+ vec2_set(&offset, 0.0f, 0.0f);
- auto func = [] (obs_scene_t*, obs_sceneitem_t *item, void *param)
- {
- if (obs_sceneitem_locked(item))
- return true;
+ switch (dir) {
+ case MoveDir::Up: offset.y = (float)-dist; break;
+ case MoveDir::Down: offset.y = (float) dist; break;
+ case MoveDir::Left: offset.x = (float)-dist; break;
+ case MoveDir::Right: offset.x = (float) dist; break;
+ }
- MoveInfo *info = reinterpret_cast(param);
- struct vec2 dir;
- struct vec2 pos;
-
- vec2_set(&dir, 0.0f, 0.0f);
-
- if (!obs_sceneitem_selected(item))
- return true;
-
- switch (info->dir) {
- case MoveDir::Up: dir.y = -info->dist; break;
- case MoveDir::Down: dir.y = info->dist; break;
- case MoveDir::Left: dir.x = -info->dist; break;
- case MoveDir::Right: dir.x = info->dist; break;
- }
-
- obs_sceneitem_get_pos(item, &pos);
- vec2_add(&pos, &pos, &dir);
- obs_sceneitem_set_pos(item, &pos);
- return true;
- };
-
- obs_scene_enum_items(GetCurrentScene(), func, &info);
+ obs_scene_enum_items(GetCurrentScene(), nudge_callback, &offset);
}
void OBSBasic::NudgeUp() {Nudge(1, MoveDir::Up);}
@@ -5576,40 +5844,17 @@ void OBSBasic::NudgeDown() {Nudge(1, MoveDir::Down);}
void OBSBasic::NudgeLeft() {Nudge(1, MoveDir::Left);}
void OBSBasic::NudgeRight() {Nudge(1, MoveDir::Right);}
-void OBSBasic::OpenProjector(obs_source_t *source, int monitor, bool window,
+OBSProjector *OBSBasic::OpenProjector(obs_source_t *source, int monitor,
QString title, ProjectorType type)
{
/* seriously? 10 monitors? */
if (monitor > 9 || monitor > QGuiApplication::screens().size() - 1)
- return;
+ return nullptr;
- if (!window) {
- delete projectors[monitor];
- projectors[monitor].clear();
- RemoveSavedProjectors(monitor);
- }
-
- OBSProjector *projector = new OBSProjector(nullptr, source, !!window);
- const char *name = obs_source_get_name(source);
-
- if (!window) {
- if (type == ProjectorType::StudioProgram) {
- studioProgramProjectorArray.at((size_t)monitor) = 1;
- } else if (type == ProjectorType::Preview) {
- previewProjectorArray.at((size_t)monitor) = 1;
- } else if (type == ProjectorType::Multiview) {
- multiviewProjectorArray.at((size_t)monitor) = 1;
- } else {
- projectorArray.at((size_t)monitor) = name;
- }
- }
-
- if (!window) {
- projector->Init(monitor, false, nullptr, type);
- projectors[monitor] = projector;
- } else {
- projector->Init(monitor, true, title, type);
+ OBSProjector *projector = new OBSProjector(nullptr, source, monitor,
+ title, type);
+ if (monitor < 0) {
for (auto &projPtr : windowProjectors) {
if (!projPtr) {
projPtr = projector;
@@ -5619,20 +5864,26 @@ void OBSBasic::OpenProjector(obs_source_t *source, int monitor, bool window,
if (projector)
windowProjectors.push_back(projector);
+ } else {
+ delete projectors[monitor];
+ projectors[monitor].clear();
+
+ projectors[monitor] = projector;
}
+
+ return projector;
}
void OBSBasic::OpenStudioProgramProjector()
{
int monitor = sender()->property("monitor").toInt();
- OpenProjector(nullptr, monitor, false, nullptr,
- ProjectorType::StudioProgram);
+ OpenProjector(nullptr, monitor, nullptr, ProjectorType::StudioProgram);
}
void OBSBasic::OpenPreviewProjector()
{
int monitor = sender()->property("monitor").toInt();
- OpenProjector(nullptr, monitor, false, nullptr, ProjectorType::Preview);
+ OpenProjector(nullptr, monitor, nullptr, ProjectorType::Preview);
}
void OBSBasic::OpenSourceProjector()
@@ -5642,14 +5893,14 @@ void OBSBasic::OpenSourceProjector()
if (!item)
return;
- OpenProjector(obs_sceneitem_get_source(item), monitor, false);
+ OpenProjector(obs_sceneitem_get_source(item), monitor, nullptr,
+ ProjectorType::Source);
}
void OBSBasic::OpenMultiviewProjector()
{
int monitor = sender()->property("monitor").toInt();
- OpenProjector(nullptr, monitor, false, nullptr,
- ProjectorType::Multiview);
+ OpenProjector(nullptr, monitor, nullptr, ProjectorType::Multiview);
}
void OBSBasic::OpenSceneProjector()
@@ -5659,114 +5910,112 @@ void OBSBasic::OpenSceneProjector()
if (!scene)
return;
- OpenProjector(obs_scene_get_source(scene), monitor, false);
+ OpenProjector(obs_scene_get_source(scene), monitor, nullptr,
+ ProjectorType::Scene);
}
void OBSBasic::OpenStudioProgramWindow()
{
- int monitor = sender()->property("monitor").toInt();
- QString title = QTStr("StudioProgramWindow");
- OpenProjector(nullptr, monitor, true, title,
+ OpenProjector(nullptr, -1, QTStr("StudioProgramWindow"),
ProjectorType::StudioProgram);
}
void OBSBasic::OpenPreviewWindow()
{
- int monitor = sender()->property("monitor").toInt();
- QString title = QTStr("PreviewWindow");
- OpenProjector(nullptr, monitor, true, nullptr, ProjectorType::Preview);
+ OpenProjector(nullptr, -1, QTStr("PreviewWindow"),
+ ProjectorType::Preview);
}
void OBSBasic::OpenSourceWindow()
{
- int monitor = sender()->property("monitor").toInt();
OBSSceneItem item = GetCurrentSceneItem();
- OBSSource source = obs_sceneitem_get_source(item);
- QString text = QString::fromUtf8(obs_source_get_name(source));
-
- QString title = QTStr("SourceWindow") + " - " + text;
-
if (!item)
return;
- OpenProjector(obs_sceneitem_get_source(item), monitor, true, title);
+ OBSSource source = obs_sceneitem_get_source(item);
+ QString title = QString::fromUtf8(obs_source_get_name(source));
+
+ OpenProjector(obs_sceneitem_get_source(item), -1, title,
+ ProjectorType::Source);
}
void OBSBasic::OpenMultiviewWindow()
{
- int monitor = sender()->property("monitor").toInt();
- OpenProjector(nullptr, monitor, true, "Multiview",
+ OpenProjector(nullptr, -1, QTStr("MultiviewWindowed"),
ProjectorType::Multiview);
}
void OBSBasic::OpenSceneWindow()
{
- int monitor = sender()->property("monitor").toInt();
OBSScene scene = GetCurrentScene();
- OBSSource source = obs_scene_get_source(scene);
- QString text = QString::fromUtf8(obs_source_get_name(source));
-
- QString title = QTStr("SceneWindow") + " - " + text;
-
if (!scene)
return;
- OpenProjector(obs_scene_get_source(scene), monitor, true, title);
+ OBSSource source = obs_scene_get_source(scene);
+ QString title = QString::fromUtf8(obs_source_get_name(source));
+
+ OpenProjector(obs_scene_get_source(scene), -1, title,
+ ProjectorType::Scene);
}
void OBSBasic::OpenSavedProjectors()
{
- bool projectorSave = config_get_bool(GetGlobalConfig(),
- "BasicWindow", "SaveProjectors");
+ for (SavedProjectorInfo *info : savedProjectorsArray) {
+ OBSProjector *projector = nullptr;
+ switch (info->type) {
+ case ProjectorType::Source:
+ case ProjectorType::Scene: {
+ OBSSource source = obs_get_source_by_name(
+ info->name.c_str());
+ if (!source)
+ continue;
- if (projectorSave) {
- for (size_t i = 0; i < projectorArray.size(); i++) {
- if (projectorArray.at(i).empty() == false) {
- OBSSource source = obs_get_source_by_name(
- projectorArray.at(i).c_str());
+ QString title = nullptr;
+ if (info->monitor < 0)
+ title = QString::fromUtf8(
+ obs_source_get_name(source));
- if (!source) {
- RemoveSavedProjectors((int)i);
- obs_source_release(source);
- continue;
- }
+ projector = OpenProjector(source, info->monitor, title,
+ info->type);
- OpenProjector(source, (int)i, false);
- obs_source_release(source);
- }
+ obs_source_release(source);
+ break;
+ }
+ case ProjectorType::Preview: {
+ projector = OpenProjector(nullptr, info->monitor,
+ QTStr("PreviewWindow"),
+ ProjectorType::Preview);
+ break;
+ }
+ case ProjectorType::StudioProgram: {
+ projector = OpenProjector(nullptr, info->monitor,
+ QTStr("StudioProgramWindow"),
+ ProjectorType::StudioProgram);
+ break;
+ }
+ case ProjectorType::Multiview: {
+ projector = OpenProjector(nullptr, info->monitor,
+ QTStr("MultiviewWindowed"),
+ ProjectorType::Multiview);
+ break;
+ }
}
- for (size_t i = 0; i < studioProgramProjectorArray.size(); i++) {
- if (studioProgramProjectorArray.at(i) == 1) {
- OpenProjector(nullptr, (int)i, false, nullptr,
- ProjectorType::StudioProgram);
- }
- }
+ if (projector && !info->geometry.empty()) {
+ QByteArray byteArray = QByteArray::fromBase64(
+ QByteArray(info->geometry.c_str()));
+ projector->restoreGeometry(byteArray);
- for (size_t i = 0; i < previewProjectorArray.size(); i++) {
- if (previewProjectorArray.at(i) == 1) {
- OpenProjector(nullptr, (int)i, false, nullptr,
- ProjectorType::Preview);
- }
- }
-
- for (size_t i = 0; i < multiviewProjectorArray.size(); i++) {
- if (multiviewProjectorArray.at(i) == 1) {
- OpenProjector(nullptr, (int)i, false, nullptr,
- ProjectorType::Multiview);
+ if (!WindowPositionValid(projector->normalGeometry())) {
+ QRect rect = App()->desktop()->geometry();
+ projector->setGeometry(QStyle::alignedRect(
+ Qt::LeftToRight,
+ Qt::AlignCenter, size(), rect));
}
}
}
}
-void OBSBasic::RemoveSavedProjectors(int monitor)
-{
- studioProgramProjectorArray.at((size_t)monitor) = 0;
- multiviewProjectorArray.at((size_t)monitor) = 0;
- previewProjectorArray.at((size_t)monitor) = 0;
- projectorArray.at((size_t)monitor) = "";
-}
-
void OBSBasic::on_actionFullscreenInterface_triggered()
{
if (!fullscreenInterface)
@@ -5978,6 +6227,10 @@ void OBSBasic::SetShowing(bool showing)
setVisible(false);
+#ifdef __APPLE__
+ EnableOSXDockIcon(false);
+#endif
+
} else if (showing && !isVisible()) {
if (showHide)
showHide->setText(QTStr("Basic.SystemTray.Hide"));
@@ -5988,6 +6241,14 @@ void OBSBasic::SetShowing(bool showing)
setVisible(true);
+#ifdef __APPLE__
+ EnableOSXDockIcon(true);
+#endif
+
+ /* raise and activate window to ensure it is on top */
+ raise();
+ activateWindow();
+
/* show all child dialogs that was visible earlier */
if (!visDialogs.isEmpty()) {
for (int i = 0; i < visDialogs.size(); ++i) {
@@ -6022,25 +6283,26 @@ void OBSBasic::ToggleShowHide()
void OBSBasic::SystemTrayInit()
{
- trayIcon = new QSystemTrayIcon(QIcon(":/res/images/obs.png"),
- this);
+ trayIcon.reset(new QSystemTrayIcon(QIcon(":/res/images/obs.png"),
+ this));
trayIcon->setToolTip("OBS Studio");
showHide = new QAction(QTStr("Basic.SystemTray.Show"),
- trayIcon);
+ trayIcon.data());
sysTrayStream = new QAction(QTStr("Basic.Main.StartStreaming"),
- trayIcon);
+ trayIcon.data());
sysTrayRecord = new QAction(QTStr("Basic.Main.StartRecording"),
- trayIcon);
+ trayIcon.data());
sysTrayReplayBuffer = new QAction(QTStr("Basic.Main.StartReplayBuffer"),
- trayIcon);
+ trayIcon.data());
exit = new QAction(QTStr("Exit"),
- trayIcon);
+ trayIcon.data());
if (outputHandler && !outputHandler->replayBuffer)
sysTrayReplayBuffer->setEnabled(false);
- connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
+ connect(trayIcon.data(),
+ SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this,
SLOT(IconActivated(QSystemTrayIcon::ActivationReason)));
connect(showHide, SIGNAL(triggered()),
@@ -6114,6 +6376,9 @@ void OBSBasic::SystemTray(bool firstStarted)
QTimer::singleShot(50, this, SLOT(hide()));
EnablePreviewDisplay(false);
setVisible(false);
+#ifdef __APPLE__
+ EnableOSXDockIcon(false);
+#endif
opt_minimize_tray = false;
}
} else if (sysTrayEnabled) {
@@ -6138,13 +6403,12 @@ bool OBSBasic::sysTrayMinimizeToTray()
void OBSBasic::on_actionCopySource_triggered()
{
- on_actionCopyTransform_triggered();
-
OBSSceneItem item = GetCurrentSceneItem();
-
if (!item)
return;
+ on_actionCopyTransform_triggered();
+
OBSSource source = obs_sceneitem_get_source(item);
copyString = obs_source_get_name(source);
@@ -6161,6 +6425,11 @@ void OBSBasic::on_actionCopySource_triggered()
void OBSBasic::on_actionPasteRef_triggered()
{
+ /* do not allow duplicate refs of the same group in the same scene */
+ OBSScene scene = GetCurrentScene();
+ if (!!obs_scene_get_group(scene, copyString))
+ return;
+
OBSBasicSourceSelect::SourcePaste(copyString, copyVisible, false);
on_actionPasteTransform_triggered();
}
@@ -6199,6 +6468,180 @@ void OBSBasic::on_actionPasteFilters_triggered()
obs_source_copy_filters(dstSource, source);
}
+static void ConfirmColor(SourceTree *sources, const QColor &color,
+ QModelIndexList selectedItems)
+{
+ for (int x = 0; x < selectedItems.count(); x++) {
+ SourceTreeItem *treeItem = sources
+ ->GetItemWidget(selectedItems[x].row());
+ treeItem->setStyleSheet("background: "
+ + color.name(QColor::HexArgb));
+ treeItem->style()->unpolish(treeItem);
+ treeItem->style()->polish(treeItem);
+
+ OBSSceneItem sceneItem = sources->Get(
+ selectedItems[x].row());
+ obs_data_t *privData =
+ obs_sceneitem_get_private_settings(sceneItem);
+ obs_data_set_int(privData, "color-preset", 1);
+ obs_data_set_string(privData, "color",
+ QT_TO_UTF8(color.name(
+ QColor::HexArgb)));
+ obs_data_release(privData);
+ }
+}
+
+void OBSBasic::ColorChange()
+{
+ QModelIndexList selectedItems =
+ ui->sources->selectionModel()->selectedIndexes();
+ QAction *action = qobject_cast(sender());
+ QPushButton *colorButton = qobject_cast(sender());
+
+ if (selectedItems.count() == 0)
+ return;
+
+ if (colorButton) {
+ int preset = colorButton->property("bgColor").value();
+
+ for (int x = 0; x < selectedItems.count(); x++) {
+ SourceTreeItem *treeItem = ui->sources
+ ->GetItemWidget(selectedItems[x].row());
+ treeItem->setStyleSheet("");
+ treeItem->setProperty("bgColor", preset);
+ treeItem->style()->unpolish(treeItem);
+ treeItem->style()->polish(treeItem);
+
+ OBSSceneItem sceneItem = ui->sources->Get(
+ selectedItems[x].row());
+ obs_data_t *privData =
+ obs_sceneitem_get_private_settings(sceneItem);
+ obs_data_set_int(privData, "color-preset", preset + 1);
+ obs_data_set_string(privData, "color", "");
+ obs_data_release(privData);
+ }
+
+ for (int i = 1; i < 9; i++) {
+ stringstream button;
+ button << "preset" << i;
+ QPushButton *cButton = colorButton->parentWidget()
+ ->findChild(button.str().c_str());
+ cButton->setStyleSheet("border: 1px solid black");
+ }
+
+ colorButton->setStyleSheet("border: 2px solid black");
+ } else if (action) {
+ int preset = action->property("bgColor").value();
+
+ if (preset == 1) {
+ OBSSceneItem curSceneItem = GetCurrentSceneItem();
+ SourceTreeItem *curTreeItem =
+ GetItemWidgetFromSceneItem(curSceneItem);
+ obs_data_t *curPrivData =
+ obs_sceneitem_get_private_settings(curSceneItem);
+
+ int oldPreset = obs_data_get_int(
+ curPrivData, "color-preset");
+ const QString oldSheet = curTreeItem->styleSheet();
+
+ auto liveChangeColor = [=](const QColor &color) {
+ if (color.isValid()) {
+ curTreeItem->setStyleSheet(
+ "background: "
+ + color.name(QColor::HexArgb));
+ }
+ };
+
+ auto changedColor = [=](const QColor &color) {
+ if (color.isValid()) {
+ ConfirmColor(ui->sources, color,
+ selectedItems);
+ }
+ };
+
+ auto rejected = [=]() {
+ if (oldPreset == 1) {
+ curTreeItem->setStyleSheet(oldSheet);
+ curTreeItem->setProperty("bgColor", 0);
+ } else if (oldPreset == 0) {
+ curTreeItem->setStyleSheet(
+ "background: none");
+ curTreeItem->setProperty("bgColor", 0);
+ } else {
+ curTreeItem->setStyleSheet("");
+ curTreeItem->setProperty("bgColor",
+ oldPreset - 1);
+ }
+
+ curTreeItem->style()->unpolish(curTreeItem);
+ curTreeItem->style()->polish(curTreeItem);
+ };
+
+ QColorDialog::ColorDialogOptions options =
+ QColorDialog::ShowAlphaChannel;
+
+ const char *oldColor = obs_data_get_string(curPrivData,
+ "color");
+ const char *customColor = *oldColor != 0 ? oldColor
+ : "#55FF0000";
+#ifdef __APPLE__
+ options |= QColorDialog::DontUseNativeDialog;
+#endif
+
+ QColorDialog *colorDialog = new QColorDialog(this);
+ colorDialog->setOptions(options);
+ colorDialog->setCurrentColor(
+ QColor(customColor));
+ connect(colorDialog, &QColorDialog::currentColorChanged,
+ liveChangeColor);
+ connect(colorDialog, &QColorDialog::colorSelected,
+ changedColor);
+ connect(colorDialog, &QColorDialog::rejected,
+ rejected);
+ colorDialog->open();
+
+ obs_data_release(curPrivData);
+ } else {
+ for (int x = 0; x < selectedItems.count(); x++) {
+ SourceTreeItem *treeItem = ui->sources
+ ->GetItemWidget(selectedItems[x].row());
+ treeItem->setStyleSheet("background: none");
+ treeItem->setProperty("bgColor", preset);
+ treeItem->style()->unpolish(treeItem);
+ treeItem->style()->polish(treeItem);
+
+ OBSSceneItem sceneItem = ui->sources->Get(
+ selectedItems[x].row());
+ obs_data_t *privData =
+ obs_sceneitem_get_private_settings(
+ sceneItem);
+ obs_data_set_int(privData, "color-preset",
+ preset);
+ obs_data_set_string(privData, "color", "");
+ obs_data_release(privData);
+ }
+ }
+ }
+}
+
+SourceTreeItem *OBSBasic::GetItemWidgetFromSceneItem(
+ obs_sceneitem_t *sceneItem)
+{
+ int i = 0;
+ SourceTreeItem *treeItem = ui->sources->GetItemWidget(i);
+ OBSSceneItem item = ui->sources->Get(i);
+ int64_t id = obs_sceneitem_get_id(sceneItem);
+ while (treeItem && obs_sceneitem_get_id(item) != id) {
+ i++;
+ treeItem = ui->sources->GetItemWidget(i);
+ item = ui->sources->Get(i);
+ }
+ if(treeItem)
+ return treeItem;
+
+ return nullptr;
+}
+
void OBSBasic::on_autoConfigure_triggered()
{
AutoConfig test(this);
@@ -6220,3 +6663,10 @@ void OBSBasic::on_stats_triggered()
statsDlg->show();
stats = statsDlg;
}
+
+ColorSelect::ColorSelect(QWidget *parent)
+ : QWidget(parent),
+ ui(new Ui::ColorSelect)
+{
+ ui->setupUi(this);
+}
diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp
index 9b4302f..24e0036 100644
--- a/UI/window-basic-main.hpp
+++ b/UI/window-basic-main.hpp
@@ -29,6 +29,7 @@
#include "window-basic-transform.hpp"
#include "window-basic-adv-audio.hpp"
#include "window-basic-filters.hpp"
+#include "window-projector.hpp"
#include
@@ -41,10 +42,10 @@
class QMessageBox;
class QListWidgetItem;
class VolControl;
-class QNetworkReply;
class OBSBasicStats;
#include "ui_OBSBasic.h"
+#include "ui_ColorSelect.h"
#define DESKTOP_AUDIO_1 Str("DesktopAudioDevice1")
#define DESKTOP_AUDIO_2 Str("DesktopAudioDevice2")
@@ -67,26 +68,33 @@ enum class QtDataRole {
OBSSignals,
};
-enum class ProjectorType {
- Source,
- Preview,
- StudioProgram,
- Multiview
+struct SavedProjectorInfo {
+ ProjectorType type;
+ int monitor;
+ std::string geometry;
+ std::string name;
};
struct QuickTransition {
QPushButton *button = nullptr;
OBSSource source;
- obs_hotkey_id hotkey = 0;
+ obs_hotkey_id hotkey = OBS_INVALID_HOTKEY_ID;
int duration = 0;
int id = 0;
inline QuickTransition() {}
inline QuickTransition(OBSSource source_, int duration_, int id_)
- : source (source_),
- duration (duration_),
- id (id_)
+ : source (source_),
+ duration (duration_),
+ id (id_),
+ renamedSignal (std::make_shared(
+ obs_source_get_signal_handler(source),
+ "rename", SourceRenamed, this))
{}
+
+private:
+ static void SourceRenamed(void *param, calldata_t *data);
+ std::shared_ptr renamedSignal;
};
class OBSBasic : public OBSMainWindow {
@@ -120,11 +128,6 @@ private:
std::vector signalHandlers;
- std::vector projectorArray;
- std::vector studioProgramProjectorArray;
- std::vector multiviewProjectorArray;
- std::vector previewProjectorArray;
-
bool loaded = false;
long disableSaving = 1;
bool projectChanged = false;
@@ -135,8 +138,9 @@ private:
const char *copyFiltersString;
bool copyVisible = true;
- QPointer updateCheckThread;
- QPointer logUploadThread;
+ QScopedPointer updateCheckThread;
+ QScopedPointer introCheckThread;
+ QScopedPointer logUploadThread;
QPointer interaction;
QPointer properties;
@@ -169,16 +173,18 @@ private:
ConfigFile basicConfig;
+ std::vector savedProjectorsArray;
QPointer projectors[10];
QList> windowProjectors;
QPointer stats;
+ QPointer remux;
QPointer startStreamMenu;
QPointer replayBufferButton;
- QPointer trayIcon;
+ QScopedPointer trayIcon;
QPointer sysTrayStream;
QPointer sysTrayRecord;
QPointer sysTrayReplayBuffer;
@@ -197,6 +203,7 @@ private:
void CreateDefaultScene(bool firstStart);
void UpdateVolumeControlsDecayRate();
+ void UpdateVolumeControlsPeakMeterType();
void ClearVolumeControls();
void UploadLog(const char *subdir, const char *file);
@@ -217,6 +224,8 @@ private:
void InitPrimitives();
+ void OnFirstLoad();
+
OBSSceneItem GetSceneItem(QListWidgetItem *item);
OBSSceneItem GetCurrentSceneItem();
@@ -233,9 +242,6 @@ private:
void UpdatePreviewScalingMenu();
- void UpdateSources(OBSScene scene);
- void InsertSceneItem(obs_sceneitem_t *item);
-
void LoadSceneListOrder(obs_data_array_t *array);
obs_data_array_t *SaveSceneListOrder();
void ChangeSceneIndex(bool relative, int idx, int invalidIdx);
@@ -244,23 +250,20 @@ private:
void TempStreamOutput(const char *url, const char *key,
int vBitrate, int aBitrate);
- void CreateInteractionWindow(obs_source_t *source);
- void CreatePropertiesWindow(obs_source_t *source);
- void CreateFiltersWindow(obs_source_t *source);
-
void CloseDialogs();
void ClearSceneData();
void Nudge(int dist, MoveDir dir);
- void OpenProjector(obs_source_t *source, int monitor, bool window,
- QString title = nullptr,
- ProjectorType type = ProjectorType::Source);
+
+ OBSProjector *OpenProjector(obs_source_t *source, int monitor,
+ QString title, ProjectorType type);
void GetAudioSourceFilters();
void GetAudioSourceProperties();
void VolControlContextMenu();
+ void ToggleVolControlLayout();
+ void ToggleMixerLayout(bool vertical);
- void AddSceneCollection(bool create_new);
void RefreshSceneCollections();
void ChangeSceneCollection();
void LogScenes();
@@ -276,7 +279,7 @@ private:
void SaveProjectNow();
- QListWidgetItem *GetTopSelectedSourceItem();
+ int GetTopSelectedSourceItem();
obs_hotkey_pair_id streamingHotkeys, recordingHotkeys,
replayBufHotkeys;
@@ -367,19 +370,14 @@ private:
obs_data_array_t *SaveProjectors();
void LoadSavedProjectors(obs_data_array_t *savedProjectors);
- obs_data_array_t *SavePreviewProjectors();
- void LoadSavedPreviewProjectors(
- obs_data_array_t *savedPreviewProjectors);
+ void ReceivedIntroJson(const QString &text);
- obs_data_array_t *SaveStudioProgramProjectors();
- void LoadSavedStudioProgramProjectors(
- obs_data_array_t *savedStudioProgramProjectors);
-
- obs_data_array_t *SaveMultiviewProjectors();
- void LoadSavedMultiviewProjectors(
- obs_data_array_t *savedMultiviewProjectors);
+ bool NoSourcesConfirmation();
public slots:
+ void DeferSaveBegin();
+ void DeferSaveEnd();
+
void StartStreaming();
void StopStreaming();
void ForceStopStreaming();
@@ -417,9 +415,12 @@ public slots:
void SetCurrentScene(OBSSource scene, bool force = false,
bool direct = false);
+ bool AddSceneCollection(
+ bool create_new,
+ const QString &name = QString());
+
private slots:
void AddSceneItem(OBSSceneItem item);
- void RemoveSceneItem(OBSSceneItem item);
void AddScene(OBSSource source);
void RemoveScene(OBSSource source);
void RenameSources(OBSSource source, QString newName, QString prevName);
@@ -462,7 +463,8 @@ private slots:
void MixerRenameSource();
- void on_mixerScrollArea_customContextMenuRequested();
+ void on_vMixerScrollArea_customContextMenuRequested();
+ void on_hMixerScrollArea_customContextMenuRequested();
void on_actionCopySource_triggered();
void on_actionPasteRef_triggered();
@@ -471,14 +473,17 @@ private slots:
void on_actionCopyFilters_triggered();
void on_actionPasteFilters_triggered();
+ void ColorChange();
+
+ SourceTreeItem *GetItemWidgetFromSceneItem(obs_sceneitem_t *sceneItem);
+
private:
/* OBS Callbacks */
static void SceneReordered(void *data, calldata_t *params);
static void SceneItemAdded(void *data, calldata_t *params);
- static void SceneItemRemoved(void *data, calldata_t *params);
static void SceneItemSelected(void *data, calldata_t *params);
static void SceneItemDeselected(void *data, calldata_t *params);
- static void SourceLoaded(void *data, obs_source_t *source);
+ static void SourceCreated(void *data, calldata_t *params);
static void SourceRemoved(void *data, calldata_t *params);
static void SourceActivated(void *data, calldata_t *params);
static void SourceDeactivated(void *data, calldata_t *params);
@@ -537,6 +542,11 @@ public:
cy = previewCY;
}
+ inline bool SavingDisabled() const
+ {
+ return disableSaving;
+ }
+
inline double GetCPUUsage() const
{
return os_cpu_usage_info_query(cpuUsageInfo);
@@ -555,11 +565,10 @@ public:
}
}
- void ReorderSceneItem(obs_sceneitem_t *item, size_t idx);
-
QMenu *AddDeinterlacingMenu(obs_source_t *source);
QMenu *AddScaleFilteringMenu(obs_sceneitem_t *item);
- void CreateSourcePopupMenu(QListWidgetItem *item, bool preview);
+ QMenu *AddBackgroundColorMenu(obs_sceneitem_t *item);
+ void CreateSourcePopupMenu(int idx, bool preview);
void UpdateTitleBar();
void UpdateSceneSelection(OBSSource source);
@@ -568,7 +577,10 @@ public:
void SystemTray(bool firstStarted);
void OpenSavedProjectors();
- void RemoveSavedProjectors(int monitor);
+
+ void CreateInteractionWindow(obs_source_t *source);
+ void CreatePropertiesWindow(obs_source_t *source);
+ void CreateFiltersWindow(obs_source_t *source);
protected:
virtual void closeEvent(QCloseEvent *event) override;
@@ -611,9 +623,7 @@ private slots:
void on_actionRemoveScene_triggered();
void on_actionSceneUp_triggered();
void on_actionSceneDown_triggered();
- void on_sources_itemSelectionChanged();
void on_sources_customContextMenuRequested(const QPoint &pos);
- void on_sources_itemDoubleClicked(QListWidgetItem *item);
void on_scenes_itemDoubleClicked(QListWidgetItem *item);
void on_actionAddSource_triggered();
void on_actionRemoveSource_triggered();
@@ -640,6 +650,7 @@ private slots:
void on_actionHelpPortal_triggered();
void on_actionWebsite_triggered();
+ void on_actionDiscord_triggered();
void on_preview_customContextMenuRequested(const QPoint &pos);
void on_program_customContextMenuRequested(const QPoint &pos);
@@ -695,8 +706,6 @@ private slots:
void SceneNameEdited(QWidget *editor,
QAbstractItemDelegate::EndEditHint endHint);
- void SceneItemNameEdited(QWidget *editor,
- QAbstractItemDelegate::EndEditHint endHint);
void OpenSceneFilters();
void OpenFilters();
@@ -721,6 +730,10 @@ private slots:
void OpenMultiviewWindow();
void OpenSceneWindow();
+ void DeferredLoad(const QString &file, int requeueCount);
+
+ void StackedMixerAreaContextMenuRequested();
+
public slots:
void on_actionResetTransform_triggered();
@@ -738,3 +751,12 @@ public:
private:
std::unique_ptr ui;
};
+
+class ColorSelect : public QWidget {
+
+public:
+ explicit ColorSelect(QWidget *parent = 0);
+
+private:
+ std::unique_ptr ui;
+};
diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp
index 8483060..bc4b2b9 100644
--- a/UI/window-basic-preview.cpp
+++ b/UI/window-basic-preview.cpp
@@ -39,6 +39,8 @@ struct SceneFindData {
OBSSceneItem item;
bool selectBelow;
+ obs_sceneitem_t *group = nullptr;
+
SceneFindData(const SceneFindData &) = delete;
SceneFindData(SceneFindData &&) = delete;
SceneFindData& operator=(const SceneFindData &) = delete;
@@ -111,16 +113,6 @@ static vec3 GetTransformedPos(float x, float y, const matrix4 &mat)
return result;
}
-static vec3 GetTransformedPosScaled(float x, float y, const matrix4 &mat,
- float scale)
-{
- vec3 result;
- vec3_set(&result, x, y, 0.0f);
- vec3_transform(&result, &result, &mat);
- vec3_mulf(&result, &result, scale);
- return result;
-}
-
static inline vec2 GetOBSScreenSize()
{
obs_video_info ovi;
@@ -214,11 +206,26 @@ static bool CheckItemSelected(obs_scene_t *scene, obs_sceneitem_t *item,
if (!SceneItemHasVideo(item))
return true;
+ if (obs_sceneitem_is_group(item)) {
+ data->group = item;
+ obs_sceneitem_group_enum_items(item, CheckItemSelected, param);
+ data->group = nullptr;
+
+ if (data->item) {
+ return false;
+ }
+ }
vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
obs_sceneitem_get_box_transform(item, &transform);
+ if (data->group) {
+ matrix4 parent_transform;
+ obs_sceneitem_get_draw_transform(data->group, &parent_transform);
+ matrix4_mul(&transform, &transform, &parent_transform);
+ }
+
matrix4_inv(&transform, &transform);
vec3_transform(&transformedPos, &pos3, &transform);
@@ -248,8 +255,9 @@ bool OBSBasicPreview::SelectedAtPos(const vec2 &pos)
}
struct HandleFindData {
- const vec2 &pos;
- const float scale;
+ const vec2 &pos;
+ const float radius;
+ matrix4 parent_xform;
OBSSceneItem item;
ItemHandle handle = ItemHandle::None;
@@ -259,38 +267,60 @@ struct HandleFindData {
HandleFindData& operator=(const HandleFindData &) = delete;
HandleFindData& operator=(HandleFindData &&) = delete;
- inline HandleFindData(const vec2 &pos_, float scale_)
- : pos (pos_),
- scale (scale_)
- {}
+ inline HandleFindData(const vec2 &pos_, float scale)
+ : pos (pos_),
+ radius (HANDLE_SEL_RADIUS / scale)
+ {
+ matrix4_identity(&parent_xform);
+ }
+
+ inline HandleFindData(const HandleFindData &hfd,
+ obs_sceneitem_t *parent)
+ : pos (hfd.pos),
+ radius (hfd.radius),
+ item (hfd.item),
+ handle (hfd.handle)
+ {
+ obs_sceneitem_get_draw_transform(parent, &parent_xform);
+ }
};
static bool FindHandleAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
void *param)
{
- if (!obs_sceneitem_selected(item))
- return true;
+ HandleFindData &data = *reinterpret_cast(param);
+
+ if (!obs_sceneitem_selected(item)) {
+ if (obs_sceneitem_is_group(item)) {
+ HandleFindData newData(data, item);
+ obs_sceneitem_group_enum_items(item, FindHandleAtPos,
+ &newData);
+ data.item = newData.item;
+ data.handle = newData.handle;
+ }
+
+ return true;
+ }
- HandleFindData *data = reinterpret_cast(param);
matrix4 transform;
vec3 pos3;
- float closestHandle = HANDLE_SEL_RADIUS;
+ float closestHandle = data.radius;
- vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
+ vec3_set(&pos3, data.pos.x, data.pos.y, 0.0f);
obs_sceneitem_get_box_transform(item, &transform);
auto TestHandle = [&] (float x, float y, ItemHandle handle)
{
- vec3 handlePos = GetTransformedPosScaled(x, y, transform,
- data->scale);
+ vec3 handlePos = GetTransformedPos(x, y, transform);
+ vec3_transform(&handlePos, &handlePos, &data.parent_xform);
float dist = vec3_dist(&handlePos, &pos3);
- if (dist < HANDLE_SEL_RADIUS) {
+ if (dist < data.radius) {
if (dist < closestHandle) {
closestHandle = dist;
- data->handle = handle;
- data->item = item;
+ data.handle = handle;
+ data.item = item;
}
}
};
@@ -339,7 +369,10 @@ void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
if (!scene)
return;
- HandleFindData data(pos, main->previewScale / main->devicePixelRatio());
+ float scale = main->previewScale / main->devicePixelRatio();
+ vec2 scaled_pos = pos;
+ vec2_divf(&scaled_pos, &scaled_pos, scale);
+ HandleFindData data(scaled_pos, scale);
obs_scene_enum_items(scene, FindHandleAtPos, &data);
stretchItem = std::move(data.item);
@@ -377,6 +410,15 @@ void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
startCrop.left - startCrop.right);
cropSize.y = float(obs_source_get_height(source) -
startCrop.top - startCrop.bottom);
+
+ stretchGroup = obs_sceneitem_get_group(scene, stretchItem);
+ if (stretchGroup) {
+ obs_sceneitem_get_draw_transform(stretchGroup,
+ &invGroupTransform);
+ matrix4_inv(&invGroupTransform,
+ &invGroupTransform);
+ obs_sceneitem_defer_group_resize_begin(stretchGroup);
+ }
}
}
@@ -482,6 +524,9 @@ static bool select_one(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
{
obs_sceneitem_t *selectedItem =
reinterpret_cast(param);
+ if (obs_sceneitem_is_group(item))
+ obs_sceneitem_group_enum_items(item, select_one, param);
+
obs_sceneitem_select(item, (selectedItem == item));
UNUSED_PARAMETER(scene);
@@ -534,10 +579,15 @@ void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)
if (!mouseMoved)
ProcessClick(pos);
- stretchItem = nullptr;
- mouseDown = false;
- mouseMoved = false;
- cropping = false;
+ if (stretchGroup) {
+ obs_sceneitem_defer_group_resize_end(stretchGroup);
+ }
+
+ stretchItem = nullptr;
+ stretchGroup = nullptr;
+ mouseDown = false;
+ mouseMoved = false;
+ cropping = false;
}
}
@@ -550,30 +600,52 @@ static bool AddItemBounds(obs_scene_t *scene, obs_sceneitem_t *item,
void *param)
{
SelectedItemBounds *data = reinterpret_cast(param);
+ vec3 t[4];
+ auto add_bounds = [data, &t] ()
+ {
+ for (const vec3 &v : t) {
+ if (data->first) {
+ vec3_copy(&data->tl, &v);
+ vec3_copy(&data->br, &v);
+ data->first = false;
+ } else {
+ vec3_min(&data->tl, &data->tl, &v);
+ vec3_max(&data->br, &data->br, &v);
+ }
+ }
+ };
+
+ if (obs_sceneitem_is_group(item)) {
+ SelectedItemBounds sib;
+ obs_sceneitem_group_enum_items(item, AddItemBounds, &sib);
+
+ if (!sib.first) {
+ matrix4 xform;
+ obs_sceneitem_get_draw_transform(item, &xform);
+
+ vec3_set(&t[0], sib.tl.x, sib.tl.y, 0.0f);
+ vec3_set(&t[1], sib.tl.x, sib.br.y, 0.0f);
+ vec3_set(&t[2], sib.br.x, sib.tl.y, 0.0f);
+ vec3_set(&t[3], sib.br.x, sib.br.y, 0.0f);
+ vec3_transform(&t[0], &t[0], &xform);
+ vec3_transform(&t[1], &t[1], &xform);
+ vec3_transform(&t[2], &t[2], &xform);
+ vec3_transform(&t[3], &t[3], &xform);
+ add_bounds();
+ }
+ }
if (!obs_sceneitem_selected(item))
return true;
matrix4 boxTransform;
obs_sceneitem_get_box_transform(item, &boxTransform);
- vec3 t[4] = {
- GetTransformedPos(0.0f, 0.0f, boxTransform),
- GetTransformedPos(1.0f, 0.0f, boxTransform),
- GetTransformedPos(0.0f, 1.0f, boxTransform),
- GetTransformedPos(1.0f, 1.0f, boxTransform)
- };
-
- for (const vec3 &v : t) {
- if (data->first) {
- vec3_copy(&data->tl, &v);
- vec3_copy(&data->br, &v);
- data->first = false;
- } else {
- vec3_min(&data->tl, &data->tl, &v);
- vec3_max(&data->br, &data->br, &v);
- }
- }
+ t[0] = GetTransformedPos(0.0f, 0.0f, boxTransform);
+ t[1] = GetTransformedPos(1.0f, 0.0f, boxTransform);
+ t[2] = GetTransformedPos(0.0f, 1.0f, boxTransform);
+ t[3] = GetTransformedPos(1.0f, 1.0f, boxTransform);
+ add_bounds();
UNUSED_PARAMETER(scene);
return true;
@@ -692,9 +764,22 @@ static bool move_items(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
if (obs_sceneitem_locked(item))
return true;
+ bool selected = obs_sceneitem_selected(item);
vec2 *offset = reinterpret_cast(param);
- if (obs_sceneitem_selected(item)) {
+ if (obs_sceneitem_is_group(item) && !selected) {
+ matrix4 transform;
+ vec3 new_offset;
+ vec3_set(&new_offset, offset->x, offset->y, 0.0f);
+
+ obs_sceneitem_get_draw_transform(item, &transform);
+ vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);
+ matrix4_inv(&transform, &transform);
+ vec3_transform(&new_offset, &new_offset, &transform);
+ obs_sceneitem_group_enum_items(item, move_items, &new_offset);
+ }
+
+ if (selected) {
vec2 pos;
obs_sceneitem_get_pos(item, &pos);
vec2_add(&pos, &pos, offset);
@@ -856,9 +941,6 @@ void OBSBasicPreview::CropItem(const vec2 &pos)
uint32_t align = obs_sceneitem_get_alignment(stretchItem);
vec3 tl, br, pos3;
- if (boundsType != OBS_BOUNDS_NONE) /* TODO */
- return;
-
vec3_zero(&tl);
vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);
@@ -963,7 +1045,8 @@ void OBSBasicPreview::CropItem(const vec2 &pos)
obs_sceneitem_defer_update_begin(stretchItem);
obs_sceneitem_set_crop(stretchItem, &crop);
- obs_sceneitem_set_pos(stretchItem, (vec2*)&newPos);
+ if (boundsType == OBS_BOUNDS_NONE)
+ obs_sceneitem_set_pos(stretchItem, (vec2*)&newPos);
obs_sceneitem_defer_update_end(stretchItem);
}
@@ -1063,6 +1146,20 @@ void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
pos.y = std::round(pos.y);
if (stretchHandle != ItemHandle::None) {
+ OBSBasic *main = reinterpret_cast(
+ App()->GetMainWindow());
+ OBSScene scene = main->GetCurrentScene();
+ obs_sceneitem_t *group = obs_sceneitem_get_group(
+ scene, stretchItem);
+ if (group) {
+ vec3 group_pos;
+ vec3_set(&group_pos, pos.x, pos.y, 0.0f);
+ vec3_transform(&group_pos, &group_pos,
+ &invGroupTransform);
+ pos.x = group_pos.x;
+ pos.y = group_pos.y;
+ }
+
if (cropping)
CropItem(pos);
else
@@ -1076,15 +1173,17 @@ void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
}
}
-static void DrawCircleAtPos(float x, float y, matrix4 &matrix,
- float previewScale)
+static void DrawCircleAtPos(float x, float y)
{
struct vec3 pos;
vec3_set(&pos, x, y, 0.0f);
+
+ struct matrix4 matrix;
+ gs_matrix_get(&matrix);
vec3_transform(&pos, &pos, &matrix);
- vec3_mulf(&pos, &pos, previewScale);
gs_matrix_push();
+ gs_matrix_identity();
gs_matrix_translate(&pos);
gs_matrix_scale3f(HANDLE_RADIUS, HANDLE_RADIUS, 1.0f);
gs_draw(GS_LINESTRIP, 0, 0);
@@ -1108,6 +1207,16 @@ bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene,
if (!SceneItemHasVideo(item))
return true;
+ if (obs_sceneitem_is_group(item)) {
+ matrix4 mat;
+ obs_sceneitem_get_draw_transform(item, &mat);
+
+ gs_matrix_push();
+ gs_matrix_mul(&mat);
+ obs_sceneitem_group_enum_items(item, DrawSelectedItem, param);
+ gs_matrix_pop();
+ }
+
if (!obs_sceneitem_selected(item))
return true;
@@ -1142,19 +1251,18 @@ bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene,
gs_load_vertexbuffer(main->circle);
- DrawCircleAtPos(0.0f, 0.0f, boxTransform, main->previewScale);
- DrawCircleAtPos(0.0f, 1.0f, boxTransform, main->previewScale);
- DrawCircleAtPos(1.0f, 0.0f, boxTransform, main->previewScale);
- DrawCircleAtPos(1.0f, 1.0f, boxTransform, main->previewScale);
- DrawCircleAtPos(0.5f, 0.0f, boxTransform, main->previewScale);
- DrawCircleAtPos(0.0f, 0.5f, boxTransform, main->previewScale);
- DrawCircleAtPos(0.5f, 1.0f, boxTransform, main->previewScale);
- DrawCircleAtPos(1.0f, 0.5f, boxTransform, main->previewScale);
-
gs_matrix_push();
- gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
gs_matrix_mul(&boxTransform);
+ DrawCircleAtPos(0.0f, 0.0f);
+ DrawCircleAtPos(0.0f, 1.0f);
+ DrawCircleAtPos(1.0f, 0.0f);
+ DrawCircleAtPos(1.0f, 1.0f);
+ DrawCircleAtPos(0.5f, 0.0f);
+ DrawCircleAtPos(0.0f, 0.5f);
+ DrawCircleAtPos(0.5f, 1.0f);
+ DrawCircleAtPos(1.0f, 0.5f);
+
obs_sceneitem_crop crop;
obs_sceneitem_get_crop(item, &crop);
@@ -1208,8 +1316,12 @@ void OBSBasicPreview::DrawSceneEditing()
OBSScene scene = main->GetCurrentScene();
- if (scene)
+ if (scene) {
+ gs_matrix_push();
+ gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
obs_scene_enum_items(scene, DrawSelectedItem, this);
+ gs_matrix_pop();
+ }
gs_load_vertexbuffer(nullptr);
@@ -1232,4 +1344,4 @@ void OBSBasicPreview::SetScalingAmount(float newScalingAmountVal) {
scrollingOffset.x *= newScalingAmountVal / scalingAmount;
scrollingOffset.y *= newScalingAmountVal / scalingAmount;
scalingAmount = newScalingAmountVal;
-}
\ No newline at end of file
+}
diff --git a/UI/window-basic-preview.hpp b/UI/window-basic-preview.hpp
index 4042281..c177dd9 100644
--- a/UI/window-basic-preview.hpp
+++ b/UI/window-basic-preview.hpp
@@ -35,11 +35,13 @@ private:
obs_sceneitem_crop startCrop;
vec2 startItemPos;
vec2 cropSize;
+ OBSSceneItem stretchGroup;
OBSSceneItem stretchItem;
ItemHandle stretchHandle = ItemHandle::None;
vec2 stretchItemSize;
matrix4 screenToItem;
matrix4 itemToScreen;
+ matrix4 invGroupTransform;
vec2 startPos;
vec2 lastMoveOffset;
diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp
index d07336b..a03f187 100644
--- a/UI/window-basic-properties.cpp
+++ b/UI/window-basic-properties.cpp
@@ -114,15 +114,19 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)
obs_display_add_draw_callback(preview->GetDisplay(),
OBSBasicProperties::DrawPreview, this);
};
-
enum obs_source_type type = obs_source_get_type(source);
uint32_t caps = obs_source_get_output_flags(source);
bool drawable_type = type == OBS_SOURCE_TYPE_INPUT ||
type == OBS_SOURCE_TYPE_SCENE;
+ bool drawable_preview = (caps & OBS_SOURCE_VIDEO) != 0;
- if (drawable_type && (caps & OBS_SOURCE_VIDEO) != 0)
+ if (drawable_preview && drawable_type) {
+ preview->show();
connect(preview.data(), &OBSQTDisplay::DisplayCreated,
addDrawCallback);
+ } else {
+ preview->hide();
+ }
}
OBSBasicProperties::~OBSBasicProperties()
diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
index d1d8f48..4701b7b 100644
--- a/UI/window-basic-settings.cpp
+++ b/UI/window-basic-settings.cpp
@@ -318,6 +318,9 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->snapDistance, DSCROLL_CHANGED,GENERAL_CHANGED);
HookWidget(ui->doubleClickSwitch, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->studioPortraitLayout, CHECK_CHANGED, GENERAL_CHANGED);
+ HookWidget(ui->multiviewMouseSwitch, CHECK_CHANGED, GENERAL_CHANGED);
+ HookWidget(ui->multiviewDrawNames, CHECK_CHANGED, GENERAL_CHANGED);
+ HookWidget(ui->multiviewDrawAreas, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->multiviewLayout, COMBO_CHANGED, GENERAL_CHANGED);
HookWidget(ui->outputMode, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->streamType, COMBO_CHANGED, STREAM1_CHANGED);
@@ -401,6 +404,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->channelSetup, COMBO_CHANGED, AUDIO_RESTART);
HookWidget(ui->sampleRate, COMBO_CHANGED, AUDIO_RESTART);
HookWidget(ui->meterDecayRate, COMBO_CHANGED, AUDIO_CHANGED);
+ HookWidget(ui->peakMeterType, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->desktopAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->desktopAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->auxAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED);
@@ -426,6 +430,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
#endif
#ifdef _WIN32
HookWidget(ui->disableAudioDucking, CHECK_CHANGED, ADV_CHANGED);
+ HookWidget(ui->browserHWAccel, CHECK_CHANGED, ADV_RESTART);
#endif
HookWidget(ui->filenameFormatting, EDIT_CHANGED, ADV_CHANGED);
HookWidget(ui->overwriteIfExists, CHECK_CHANGED, ADV_CHANGED);
@@ -441,6 +446,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->bindToIP, COMBO_CHANGED, ADV_CHANGED);
HookWidget(ui->enableNewSocketLoop, CHECK_CHANGED, ADV_CHANGED);
HookWidget(ui->enableLowLatencyMode, CHECK_CHANGED, ADV_CHANGED);
+ HookWidget(ui->disableFocusHotkeys, CHECK_CHANGED, ADV_CHANGED);
#if !defined(_WIN32) && !defined(__APPLE__)
delete ui->enableAutoUpdates;
@@ -495,6 +501,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
delete ui->advancedGeneralGroupBox;
delete ui->enableNewSocketLoop;
delete ui->enableLowLatencyMode;
+ delete ui->browserHWAccel;
+ delete ui->sourcesGroup;
#if defined(__APPLE__) || HAVE_PULSEAUDIO
delete ui->disableAudioDucking;
#endif
@@ -507,6 +515,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
ui->advancedGeneralGroupBox = nullptr;
ui->enableNewSocketLoop = nullptr;
ui->enableLowLatencyMode = nullptr;
+ ui->browserHWAccel = nullptr;
+ ui->sourcesGroup = nullptr;
#if defined(__APPLE__) || HAVE_PULSEAUDIO
ui->disableAudioDucking = nullptr;
#endif
@@ -701,11 +711,17 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
SimpleRecordingQualityChanged();
UpdateAutomaticReplayBufferCheckboxes();
+
+ App()->EnableInFocusHotkeys(false);
}
OBSBasicSettings::~OBSBasicSettings()
{
+ bool disableHotkeysInFocus = config_get_bool(App()->GlobalConfig(),
+ "General", "DisableHotkeysInFocus");
+ delete ui->filenameFormatting->completer();
main->EnableOutputs(true);
+ App()->EnableInFocusHotkeys(!disableHotkeysInFocus);
}
void OBSBasicSettings::SaveCombo(QComboBox *widget, const char *section,
@@ -1092,30 +1108,37 @@ void OBSBasicSettings::LoadGeneralSettings()
"BasicWindow", "StudioPortraitLayout");
ui->studioPortraitLayout->setChecked(studioPortraitLayout);
+ bool multiviewMouseSwitch = config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "MultiviewMouseSwitch");
+ ui->multiviewMouseSwitch->setChecked(multiviewMouseSwitch);
+
+ bool multiviewDrawNames = config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "MultiviewDrawNames");
+ ui->multiviewDrawNames->setChecked(multiviewDrawNames);
+
+ bool multiviewDrawAreas = config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "MultiviewDrawAreas");
+ ui->multiviewDrawAreas->setChecked(multiviewDrawAreas);
+
ui->multiviewLayout->addItem(QTStr(
"Basic.Settings.General.MultiviewLayout.Horizontal.Top"),
- QT_UTF8("horizontaltop"));
+ static_cast(MultiviewLayout::HORIZONTAL_TOP_8_SCENES));
ui->multiviewLayout->addItem(QTStr(
"Basic.Settings.General.MultiviewLayout.Horizontal.Bottom"),
- QT_UTF8("horizontalbottom"));
+ static_cast(MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES));
ui->multiviewLayout->addItem(QTStr(
"Basic.Settings.General.MultiviewLayout.Vertical.Left"),
- QT_UTF8("verticalleft"));
+ static_cast(MultiviewLayout::VERTICAL_LEFT_8_SCENES));
ui->multiviewLayout->addItem(QTStr(
"Basic.Settings.General.MultiviewLayout.Vertical.Right"),
- QT_UTF8("verticalright"));
+ static_cast(MultiviewLayout::VERTICAL_RIGHT_8_SCENES));
+ ui->multiviewLayout->addItem(QTStr(
+ "Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top"),
+ static_cast(MultiviewLayout::HORIZONTAL_TOP_24_SCENES));
- const char *multiviewLayoutText = config_get_string(GetGlobalConfig(),
- "BasicWindow", "MultiviewLayout");
-
- if (astrcmpi(multiviewLayoutText, "horizontalbottom") == 0)
- ui->multiviewLayout->setCurrentIndex(1);
- else if (astrcmpi(multiviewLayoutText, "verticalleft") == 0)
- ui->multiviewLayout->setCurrentIndex(2);
- else if (astrcmpi(multiviewLayoutText, "verticalright") == 0)
- ui->multiviewLayout->setCurrentIndex(3);
- else
- ui->multiviewLayout->setCurrentIndex(0);
+ ui->multiviewLayout->setCurrentIndex(
+ config_get_int(GetGlobalConfig(), "BasicWindow",
+ "MultiviewLayout"));
loading = false;
}
@@ -2124,6 +2147,8 @@ void OBSBasicSettings::LoadAudioSettings()
"ChannelSetup");
double meterDecayRate = config_get_double(main->Config(), "Audio",
"MeterDecayRate");
+ uint32_t peakMeterTypeIdx = config_get_uint(main->Config(), "Audio",
+ "PeakMeterType");
loading = true;
@@ -2159,6 +2184,8 @@ void OBSBasicSettings::LoadAudioSettings()
else
ui->meterDecayRate->setCurrentIndex(0);
+ ui->peakMeterType->setCurrentIndex(peakMeterTypeIdx);
+
LoadAudioDevices();
LoadAudioSources();
@@ -2273,8 +2300,16 @@ void OBSBasicSettings::LoadAdvancedSettings()
ui->enableNewSocketLoop->setChecked(enableNewSocketLoop);
ui->enableLowLatencyMode->setChecked(enableLowLatencyMode);
+
+ bool browserHWAccel = config_get_bool(App()->GlobalConfig(),
+ "General", "BrowserHWAccel");
+ ui->browserHWAccel->setChecked(browserHWAccel);
#endif
+ bool disableFocusHotkeys = config_get_bool(App()->GlobalConfig(),
+ "General", "DisableHotkeysInFocus");
+ ui->disableFocusHotkeys->setChecked(disableFocusHotkeys);
+
loading = false;
}
@@ -2696,13 +2731,37 @@ void OBSBasicSettings::SaveGeneralSettings()
main->ResetUI();
}
- if (WidgetChanged(ui->multiviewLayout)) {
- config_set_string(GetGlobalConfig(), "BasicWindow",
- "MultiviewLayout",
- QT_TO_UTF8(GetComboData(ui->multiviewLayout)));
-
- OBSProjector::UpdateMultiviewProjectors();
+ bool multiviewChanged = false;
+ if (WidgetChanged(ui->multiviewMouseSwitch)) {
+ config_set_bool(GetGlobalConfig(), "BasicWindow",
+ "MultiviewMouseSwitch",
+ ui->multiviewMouseSwitch->isChecked());
+ multiviewChanged = true;
}
+
+ if (WidgetChanged(ui->multiviewDrawNames)) {
+ config_set_bool(GetGlobalConfig(), "BasicWindow",
+ "MultiviewDrawNames",
+ ui->multiviewDrawNames->isChecked());
+ multiviewChanged = true;
+ }
+
+ if (WidgetChanged(ui->multiviewDrawAreas)) {
+ config_set_bool(GetGlobalConfig(), "BasicWindow",
+ "MultiviewDrawAreas",
+ ui->multiviewDrawAreas->isChecked());
+ multiviewChanged = true;
+ }
+
+ if (WidgetChanged(ui->multiviewLayout)) {
+ config_set_int(GetGlobalConfig(), "BasicWindow",
+ "MultiviewLayout",
+ ui->multiviewLayout->currentData().toInt());
+ multiviewChanged = true;
+ }
+
+ if (multiviewChanged)
+ OBSProjector::UpdateMultiviewProjectors();
}
void OBSBasicSettings::SaveStream1Settings()
@@ -2782,8 +2841,16 @@ void OBSBasicSettings::SaveAdvancedSettings()
SaveCheckBox(ui->enableNewSocketLoop, "Output", "NewSocketLoopEnable");
SaveCheckBox(ui->enableLowLatencyMode, "Output", "LowLatencyEnable");
+
+ bool browserHWAccel = ui->browserHWAccel->isChecked();
+ config_set_bool(App()->GlobalConfig(), "General",
+ "BrowserHWAccel", browserHWAccel);
#endif
+ bool disableFocusHotkeys = ui->disableFocusHotkeys->isChecked();
+ config_set_bool(App()->GlobalConfig(), "General",
+ "DisableHotkeysInFocus", disableFocusHotkeys);
+
#ifdef __APPLE__
if (WidgetChanged(ui->disableOSXVSync)) {
bool disable = ui->disableOSXVSync->isChecked();
@@ -3113,6 +3180,14 @@ void OBSBasicSettings::SaveAudioSettings()
main->UpdateVolumeControlsDecayRate();
}
+ if (WidgetChanged(ui->peakMeterType)) {
+ uint32_t peakMeterTypeIdx = ui->peakMeterType->currentIndex();
+ config_set_uint(main->Config(), "Audio", "PeakMeterType",
+ peakMeterTypeIdx);
+
+ main->UpdateVolumeControlsPeakMeterType();
+ }
+
for (auto &audioSource : audioSources) {
auto source = OBSGetStrongRef(get<0>(audioSource));
if (!source)
@@ -4123,6 +4198,9 @@ void OBSBasicSettings::AdvReplayBufferChanged()
int vbitrate = (int)obs_data_get_int(settings, "bitrate");
const char *rateControl = obs_data_get_string(settings, "rate_control");
+ if (!rateControl)
+ rateControl = "";
+
bool lossless = strcmp(rateControl, "lossless") == 0 ||
ui->advOutRecType->currentIndex() == 1;
bool replayBufferEnabled = ui->advReplayBuf->isChecked();
@@ -4148,9 +4226,6 @@ void OBSBasicSettings::AdvReplayBufferChanged()
if (memMB < 1)
memMB = 1;
- if (!rateControl)
- rateControl = "";
-
bool varRateControl = (astrcmpi(rateControl, "CBR") == 0 ||
astrcmpi(rateControl, "VBR") == 0 ||
astrcmpi(rateControl, "ABR") == 0);
@@ -4244,12 +4319,6 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged()
warning += "\n\n";
warning += SIMPLE_OUTPUT_WARNING("Encoder");
}
-
- if (streamEnc == enc && enc == SIMPLE_ENCODER_QSV) {
- if (!warning.isEmpty())
- warning += "\n\n";
- warning += SIMPLE_OUTPUT_WARNING("MultipleQSV");
- }
}
if (ui->simpleOutRecFormat->currentText().compare("mp4") == 0) {
diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp
index 72b3558..da81782 100644
--- a/UI/window-basic-source-select.cpp
+++ b/UI/window-basic-source-select.cpp
@@ -38,6 +38,25 @@ bool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t *source)
return true;
}
+bool OBSBasicSourceSelect::EnumGroups(void *data, obs_source_t *source)
+{
+ OBSBasicSourceSelect *window = static_cast(data);
+ const char *name = obs_source_get_name(source);
+ const char *id = obs_source_get_id(source);
+
+ if (strcmp(id, window->id) == 0) {
+ OBSBasic *main = reinterpret_cast(
+ App()->GetMainWindow());
+ OBSScene scene = main->GetCurrentScene();
+
+ obs_sceneitem_t *existing = obs_scene_get_group(scene, name);
+ if (!existing)
+ window->ui->sourceList->addItem(QT_UTF8(name));
+ }
+
+ return true;
+}
+
void OBSBasicSourceSelect::OBSSourceAdded(void *data, calldata_t *calldata)
{
OBSBasicSourceSelect *window = static_cast(data);
@@ -277,6 +296,8 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_)
const char *name = obs_source_get_name(sceneSource);
ui->sourceList->addItem(QT_UTF8(name));
}
+ } else if (strcmp(id_, "group") == 0) {
+ obs_enum_sources(EnumGroups, this);
} else {
obs_enum_sources(EnumSources, this);
}
diff --git a/UI/window-basic-source-select.hpp b/UI/window-basic-source-select.hpp
index 7bab476..9049679 100644
--- a/UI/window-basic-source-select.hpp
+++ b/UI/window-basic-source-select.hpp
@@ -32,6 +32,7 @@ private:
const char *id;
static bool EnumSources(void *data, obs_source_t *source);
+ static bool EnumGroups(void *data, obs_source_t *source);
static void OBSSourceRemoved(void *data, calldata_t *calldata);
static void OBSSourceAdded(void *data, calldata_t *calldata);
diff --git a/UI/window-basic-stats.cpp b/UI/window-basic-stats.cpp
index 2a66c2d..b758c2f 100644
--- a/UI/window-basic-stats.cpp
+++ b/UI/window-basic-stats.cpp
@@ -136,6 +136,7 @@ OBSBasicStats::OBSBasicStats(QWidget *parent)
Qt::WindowMinimizeButtonHint |
Qt::WindowCloseButtonHint);
setWindowTitle(QTStr("Basic.Stats"));
+ setWindowIcon(QIcon(":/res/images/obs.png"));
setWindowModality(Qt::NonModal);
setAttribute(Qt::WA_DeleteOnClose, true);
diff --git a/UI/window-basic-transform.cpp b/UI/window-basic-transform.cpp
index 7b0b4b0..1f529dd 100644
--- a/UI/window-basic-transform.cpp
+++ b/UI/window-basic-transform.cpp
@@ -4,23 +4,28 @@
Q_DECLARE_METATYPE(OBSSceneItem);
-static OBSSceneItem FindASelectedItem(OBSScene scene)
+static bool find_sel(obs_scene_t *, obs_sceneitem_t *item, void *param)
{
- auto func = [] (obs_scene_t *scene, obs_sceneitem_t *item, void *param)
- {
- OBSSceneItem &dst = *reinterpret_cast(param);
+ OBSSceneItem &dst = *reinterpret_cast(param);
- if (obs_sceneitem_selected(item)) {
- dst = item;
+ if (obs_sceneitem_selected(item)) {
+ dst = item;
+ return false;
+ }
+ if (obs_sceneitem_is_group(item)) {
+ obs_sceneitem_group_enum_items(item, find_sel, param);
+ if (!!dst) {
return false;
}
+ }
- UNUSED_PARAMETER(scene);
- return true;
- };
+ return true;
+};
+static OBSSceneItem FindASelectedItem(OBSScene scene)
+{
OBSSceneItem item;
- obs_scene_enum_items(scene, func, &item);
+ obs_scene_enum_items(scene, find_sel, &item);
return item;
}
@@ -63,9 +68,10 @@ OBSBasicTransform::OBSBasicTransform(OBSBasic *parent)
installEventFilter(CreateShortcutFilter());
- OBSScene curScene = main->GetCurrentScene();
- SetScene(curScene);
- SetItem(FindASelectedItem(curScene));
+ OBSSceneItem item = FindASelectedItem(main->GetCurrentScene());
+ OBSScene scene = obs_sceneitem_get_scene(item);
+ SetScene(scene);
+ SetItem(item);
channelChangedSignal.Connect(obs_get_signal_handler(), "channel_change",
OBSChannelChanged, this);
diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp
index 0ad9755..2ee09f7 100644
--- a/UI/window-projector.cpp
+++ b/UI/window-projector.cpp
@@ -3,30 +3,56 @@
#include
#include
#include
-#include "window-projector.hpp"
+#include "obs-app.hpp"
+#include "window-basic-main.hpp"
#include "display-helpers.hpp"
#include "qt-wrappers.hpp"
#include "platform.hpp"
-#define HORIZONTAL_TOP 0
-#define HORIZONTAL_BOTTOM 1
-#define VERTICAL_LEFT 2
-#define VERTICAL_RIGHT 3
-
+static QList windowedProjectors;
static QList multiviewProjectors;
-static bool updatingMultiview = false;
-static int multiviewLayout = HORIZONTAL_TOP;
+static bool updatingMultiview = false, drawLabel, drawSafeArea, mouseSwitching,
+ transitionOnDoubleClick;
+static MultiviewLayout multiviewLayout;
+static size_t maxSrcs, numSrcs;
-OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window)
+OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
+ QString title, ProjectorType type_)
: OBSQTDisplay (widget,
Qt::Window),
source (source_),
removedSignal (obs_source_get_signal_handler(source),
"remove", OBSSourceRemoved, this)
{
- if (!window) {
+ projectorTitle = std::move(title);
+ savedMonitor = monitor;
+ isWindow = savedMonitor < 0;
+ type = type_;
+
+ if (isWindow) {
+ setWindowIcon(QIcon(":/res/images/obs.png"));
+
+ UpdateProjectorTitle(projectorTitle);
+ windowedProjectors.push_back(this);
+
+ resize(480, 270);
+
+ SetAlwaysOnTop(this, config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "ProjectorAlwaysOnTop"));
+ } else {
setWindowFlags(Qt::FramelessWindowHint |
Qt::X11BypassWindowManagerHint);
+
+ QScreen *screen = QGuiApplication::screens()[savedMonitor];
+ setGeometry(screen->geometry());
+
+ QAction *action = new QAction(this);
+ action->setShortcut(Qt::Key_Escape);
+ addAction(action);
+ connect(action, SIGNAL(triggered()), this,
+ SLOT(EscapeTriggered()));
+
+ SetAlwaysOnTop(this, true);
}
setAttribute(Qt::WA_DeleteOnClose, true);
@@ -49,14 +75,82 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window)
bool hideCursor = config_get_bool(GetGlobalConfig(),
"BasicWindow", "HideProjectorCursor");
- if (hideCursor && !window) {
+ if (hideCursor && !isWindow) {
QPixmap empty(16, 16);
empty.fill(Qt::transparent);
setCursor(QCursor(empty));
}
+ if (type == ProjectorType::Multiview) {
+ obs_enter_graphics();
+
+ // All essential action should be placed inside this area
+ gs_render_start(true);
+ gs_vertex2f(actionSafePercentage, actionSafePercentage);
+ gs_vertex2f(actionSafePercentage, 1 - actionSafePercentage);
+ gs_vertex2f(1 - actionSafePercentage, 1 - actionSafePercentage);
+ gs_vertex2f(1 - actionSafePercentage, actionSafePercentage);
+ gs_vertex2f(actionSafePercentage, actionSafePercentage);
+ actionSafeMargin = gs_render_save();
+
+ // All graphics should be placed inside this area
+ gs_render_start(true);
+ gs_vertex2f(graphicsSafePercentage, graphicsSafePercentage);
+ gs_vertex2f(graphicsSafePercentage, 1 - graphicsSafePercentage);
+ gs_vertex2f(1 - graphicsSafePercentage,
+ 1 - graphicsSafePercentage);
+ gs_vertex2f(1 - graphicsSafePercentage, graphicsSafePercentage);
+ gs_vertex2f(graphicsSafePercentage, graphicsSafePercentage);
+ graphicsSafeMargin = gs_render_save();
+
+ // 4:3 safe area for widescreen
+ gs_render_start(true);
+ gs_vertex2f(fourByThreeSafePercentage, graphicsSafePercentage);
+ gs_vertex2f(1 - fourByThreeSafePercentage,
+ graphicsSafePercentage);
+ gs_vertex2f(1 - fourByThreeSafePercentage, 1 -
+ graphicsSafePercentage);
+ gs_vertex2f(fourByThreeSafePercentage,
+ 1 - graphicsSafePercentage);
+ gs_vertex2f(fourByThreeSafePercentage, graphicsSafePercentage);
+ fourByThreeSafeMargin = gs_render_save();
+
+ gs_render_start(true);
+ gs_vertex2f(0.0f, 0.5f);
+ gs_vertex2f(lineLength, 0.5f);
+ leftLine = gs_render_save();
+
+ gs_render_start(true);
+ gs_vertex2f(0.5f, 0.0f);
+ gs_vertex2f(0.5f, lineLength);
+ topLine = gs_render_save();
+
+ gs_render_start(true);
+ gs_vertex2f(1.0f, 0.5f);
+ gs_vertex2f(1 - lineLength, 0.5f);
+ rightLine = gs_render_save();
+ obs_leave_graphics();
+
+ solid = obs_get_base_effect(OBS_EFFECT_SOLID);
+ color = gs_effect_get_param_by_name(solid, "color");
+
+ UpdateMultiview();
+
+ multiviewProjectors.push_back(this);
+ }
+
App()->IncrementSleepInhibition();
- resize(480, 270);
+
+ if (source)
+ obs_source_inc_showing(source);
+
+ ready = true;
+
+ show();
+
+ // We need it here to allow keyboard input in X11 to listen to Escape
+ if (!isWindow)
+ activateWindow();
}
OBSProjector::~OBSProjector()
@@ -76,10 +170,9 @@ OBSProjector::~OBSProjector()
}
obs_enter_graphics();
- gs_vertexbuffer_destroy(outerBox);
- gs_vertexbuffer_destroy(innerBox);
- gs_vertexbuffer_destroy(leftVLine);
- gs_vertexbuffer_destroy(rightVLine);
+ gs_vertexbuffer_destroy(actionSafeMargin);
+ gs_vertexbuffer_destroy(graphicsSafeMargin);
+ gs_vertexbuffer_destroy(fourByThreeSafeMargin);
gs_vertexbuffer_destroy(leftLine);
gs_vertexbuffer_destroy(topLine);
gs_vertexbuffer_destroy(rightLine);
@@ -89,6 +182,9 @@ OBSProjector::~OBSProjector()
if (type == ProjectorType::Multiview)
multiviewProjectors.removeAll(this);
+ if (isWindow)
+ windowedProjectors.removeAll(this);
+
App()->DecrementSleepInhibition();
}
@@ -132,92 +228,6 @@ static OBSSource CreateLabel(const char *name, size_t h)
return txtSource;
}
-void OBSProjector::Init(int monitor, bool window, QString title,
- ProjectorType type_)
-{
- QScreen *screen = QGuiApplication::screens()[monitor];
-
- if (!window)
- setGeometry(screen->geometry());
-
- bool alwaysOnTop = config_get_bool(GetGlobalConfig(),
- "BasicWindow", "ProjectorAlwaysOnTop");
- if (alwaysOnTop && !window)
- SetAlwaysOnTop(this, true);
-
- if (window)
- setWindowTitle(title);
-
- show();
-
- if (source)
- obs_source_inc_showing(source);
-
- if (!window) {
- QAction *action = new QAction(this);
- action->setShortcut(Qt::Key_Escape);
- addAction(action);
- connect(action, SIGNAL(triggered()), this,
- SLOT(EscapeTriggered()));
- activateWindow();
- }
-
- savedMonitor = monitor;
- isWindow = window;
- type = type_;
-
- if (type == ProjectorType::Multiview) {
- obs_enter_graphics();
- gs_render_start(true);
- gs_vertex2f(0.001f, 0.001f);
- gs_vertex2f(0.001f, 0.997f);
- gs_vertex2f(0.997f, 0.997f);
- gs_vertex2f(0.997f, 0.001f);
- gs_vertex2f(0.001f, 0.001f);
- outerBox = gs_render_save();
-
- gs_render_start(true);
- gs_vertex2f(0.04f, 0.04f);
- gs_vertex2f(0.04f, 0.96f);
- gs_vertex2f(0.96f, 0.96f);
- gs_vertex2f(0.96f, 0.04f);
- gs_vertex2f(0.04f, 0.04f);
- innerBox = gs_render_save();
-
- gs_render_start(true);
- gs_vertex2f(0.15f, 0.04f);
- gs_vertex2f(0.15f, 0.96f);
- leftVLine = gs_render_save();
-
- gs_render_start(true);
- gs_vertex2f(0.85f, 0.04f);
- gs_vertex2f(0.85f, 0.96f);
- rightVLine = gs_render_save();
-
- gs_render_start(true);
- gs_vertex2f(0.0f, 0.5f);
- gs_vertex2f(0.075f, 0.5f);
- leftLine = gs_render_save();
-
- gs_render_start(true);
- gs_vertex2f(0.5f, 0.0f);
- gs_vertex2f(0.5f, 0.09f);
- topLine = gs_render_save();
-
- gs_render_start(true);
- gs_vertex2f(0.925f, 0.5f);
- gs_vertex2f(1.0f, 0.5f);
- rightLine = gs_render_save();
- obs_leave_graphics();
-
- UpdateMultiview();
-
- multiviewProjectors.push_back(this);
- }
-
- ready = true;
-}
-
static inline void renderVB(gs_effect_t *effect, gs_vertbuffer_t *vb,
int cx, int cy)
{
@@ -243,10 +253,36 @@ static inline void renderVB(gs_effect_t *effect, gs_vertbuffer_t *vb,
static inline uint32_t labelOffset(obs_source_t *label, uint32_t cx)
{
uint32_t w = obs_source_get_width(label);
- w = uint32_t(float(w) * 0.5f);
+
+ int n; // Number of scenes per row
+ switch (multiviewLayout) {
+ case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
+ n = 6;
+ break;
+ default:
+ n = 4;
+ break;
+ }
+
+ w = uint32_t(w * ((1.0f) / n));
return (cx / 2) - w;
}
+static inline void startRegion(int vX, int vY, int vCX, int vCY, float oL,
+ float oR, float oT, float oB)
+{
+ gs_projection_push();
+ gs_viewport_push();
+ gs_set_viewport(vX, vY, vCX, vCY);
+ gs_ortho(oL, oR, oT, oB, -100.0f, 100.0f);
+}
+
+static inline void endRegion()
+{
+ gs_viewport_pop();
+ gs_projection_pop();
+}
+
void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
{
OBSProjector *window = (OBSProjector *)data;
@@ -257,327 +293,342 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
uint32_t targetCX, targetCY;
int x, y;
- float fX, fY, halfCX, halfCY, sourceX, sourceY, labelX, labelY,
- quarterCX, quarterCY, scale, targetCXF, targetCYF,
- hiCX, hiCY, qiX, qiY, qiCX, qiCY, hiScaleX, hiScaleY,
- qiScaleX, qiScaleY;
- uint32_t offset;
+ float scale;
- gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
- gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
-
- struct obs_video_info ovi;
- obs_get_video_info(&ovi);
- targetCX = ovi.base_width;
- targetCY = ovi.base_height;
+ targetCX = (uint32_t)window->fw;
+ targetCY = (uint32_t)window->fh;
GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);
- targetCXF = float(targetCX);
- targetCYF = float(targetCY);
- fX = float(x);
- fY = float(y);
-
- halfCX = (targetCXF + 1) / 2;
- halfCY = (targetCYF + 1) / 2;
- hiCX = (halfCX - 4.0);
- hiCY = (halfCY - 4.0);
- hiScaleX = hiCX / targetCXF;
- hiScaleY = hiCY / targetCYF;
-
- quarterCX = (halfCX + 1) / 2;
- quarterCY = (halfCY + 1) / 2;
- qiCX = (quarterCX - 8.0);
- qiCY = (quarterCY - 8.0);
- qiScaleX = qiCX / targetCXF;
- qiScaleY = qiCY / targetCYF;
-
OBSSource previewSrc = main->GetCurrentSceneSource();
OBSSource programSrc = main->GetProgramSource();
-
bool studioMode = main->IsPreviewProgramMode();
- auto drawBox = [solid, color] (float cx, float cy,
+ auto renderVB = [&](gs_vertbuffer_t *vb, int cx, int cy,
uint32_t colorVal)
{
- gs_effect_set_color(color, colorVal);
- while (gs_effect_loop(solid, "Solid"))
+ if (!vb)
+ return;
+
+ matrix4 transform;
+ matrix4_identity(&transform);
+ transform.x.x = cx;
+ transform.y.y = cy;
+
+ gs_load_vertexbuffer(vb);
+
+ gs_matrix_push();
+ gs_matrix_mul(&transform);
+
+ gs_effect_set_color(window->color, colorVal);
+ while (gs_effect_loop(window->solid, "Solid"))
+ gs_draw(GS_LINESTRIP, 0, 0);
+
+ gs_matrix_pop();
+ };
+
+ auto drawBox = [&](float cx, float cy, uint32_t colorVal)
+ {
+ gs_effect_set_color(window->color, colorVal);
+ while (gs_effect_loop(window->solid, "Solid"))
gs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy);
};
- auto setRegion = [fX, fY, scale] (float x, float y, float cx, float cy)
+ auto setRegion = [&](float bx, float by, float cx,
+ float cy)
{
- float vX = int(fX + x * scale);
- float vY = int(fY + y * scale);
+ float vX = int(x + bx * scale);
+ float vY = int(y + by * scale);
float vCX = int(cx * scale);
float vCY = int(cy * scale);
- float oL = x;
- float oT = y;
- float oR = (x + cx);
- float oB = (y + cy);
+ float oL = bx;
+ float oT = by;
+ float oR = (bx + cx);
+ float oB = (by + cy);
- gs_projection_push();
- gs_viewport_push();
- gs_set_viewport(vX, vY, vCX, vCY);
- gs_ortho(oL, oR, oT, oB, -100.0f, 100.0f);
- };
-
- auto resetRegion = [] ()
- {
- gs_viewport_pop();
- gs_projection_pop();
+ startRegion(vX, vY, vCX, vCY, oL, oR, oT, oB);
};
auto calcBaseSource = [&](size_t i)
{
switch (multiviewLayout) {
- case VERTICAL_LEFT:
- sourceX = halfCX;
- sourceY = (i / 2 ) * quarterCY;
- if (i % 2 != 0)
- sourceX = halfCX + quarterCX;
+ case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
+ window->sourceX = (i % 6) * window->scenesCX;
+ window->sourceY = window->pvwprgCY +
+ (i / 6) * window->scenesCY;
break;
- case VERTICAL_RIGHT:
- sourceX = 0;
- sourceY = (i / 2 ) * quarterCY;
+ case MultiviewLayout::VERTICAL_LEFT_8_SCENES:
+ window->sourceX = window->pvwprgCX;
+ window->sourceY = (i / 2 ) * window->scenesCY;
if (i % 2 != 0)
- sourceX = quarterCX;
+ window->sourceX += window->scenesCX;
break;
- case HORIZONTAL_BOTTOM:
+ case MultiviewLayout::VERTICAL_RIGHT_8_SCENES:
+ window->sourceX = 0;
+ window->sourceY = (i / 2 ) * window->scenesCY;
+ if (i % 2 != 0)
+ window->sourceX = window->scenesCX;
+ break;
+ case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:
if (i < 4) {
- sourceX = (float(i) * quarterCX);
- sourceY = 0;
+ window->sourceX = (float(i) * window->scenesCX);
+ window->sourceY = 0;
} else {
- sourceX = (float(i - 4) * quarterCX);
- sourceY = quarterCY;
+ window->sourceX = (float(i - 4) *
+ window->scenesCX);
+ window->sourceY = window->scenesCY;
}
break;
- default: //HORIZONTAL_TOP:
+ default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES:
if (i < 4) {
- sourceX = (float(i) * quarterCX);
- sourceY = halfCY;
+ window->sourceX = (float(i) * window->scenesCX);
+ window->sourceY = window->pvwprgCY;
} else {
- sourceX = (float(i - 4) * quarterCX);
- sourceY = halfCY + quarterCY;
+ window->sourceX = (float(i - 4) *
+ window->scenesCX);
+ window->sourceY = window->pvwprgCY +
+ window->scenesCY;
}
}
+ window->siX = window->sourceX + window->thickness;
+ window->siY = window->sourceY + window->thickness;
};
auto calcPreviewProgram = [&](bool program)
{
switch (multiviewLayout) {
- case VERTICAL_LEFT:
- sourceX = 2.0f;
- sourceY = halfCY + 2.0f;
- labelX = offset;
- labelY = halfCY * 1.8f;
+ case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
+ window->sourceX = window->thickness +
+ window->pvwprgCX / 2;
+ window->sourceY = window->thickness;
+ window->labelX = window->offset + window->pvwprgCX / 2;
+ window->labelY = window->pvwprgCY * 0.85f;
if (program) {
- sourceY = 2.0f;
- labelY = halfCY * 0.8f;
+ window->sourceX += window->pvwprgCX;
+ window->labelX += window->pvwprgCX;
}
break;
- case VERTICAL_RIGHT:
- sourceX = halfCX + 2.0f;
- sourceY = halfCY + 2.0f;
- labelX = halfCX + offset;
- labelY = halfCY * 1.8f;
+ case MultiviewLayout::VERTICAL_LEFT_8_SCENES:
+ window->sourceX = window->thickness;
+ window->sourceY = window->pvwprgCY + window->thickness;
+ window->labelX = window->offset;
+ window->labelY = window->pvwprgCY * 1.85f;
if (program) {
- sourceY = 2.0f;
- labelY = halfCY * 0.8f;
+ window->sourceY = window->thickness;
+ window->labelY = window->pvwprgCY * 0.85f;
}
break;
- case HORIZONTAL_BOTTOM:
- sourceX = 2.0f;
- sourceY = halfCY + 2.0f;
- labelX = offset;
- labelY = halfCY * 1.8f;
+ case MultiviewLayout::VERTICAL_RIGHT_8_SCENES:
+ window->sourceX = window->pvwprgCX + window->thickness;
+ window->sourceY = window->pvwprgCY + window->thickness;
+ window->labelX = window->pvwprgCX + window->offset;
+ window->labelY = window->pvwprgCY * 1.85f;
if (program) {
- sourceX = halfCX + 2.0f;
- labelX = halfCX + offset;
+ window->sourceY = window->thickness;
+ window->labelY = window->pvwprgCY * 0.85f;
}
break;
- default: //HORIZONTAL_TOP:
- sourceX = 2.0f;
- sourceY = 2.0f;
- labelX = offset;
- labelY = halfCY * 0.8f;
+ case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:
+ window->sourceX = window->thickness;
+ window->sourceY = window->pvwprgCY + window->thickness;
+ window->labelX = window->offset;
+ window->labelY = window->pvwprgCY * 1.85f;
if (program) {
- sourceX = halfCX + 2.0f;
- labelX = halfCX + offset;
+ window->sourceX += window->pvwprgCX;
+ window->labelX += window->pvwprgCX;
+ }
+ break;
+ default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES:
+ window->sourceX = window->thickness;
+ window->sourceY = window->thickness;
+ window->labelX = window->offset;
+ window->labelY = window->pvwprgCY * 0.85f;
+ if (program) {
+ window->sourceX += window->pvwprgCX;
+ window->labelX += window->pvwprgCX;
}
}
};
+ auto paintAreaWithColor = [&](float tx, float ty, float cx, float cy,
+ uint32_t color)
+ {
+ gs_matrix_push();
+ gs_matrix_translate3f(tx, ty, 0.0f);
+ drawBox(cx, cy, color);
+ gs_matrix_pop();
+ };
+
+ // Define the whole usable region for the multiview
+ startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, window->fw,
+ 0.0f, window->fh);
+
+ // Change the background color to highlight all sources
+ drawBox(window->fw, window->fh, outerColor);
+
/* ----------------------------- */
/* draw sources */
- gs_projection_push();
- gs_viewport_push();
- gs_set_viewport(x, y, targetCX * scale, targetCY * scale);
- gs_ortho(0.0f, targetCXF, 0.0f, targetCYF, -100.0f, 100.0f);
+ for (size_t i = 0; i < maxSrcs; i++) {
+ // Handle all the offsets
+ calcBaseSource(i);
- for (size_t i = 0; i < 8; i++) {
- OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]);
- obs_source *label = window->multiviewLabels[i + 2];
-
- if (!src)
+ if (i >= numSrcs) {
+ // Just paint the background and continue
+ paintAreaWithColor(window->sourceX, window->sourceY,
+ window->scenesCX, window->scenesCY,
+ outerColor);
+ paintAreaWithColor(window->siX, window->siY,
+ window->siCX, window->siCY,
+ backgroundColor);
continue;
+ }
+
+ OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]);
+
+ // We have a source. Now chose the proper highlight color
+ uint32_t colorVal = outerColor;
+ if (src == programSrc)
+ colorVal = programColor;
+ else if (src == previewSrc)
+ colorVal = studioMode ? previewColor : programColor;
+
+ // Paint the background
+ paintAreaWithColor(window->sourceX, window->sourceY,
+ window->scenesCX, window->scenesCY, colorVal);
+ paintAreaWithColor(window->siX, window->siY, window->siCX,
+ window->siCY, backgroundColor);
+
+ /* ----------- */
+
+ // Render the source
+ gs_matrix_push();
+ gs_matrix_translate3f(window->siX, window->siY, 0.0f);
+ gs_matrix_scale3f(window->siScaleX, window->siScaleY, 1.0f);
+ setRegion(window->siX, window->siY, window->siCX, window->siCY);
+ obs_source_video_render(src);
+ endRegion();
+ gs_matrix_pop();
+
+ /* ----------- */
+
+ // Render the label
+ if (!drawLabel)
+ continue;
+
+ obs_source *label = window->multiviewLabels[i + 2];
if (!label)
continue;
- calcBaseSource(i);
-
- qiX = sourceX + 4.0f;
- qiY = sourceY + 4.0f;
-
- /* ----------- */
-
- if (src == previewSrc || src == programSrc) {
- uint32_t colorVal = src == programSrc
- ? 0xFFFF0000
- : 0xFF00FF00;
-
- gs_matrix_push();
- gs_matrix_translate3f(sourceX, sourceY, 0.0f);
- drawBox(quarterCX, quarterCY, colorVal);
- gs_matrix_pop();
-
- gs_matrix_push();
- gs_matrix_translate3f(qiX, qiY, 0.0f);
- drawBox(qiCX, qiCY, 0xFF000000);
- gs_matrix_pop();
- }
-
- /* ----------- */
+ window->offset = labelOffset(label, window->scenesCX);
gs_matrix_push();
- gs_matrix_translate3f(qiX, qiY, 0.0f);
- gs_matrix_scale3f(qiScaleX, qiScaleY, 1.0f);
-
- setRegion(qiX, qiY, qiCX, qiCY);
- obs_source_video_render(src);
- resetRegion();
-
- gs_effect_set_color(color, 0xFFFFFFFF);
- renderVB(solid, window->outerBox, targetCX, targetCY);
-
- gs_matrix_pop();
-
- /* ----------- */
-
- offset = labelOffset(label, quarterCX);
- cx = obs_source_get_width(label);
- cy = obs_source_get_height(label);
-
- gs_matrix_push();
- gs_matrix_translate3f(sourceX + offset,
- (quarterCY * 0.8f) + sourceY, 0.0f);
-
- drawBox(cx, cy + int(quarterCX * 0.015f), 0xD91F1F1F);
+ gs_matrix_translate3f(window->sourceX + window->offset,
+ (window->scenesCY * 0.85f) + window->sourceY,
+ 0.0f);
+ gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f);
+ drawBox(obs_source_get_width(label),
+ obs_source_get_height(label) +
+ int(window->sourceY * 0.015f), labelColor);
obs_source_video_render(label);
-
gs_matrix_pop();
}
- gs_effect_set_color(color, 0xFFFFFFFF);
-
/* ----------------------------- */
/* draw preview */
obs_source_t *previewLabel = window->multiviewLabels[0];
- offset = labelOffset(previewLabel, halfCX);
+ window->offset = labelOffset(previewLabel, window->pvwprgCX);
calcPreviewProgram(false);
+ // Paint the background
+ paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX,
+ window->ppiCY, backgroundColor);
+
+ // Scale and Draw the preview
gs_matrix_push();
- gs_matrix_translate3f(sourceX, sourceY, 0.0f);
- gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
-
- setRegion(sourceX, sourceY, hiCX, hiCY);
-
- if (studioMode) {
+ gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f);
+ gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f);
+ setRegion(window->sourceX, window->sourceY, window->ppiCX,
+ window->ppiCY);
+ if (studioMode)
obs_source_video_render(previewSrc);
- } else {
+ else
obs_render_main_texture();
+ if (drawSafeArea) {
+ renderVB(window->actionSafeMargin, targetCX, targetCY,
+ outerColor);
+ renderVB(window->graphicsSafeMargin, targetCX, targetCY,
+ outerColor);
+ renderVB(window->fourByThreeSafeMargin, targetCX, targetCY,
+ outerColor);
+ renderVB(window->leftLine, targetCX, targetCY, outerColor);
+ renderVB(window->topLine, targetCX, targetCY, outerColor);
+ renderVB(window->rightLine, targetCX, targetCY, outerColor);
}
-
- resetRegion();
-
+ endRegion();
gs_matrix_pop();
/* ----------- */
- gs_matrix_push();
- gs_matrix_translate3f(sourceX, sourceY, 0.0f);
- gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
-
- renderVB(solid, window->outerBox, targetCX, targetCY);
- renderVB(solid, window->innerBox, targetCX, targetCY);
- renderVB(solid, window->leftVLine, targetCX, targetCY);
- renderVB(solid, window->rightVLine, targetCX, targetCY);
- renderVB(solid, window->leftLine, targetCX, targetCY);
- renderVB(solid, window->topLine, targetCX, targetCY);
- renderVB(solid, window->rightLine, targetCX, targetCY);
-
- gs_matrix_pop();
-
- /* ----------- */
-
- cx = obs_source_get_width(previewLabel);
- cy = obs_source_get_height(previewLabel);
-
- gs_matrix_push();
- gs_matrix_translate3f(labelX, labelY, 0.0f);
-
- drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F);
- obs_source_video_render(previewLabel);
-
- gs_matrix_pop();
+ // Draw the Label
+ if (drawLabel) {
+ gs_matrix_push();
+ gs_matrix_translate3f(window->labelX, window->labelY, 0.0f);
+ gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f);
+ drawBox(obs_source_get_width(previewLabel),
+ obs_source_get_height(previewLabel) +
+ int(window->pvwprgCX * 0.015f), labelColor);
+ obs_source_video_render(previewLabel);
+ gs_matrix_pop();
+ }
/* ----------------------------- */
/* draw program */
obs_source_t *programLabel = window->multiviewLabels[1];
- offset = labelOffset(programLabel, halfCX);
+ window->offset = labelOffset(programLabel, window->pvwprgCX);
calcPreviewProgram(true);
+ // Scale and Draw the program
gs_matrix_push();
- gs_matrix_translate3f(sourceX, sourceY, 0.0f);
- gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
-
- setRegion(sourceX, sourceY, hiCX, hiCY);
+ gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f);
+ gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f);
+ setRegion(window->sourceX, window->sourceY, window->ppiCX,
+ window->ppiCY);
obs_render_main_texture();
- resetRegion();
-
+ endRegion();
gs_matrix_pop();
/* ----------- */
- gs_matrix_push();
- gs_matrix_translate3f(sourceX, sourceY, 0.0f);
- gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
+ // Draw the Label
+ if (drawLabel) {
+ gs_matrix_push();
+ gs_matrix_translate3f(window->labelX, window->labelY, 0.0f);
+ gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f);
+ drawBox(obs_source_get_width(programLabel),
+ obs_source_get_height(programLabel) +
+ int(window->pvwprgCX * 0.015f), labelColor);
+ obs_source_video_render(programLabel);
+ gs_matrix_pop();
+ }
- renderVB(solid, window->outerBox, targetCX, targetCY);
+ // Region for future usage with aditional info.
+ if (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) {
+ // Just paint the background for now
+ paintAreaWithColor(window->thickness, window->thickness,
+ window->siCX, window->siCY * 2 +
+ window->thicknessx2, backgroundColor);
+ paintAreaWithColor(window->thickness + 2.5 * (
+ window->thicknessx2 + window->ppiCX),
+ window->thickness, window->siCX,
+ window->siCY * 2 + window->thicknessx2,
+ backgroundColor);
+ }
- gs_matrix_pop();
-
- /* ----------- */
-
- cx = obs_source_get_width(programLabel);
- cy = obs_source_get_height(programLabel);
-
- gs_matrix_push();
- gs_matrix_translate3f(labelX, labelY, 0.0f);
-
- drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F);
- obs_source_video_render(programLabel);
-
- gs_matrix_pop();
-
- /* ----------------------------- */
-
- gs_viewport_pop();
- gs_projection_pop();
+ endRegion();
}
void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
@@ -588,6 +639,7 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
return;
OBSBasic *main = reinterpret_cast(App()->GetMainWindow());
+ OBSSource source = window->source;
uint32_t targetCX;
uint32_t targetCY;
@@ -595,9 +647,9 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
int newCX, newCY;
float scale;
- if (window->source) {
- targetCX = std::max(obs_source_get_width(window->source), 1u);
- targetCY = std::max(obs_source_get_height(window->source), 1u);
+ if (source) {
+ targetCX = std::max(obs_source_get_width(source), 1u);
+ targetCY = std::max(obs_source_get_height(source), 1u);
} else {
struct obs_video_info ovi;
obs_get_video_info(&ovi);
@@ -610,32 +662,26 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
newCX = int(scale * float(targetCX));
newCY = int(scale * float(targetCY));
- gs_viewport_push();
- gs_projection_push();
- gs_ortho(0.0f, float(targetCX), 0.0f, float(targetCY), -100.0f, 100.0f);
- gs_set_viewport(x, y, newCX, newCY);
-
- OBSSource source = window->source;
+ startRegion(x, y, newCX, newCY, 0.0f, float(targetCX), 0.0f,
+ float(targetCY));
if (window->type == ProjectorType::Preview &&
main->IsPreviewProgramMode()) {
OBSSource curSource = main->GetCurrentSceneSource();
- if (window->source != curSource) {
- obs_source_dec_showing(window->source);
+ if (source != curSource) {
+ obs_source_dec_showing(source);
obs_source_inc_showing(curSource);
source = curSource;
}
}
- if (source) {
+ if (source)
obs_source_video_render(source);
- } else {
+ else
obs_render_main_texture();
- }
- gs_projection_pop();
- gs_viewport_pop();
+ endRegion();
}
void OBSProjector::OBSSourceRemoved(void *data, calldata_t *params)
@@ -647,35 +693,50 @@ void OBSProjector::OBSSourceRemoved(void *data, calldata_t *params)
UNUSED_PARAMETER(params);
}
-static int getSourceByPosition(int x, int y)
+static int getSourceByPosition(int x, int y, float ratio)
{
- struct obs_video_info ovi;
- obs_get_video_info(&ovi);
- float ratio = float(ovi.base_width) / float(ovi.base_height);
-
- QWidget *rec = QApplication::activeWindow();
+ int pos = -1;
+ QWidget *rec = QApplication::activeWindow();
+ if (!rec)
+ return pos;
int cx = rec->width();
int cy = rec->height();
int minX = 0;
int minY = 0;
int maxX = cx;
int maxY = cy;
- int halfX = cx / 2;
- int halfY = cy / 2;
- int pos = -1;
switch (multiviewLayout) {
- case VERTICAL_LEFT:
+ case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
if (float(cx) / float(cy) > ratio) {
int validX = cy * ratio;
- maxX = halfX + (validX / 2);
+ minX = (cx / 2) - (validX / 2);
+ maxX = (cx / 2) + (validX / 2);
+ minY = cy / 3;
} else {
int validY = cx / ratio;
- minY = halfY - (validY / 2);
- maxY = halfY + (validY / 2);
+ maxY = (cy / 2) + (validY / 2);
+ minY = (cy / 2) - (validY / 6);
}
- minX = halfX;
+ if (x < minX || x > maxX || y < minY || y > maxY)
+ break;
+
+ pos = (x - minX) / ((maxX - minX) / 6);
+ pos += ((y - minY) / ((maxY - minY) / 4)) * 6;
+
+ break;
+ case MultiviewLayout::VERTICAL_LEFT_8_SCENES:
+ if (float(cx) / float(cy) > ratio) {
+ int validX = cy * ratio;
+ maxX = (cx / 2) + (validX / 2);
+ } else {
+ int validY = cx / ratio;
+ minY = (cy / 2) - (validY / 2);
+ maxY = (cy / 2) + (validY / 2);
+ }
+
+ minX = cx / 2;
if (x < minX || x > maxX || y < minY || y > maxY)
break;
@@ -684,17 +745,17 @@ static int getSourceByPosition(int x, int y)
if (x > minX + ((maxX - minX) / 2))
pos++;
break;
- case VERTICAL_RIGHT:
+ case MultiviewLayout::VERTICAL_RIGHT_8_SCENES:
if (float(cx) / float(cy) > ratio) {
int validX = cy * ratio;
- minX = halfX - (validX / 2);
+ minX = (cx / 2) - (validX / 2);
} else {
int validY = cx / ratio;
- minY = halfY - (validY / 2);
- maxY = halfY + (validY / 2);
+ minY = (cy / 2) - (validY / 2);
+ maxY = (cy / 2) + (validY / 2);
}
- maxX = halfX;
+ maxX = (cx / 2);
if (x < minX || x > maxX || y < minY || y > maxY)
break;
@@ -703,17 +764,17 @@ static int getSourceByPosition(int x, int y)
if (x > minX + ((maxX - minX) / 2))
pos++;
break;
- case HORIZONTAL_BOTTOM:
+ case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:
if (float(cx) / float(cy) > ratio) {
int validX = cy * ratio;
- minX = halfX - (validX / 2);
- maxX = halfX + (validX / 2);
+ minX = (cx / 2) - (validX / 2);
+ maxX = (cx / 2) + (validX / 2);
} else {
int validY = cx / ratio;
- minY = halfY - (validY / 2);
+ minY = (cy / 2) - (validY / 2);
}
- maxY = halfY;
+ maxY = (cy / 2);
if (x < minX || x > maxX || y < minY || y > maxY)
break;
@@ -722,17 +783,17 @@ static int getSourceByPosition(int x, int y)
if (y > minY + ((maxY - minY) / 2))
pos += 4;
break;
- default: // HORIZONTAL_TOP
+ default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES
if (float(cx) / float(cy) > ratio) {
int validX = cy * ratio;
- minX = halfX - (validX / 2);
- maxX = halfX + (validX / 2);
+ minX = (cx / 2) - (validX / 2);
+ maxX = (cx / 2) + (validX / 2);
} else {
int validY = cx / ratio;
- maxY = halfY + (validY / 2);
+ maxY = (cy / 2) + (validY / 2);
}
- minY = halfY;
+ minY = (cy / 2);
if (x < minX || x > maxX || y < minY || y > maxY)
break;
@@ -749,8 +810,10 @@ void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)
{
OBSQTDisplay::mouseDoubleClickEvent(event);
- if (!config_get_bool(GetGlobalConfig(), "BasicWindow",
- "TransitionOnDoubleClick"))
+ if (!mouseSwitching)
+ return;
+
+ if (!transitionOnDoubleClick)
return;
OBSBasic *main = (OBSBasic*)obs_frontend_get_main_window();
@@ -758,8 +821,8 @@ void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)
return;
if (event->button() == Qt::LeftButton) {
- int pos = getSourceByPosition(event->x(), event->y());
- if (pos < 0)
+ int pos = getSourceByPosition(event->x(), event->y(), ratio);
+ if (pos < 0 || pos >= (int)numSrcs)
return;
OBSSource src = OBSGetStrongRef(multiviewScenes[pos]);
if (!src)
@@ -780,9 +843,12 @@ void OBSProjector::mousePressEvent(QMouseEvent *event)
popup.exec(QCursor::pos());
}
+ if (!mouseSwitching)
+ return;
+
if (event->button() == Qt::LeftButton) {
- int pos = getSourceByPosition(event->x(), event->y());
- if (pos < 0)
+ int pos = getSourceByPosition(event->x(), event->y(), ratio);
+ if (pos < 0 || pos >= (int)numSrcs)
return;
OBSSource src = OBSGetStrongRef(multiviewScenes[pos]);
if (!src)
@@ -796,36 +862,76 @@ void OBSProjector::mousePressEvent(QMouseEvent *event)
void OBSProjector::EscapeTriggered()
{
- if (!isWindow) {
- OBSBasic *main = (OBSBasic*)obs_frontend_get_main_window();
- main->RemoveSavedProjectors(savedMonitor);
- }
-
deleteLater();
}
void OBSProjector::UpdateMultiview()
{
- for (OBSWeakSource &val : multiviewScenes)
- val = nullptr;
- for (OBSSource &val : multiviewLabels)
- val = nullptr;
+ multiviewScenes.clear();
+ multiviewLabels.clear();
struct obs_video_info ovi;
obs_get_video_info(&ovi);
- uint32_t h = ovi.base_height;
+ uint32_t w = ovi.base_width;
+ uint32_t h = ovi.base_height;
+ fw = float(w);
+ fh = float(h);
+ ratio = fw / fh;
struct obs_frontend_source_list scenes = {};
obs_frontend_get_scenes(&scenes);
- int curIdx = 0;
+ multiviewLabels.emplace_back(CreateLabel(Str("StudioMode.Preview"),
+ h / 2));
+ multiviewLabels.emplace_back(CreateLabel(Str("StudioMode.Program"),
+ h / 2));
- multiviewLabels[0] = CreateLabel(Str("StudioMode.Preview"), h / 2);
- multiviewLabels[1] = CreateLabel(Str("StudioMode.Program"), h / 2);
+ multiviewLayout = static_cast(config_get_int(
+ GetGlobalConfig(), "BasicWindow", "MultiviewLayout"));
- for (size_t i = 0; i < scenes.sources.num && curIdx < 8; i++) {
- obs_source_t *src = scenes.sources.array[i];
+ drawLabel = config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "MultiviewDrawNames");
+
+ drawSafeArea = config_get_bool(GetGlobalConfig(), "BasicWindow",
+ "MultiviewDrawAreas");
+
+ mouseSwitching = config_get_bool(GetGlobalConfig(), "BasicWindow",
+ "MultiviewMouseSwitch");
+
+ transitionOnDoubleClick = config_get_bool(GetGlobalConfig(),
+ "BasicWindow", "TransitionOnDoubleClick");
+
+ switch(multiviewLayout) {
+ case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
+ pvwprgCX = fw / 3;
+ pvwprgCY = fh / 3;
+
+ maxSrcs = 24;
+ break;
+ default:
+ pvwprgCX = fw / 2;
+ pvwprgCY = fh / 2;
+
+ maxSrcs = 8;
+ }
+
+ ppiCX = pvwprgCX - thicknessx2;
+ ppiCY = pvwprgCY - thicknessx2;
+ ppiScaleX = (pvwprgCX - thicknessx2) / fw;
+ ppiScaleY = (pvwprgCY - thicknessx2) / fh;
+
+ scenesCX = pvwprgCX / 2;
+ scenesCY = pvwprgCY / 2;
+ siCX = scenesCX - thicknessx2;
+ siCY = scenesCY - thicknessx2;
+ siScaleX = (scenesCX - thicknessx2) / fw;
+ siScaleY = (scenesCY - thicknessx2) / fh;
+
+ numSrcs = 0;
+ size_t i = 0;
+ while (i < scenes.sources.num && numSrcs < maxSrcs) {
+ obs_source_t *src = scenes.sources.array[i++];
OBSData data = obs_source_get_private_settings(src);
obs_data_release(data);
@@ -833,32 +939,53 @@ void OBSProjector::UpdateMultiview()
if (!obs_data_get_bool(data, "show_in_multiview"))
continue;
- multiviewScenes[curIdx] = OBSGetWeakRef(src);
+ // We have a displayable source.
+ numSrcs++;
+
+ multiviewScenes.emplace_back(OBSGetWeakRef(src));
obs_source_inc_showing(src);
- std::string name;
- name += std::to_string(curIdx + 1);
- name += " - ";
- name += obs_source_get_name(src);
-
- multiviewLabels[curIdx + 2] = CreateLabel(name.c_str(), h / 3);
-
- curIdx++;
+ std::string name = std::to_string(numSrcs) + " - " +
+ obs_source_get_name(src);
+ multiviewLabels.emplace_back(CreateLabel(name.c_str(), h / 3));
}
obs_frontend_source_list_free(&scenes);
+}
- const char *multiviewLayoutText = config_get_string(GetGlobalConfig(),
- "BasicWindow", "MultiviewLayout");
+void OBSProjector::UpdateProjectorTitle(QString name)
+{
+ projectorTitle = name;
- if (astrcmpi(multiviewLayoutText, "horizontalbottom") == 0)
- multiviewLayout = HORIZONTAL_BOTTOM;
- else if (astrcmpi(multiviewLayoutText, "verticalleft") == 0)
- multiviewLayout = VERTICAL_LEFT;
- else if (astrcmpi(multiviewLayoutText, "verticalright") == 0)
- multiviewLayout = VERTICAL_RIGHT;
- else
- multiviewLayout = HORIZONTAL_TOP;
+ QString title = nullptr;
+ switch (type) {
+ case ProjectorType::Scene:
+ title = QTStr("SceneWindow") + " - " + name;
+ break;
+ case ProjectorType::Source:
+ title = QTStr("SourceWindow") + " - " + name;
+ break;
+ default:
+ title = name;
+ break;
+ }
+
+ setWindowTitle(title);
+}
+
+OBSSource OBSProjector::GetSource()
+{
+ return source;
+}
+
+ProjectorType OBSProjector::GetProjectorType()
+{
+ return type;
+}
+
+int OBSProjector::GetMonitor()
+{
+ return savedMonitor;
}
void OBSProjector::UpdateMultiviewProjectors()
@@ -874,3 +1001,10 @@ void OBSProjector::UpdateMultiviewProjectors()
updatingMultiview = false;
obs_leave_graphics();
}
+
+void OBSProjector::RenameProjector(QString oldName, QString newName)
+{
+ for (auto &projector : windowedProjectors)
+ if (projector->projectorTitle == oldName)
+ projector->UpdateProjectorTitle(newName);
+}
diff --git a/UI/window-projector.hpp b/UI/window-projector.hpp
index 606ab61..1ec629d 100644
--- a/UI/window-projector.hpp
+++ b/UI/window-projector.hpp
@@ -2,10 +2,25 @@
#include
#include "qt-display.hpp"
-#include "window-basic-main.hpp"
+
+enum class ProjectorType {
+ Source,
+ Scene,
+ Preview,
+ StudioProgram,
+ Multiview
+};
class QMouseEvent;
+enum class MultiviewLayout : uint8_t {
+ HORIZONTAL_TOP_8_SCENES = 0,
+ HORIZONTAL_BOTTOM_8_SCENES = 1,
+ VERTICAL_LEFT_8_SCENES = 2,
+ VERTICAL_RIGHT_8_SCENES = 3,
+ HORIZONTAL_TOP_24_SCENES = 4
+};
+
class OBSProjector : public OBSQTDisplay {
Q_OBJECT
@@ -20,31 +35,55 @@ private:
void mousePressEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
- int savedMonitor = 0;
- bool isWindow = false;
+ int savedMonitor;
+ bool isWindow;
+ QString projectorTitle;
ProjectorType type = ProjectorType::Source;
- OBSWeakSource multiviewScenes[8];
- OBSSource multiviewLabels[10];
- gs_vertbuffer_t *outerBox = nullptr;
- gs_vertbuffer_t *innerBox = nullptr;
- gs_vertbuffer_t *leftVLine = nullptr;
- gs_vertbuffer_t *rightVLine = nullptr;
- gs_vertbuffer_t *leftLine = nullptr;
- gs_vertbuffer_t *topLine = nullptr;
- gs_vertbuffer_t *rightLine = nullptr;
+ std::vector multiviewScenes;
+ std::vector