diff --git a/.gitignore b/.gitignore index f515c8000..da6685359 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ /debug32/ /debug64/ /builds/ +.vs/ *.o.d *.ninja .ninja* @@ -27,6 +28,9 @@ #xcode *.xcodeproj/ +#clion +.idea/ + #other stuff (windows stuff, qt moc stuff, etc) Release_MD/ Release/ @@ -45,6 +49,14 @@ install-sh Makefile.in Makefile +#python +__pycache__ + +#sphinx +/docs/sphinx/_build/* +!/docs/sphinx/_build/.gitignore +!/docs/sphinx/Makefile + #random useless file stuff *.dmg *.app @@ -89,3 +101,5 @@ tags */**/.DS_Store dependencies2015/ +nightly +.idea/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index aa5f14a6f..aa640277e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,19 +1,18 @@ [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 [submodule "plugins/enc-amf"] path = plugins/enc-amf - url = https://github.com/Xaymar/obs-studio_amf-encoder-plugin.git + url = https://github.com/obsproject/obs-amd-encoder.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/websocket-client/third_party"] path = plugins/websocket-client/third_party - url = https://github.com/chriskohlhoff/asio.git - + url = https://github.com/chriskohlhoff/asio.git \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9efe0563a..910019688 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/AUTHORS b/AUTHORS index a2f519228..3060d4a0e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,5 @@ +Original Author: Hugh Bailey ("Jim") + Contributors are sorted by their amount of commits / translated strings. Contributors: diff --git a/CI/before-deploy-osx.sh b/CI/before-deploy-osx.sh index f1a87f41c..26671103b 100755 --- a/CI/before-deploy-osx.sh +++ b/CI/before-deploy-osx.sh @@ -15,13 +15,13 @@ 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 ./ +# 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/ +#hr "Moving OBS LUA" +#cp ./rundir/RelWithDebInfo/data/obs-scripting/obslua.so ./rundir/RelWithDebInfo/bin/ # Move obspython # hr "Moving OBS Python" @@ -35,33 +35,48 @@ 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/ +# 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" packagesbuild ../CI/install/osx/CMakeLists.pkgproj # Signing stuff -hr "Decrypting Cert" -openssl aes-256-cbc -K $encrypted_dd3c7f5e9db9_key -iv $encrypted_dd3c7f5e9db9_iv -in ../CI/osxcert/Certificates.p12.enc -out Certificates.p12 -d -hr "Creating Keychain" -security create-keychain -p mysecretpassword build.keychain -security default-keychain -s build.keychain -security unlock-keychain -p mysecretpassword build.keychain -security set-keychain-settings -t 3600 -u build.keychain -hr "Importing certs into keychain" -security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P "" -# macOS 10.12+ -security set-key-partition-list -S apple-tool:,apple: -s -k mysecretpassword build.keychain -hr "Signing Package" -productsign --sign 2MMRE5MTB8 ./OBS.pkg ./$FILENAME +#hr "Decrypting Cert" +#openssl aes-256-cbc -K $encrypted_dd3c7f5e9db9_key -iv $encrypted_dd3c7f5e9db9_iv -in ../CI/osxcert/Certificates.p12.enc -out Certificates.p12 -d +#hr "Creating Keychain" +#security create-keychain -p mysecretpassword build.keychain +#security default-keychain -s build.keychain +#security unlock-keychain -p mysecretpassword build.keychain +#security set-keychain-settings -t 3600 -u build.keychain +#hr "Importing certs into keychain" +#security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P "" +## macOS 10.12+ +#security set-key-partition-list -S apple-tool:,apple: -s -k mysecretpassword build.keychain +#hr "Signing Package" +#productsign --sign 2MMRE5MTB8 ./OBS.pkg ./$FILENAME + +mv ./EBS.pkg ./$FILENAME # Move to the folder that travis uses to upload artifacts from hr "Moving package to nightly folder for distribution" -mkdir ../nightly +mkdir -p ../nightly sudo mv ./$FILENAME ../nightly diff --git a/CI/before-deploy-win.cmd b/CI/before-deploy-win.cmd index 0597fe00e..84a307082 100644 --- a/CI/before-deploy-win.cmd +++ b/CI/before-deploy-win.cmd @@ -1,3 +1,3 @@ -robocopy C:\projects\obs-studio\build32\rundir\RelWithDebInfo C:\projects\obs-studio\build\ /E /XF .gitignore -robocopy C:\projects\obs-studio\build64\rundir\RelWithDebInfo C:\projects\obs-studio\build\ /E /XC /XN /XO /XF .gitignore -7z a build.zip C:\projects\obs-studio\build\* \ No newline at end of file +robocopy C:\projects\ebs-studio\build32\rundir\RelWithDebInfo C:\projects\ebs-studio\build\ /E /XF .gitignore +robocopy C:\projects\ebs-studio\build64\rundir\RelWithDebInfo C:\projects\ebs-studio\build\ /E /XC /XN /XO /XF .gitignore +7z a build.zip C:\projects\ebs-studio\build\* \ No newline at end of file diff --git a/CI/before-script-osx.sh b/CI/before-script-osx.sh index 6cff70dab..9066ef0f6 100755 --- a/CI/before-script-osx.sh +++ b/CI/before-script-osx.sh @@ -1,11 +1,15 @@ # Make sure ccache is found -export PATH=/usr/local/opt/ccache/libexec:$PATH +# export PATH=/usr/local/opt/ccache/libexec:$PATH +# set CEF version for use later +# export CEF_BUILD_VERSION="3.3282.1726.gc8368c8" + +sudo rm -rf build mkdir build cd build -cmake -DENABLE_SPARKLE_UPDATER=ON \ +cmake \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \ --DDepsPath=/tmp/obsdeps \ +-DDepsPath=/tmp/ebsdeps \ -DVLCPath=$PWD/../../vlc-master \ --DBUILD_BROWSER=ON \ --DCEF_ROOT_DIR=$PWD/../../cef_binary_${CEF_BUILD_VERSION}_macosx64 .. \ No newline at end of file +-DCMAKE_INSTALL_PREFIX=/opt/ebs \ +-DCMAKE_BUILD_TYPE=RelWithDebInfo .. \ No newline at end of file diff --git a/CI/install-dependencies-linux-ubuntu16.sh b/CI/install-dependencies-linux-ubuntu16.sh new file mode 100755 index 000000000..470dc2cbb --- /dev/null +++ b/CI/install-dependencies-linux-ubuntu16.sh @@ -0,0 +1,57 @@ +#!/bin/sh +set -ex + +sudo apt-get -qq update +sudo apt-get install -y \ + build-essential \ + checkinstall \ + cmake \ + libasound2-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavformat-dev \ + libavutil-dev \ + libcurl4-openssl-dev \ + libfdk-aac-dev \ + libfontconfig-dev \ + libfreetype6-dev \ + libgl1-mesa-dev \ + libjack-jackd2-dev \ + libjansson-dev \ + libluajit-5.1-dev \ + libpulse-dev \ + libqt5x11extras5-dev \ + libspeexdsp-dev \ + libswresample-dev \ + libswscale-dev \ + libudev-dev \ + libv4l-dev \ + libvlc-dev \ + libx11-dev \ + libx264-dev \ + libxcb-shm0-dev \ + libxcb-xinerama0-dev \ + libxcomposite-dev \ + libxinerama-dev \ + pkg-config \ + python3-dev \ + qtbase5-dev \ + swig + + +# build mbedTLS +cd ~/projects +mkdir mbedtls +cd mbedtls +mbedtlsPath=$PWD +curl -L -O https://github.com/ARMmbed/mbedtls/archive/mbedtls-2.12.0.tar.gz +tar -xf mbedtls-2.12.0.tar.gz +mkdir build +cd ./build +cmake -DENABLE_TESTING=Off -DUSE_SHARED_MBEDTLS_LIBRARY=On ../mbedtls-mbedtls-2.12.0 +make -j 12 +sudo make install + +# return to OBS build dir +cd $APPVEYOR_BUILD_FOLDER diff --git a/CI/install-dependencies-linux.sh b/CI/install-dependencies-linux.sh index 2cbd8de9b..21b68b688 100755 --- a/CI/install-dependencies-linux.sh +++ b/CI/install-dependencies-linux.sh @@ -1,18 +1,18 @@ #!/bin/sh set -ex -sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-next -y +sudo add-apt-repository ppa:jonathonf/ffmpeg-3 -y sudo apt-get -qq update sudo apt-get install -y \ build-essential \ checkinstall \ cmake \ libasound2-dev \ - libavcodec-ffmpeg-dev \ - libavdevice-ffmpeg-dev \ - libavfilter-ffmpeg-dev \ - libavformat-ffmpeg-dev \ - libavutil-ffmpeg-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavformat-dev \ + libavutil-dev \ libcurl4-openssl-dev \ libfdk-aac-dev \ libfontconfig-dev \ @@ -24,10 +24,11 @@ sudo apt-get install -y \ libpulse-dev \ libqt5x11extras5-dev \ libspeexdsp-dev \ - libswresample-ffmpeg-dev \ - libswscale-ffmpeg-dev \ + libswresample-dev \ + libswscale-dev \ libudev-dev \ libv4l-dev \ + libva-dev \ libvlc-dev \ libx11-dev \ libx264-dev \ diff --git a/CI/install-dependencies-osx.sh b/CI/install-dependencies-osx.sh index 4d5329661..ff8098c07 100755 --- a/CI/install-dependencies-osx.sh +++ b/CI/install-dependencies-osx.sh @@ -1,50 +1,72 @@ +hr() { + echo "───────────────────────────────────────────────────" + echo $1 + echo "───────────────────────────────────────────────────" +} + # Exit if something fails set -e # Echo all commands before executing set -v -git fetch --unshallow +# set CEF version for use later +# export CEF_BUILD_VERSION=3.3282.1726.gc8368c8 + +# needed for enabling "ibtool" which is used later during packaging +sudo xcode-select -s /Applications/Xcode.app/Contents/Developer + +# doesn't work in a repo +# git fetch --unshallow # Leave obs-studio folder 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 +# skip installing jack as it causes libcrypto conflict in obs-outputs +# skip installing ccache, enable if you want +brew install speexdsp swig #ccache #jack +# install qt@5.10.1 +brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/8d4d48f0bb552b7b107119aeef59f141ce1f72c3/Formula/qt.rb -export PATH=/usr/local/opt/ccache/libexec:$PATH -ccache -s || echo "CCache is not available." +# 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 +hr "Downloading OBS deps" 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 # Fetch vlc codebase +hr "Downloading VLC repo" wget --retry-connrefused --waitretry=1 -O vlc-master.zip https://github.com/videolan/vlc/archive/master.zip unzip -q ./vlc-master.zip # Get sparkle +hr "Downloading Sparkle framework" 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 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 -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 .. -make -j4 -mkdir libcef_dll -cd ../../ +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 .. +# make -j4 +# mkdir libcef_dll +# cd ../../ diff --git a/CI/install-script-linux.sh b/CI/install-script-linux.sh new file mode 100755 index 000000000..abbfbf00c --- /dev/null +++ b/CI/install-script-linux.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -ex + +build_config=RelWithDebInfo diff --git a/CI/install-script-win.cmd b/CI/install-script-win.cmd new file mode 100644 index 000000000..2975926d2 --- /dev/null +++ b/CI/install-script-win.cmd @@ -0,0 +1,22 @@ +if exist dependencies2017.zip (curl -kLO https://obsproject.com/downloads/dependencies2017.zip -f --retry 5 -z dependencies2017.zip) else (curl -kLO https://obsproject.com/downloads/dependencies2017.zip -f --retry 5 -C -) +if exist vlc.zip (curl -kLO https://obsproject.com/downloads/vlc.zip -f --retry 5 -z vlc.zip) else (curl -kLO https://obsproject.com/downloads/vlc.zip -f --retry 5 -C -) +if exist cef_binary_%CEF_VERSION%_windows32.zip (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows32.zip) else (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32.zip -f --retry 5 -C -) +if exist cef_binary_%CEF_VERSION%_windows64.zip (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows64.zip) else (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64.zip -f --retry 5 -C -) +7z x dependencies2017.zip -odependencies2017 +7z x vlc.zip -ovlc +7z x cef_binary_%CEF_VERSION%_windows32.zip -oCEF_32 +7z x cef_binary_%CEF_VERSION%_windows64.zip -oCEF_64 +set DepsPath32=%CD%\dependencies2017\win32 +set DepsPath64=%CD%\dependencies2017\win64 +set VLCPath=%CD%\vlc +set QTDIR32=C:\Qt\5.11.1\msvc2015 +set QTDIR64=C:\Qt\5.11.1\msvc2017_64 +set CEF_32=%CD%\CEF_32\cef_binary_%CEF_VERSION%_windows32 +set CEF_64=%CD%\CEF_64\cef_binary_%CEF_VERSION%_windows64 +set build_config=RelWithDebInfo +mkdir build build32 build64 +cd ./build32 +cmake -G "Visual Studio 15 2017" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% .. +cd ../build64 +cmake -G "Visual Studio 15 2017 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% .. +cd .. diff --git a/CI/install/osx/CMakeLists.pkgproj b/CI/install/osx/CMakeLists.pkgproj index eff607a0b..d66934e24 100644 --- a/CI/install/osx/CMakeLists.pkgproj +++ b/CI/install/osx/CMakeLists.pkgproj @@ -22,7 +22,7 @@ GID 80 PATH - ../../../build/OBS.app + ../../../build/EBS.app PATH_TYPE 3 PERMISSIONS @@ -80,13 +80,13 @@ CHILDREN - + GID 80 @@ -158,7 +158,7 @@ GID 80 PATH - obs-studio + ebs-studio PATH_TYPE 0 PERMISSIONS @@ -591,9 +591,9 @@ CONCLUSION_ACTION 0 IDENTIFIER - org.obsproject.pkg.obs-studio + evercast.pkg.ebs-studio NAME - OBS + EBS OVERWRITE_PERMISSIONS VERSION @@ -651,7 +651,7 @@ BACKGROUND_PATH PATH - obs.png + ebs.png PATH_TYPE 1 @@ -815,7 +815,7 @@ LANGUAGE English VALUE - OBS + EBS @@ -1015,7 +1015,7 @@ NAME - OBS + EBS TYPE diff --git a/CI/install/osx/build_app.py b/CI/install/osx/build_app.py index 0709adbb8..6758f1d77 100644 --- a/CI/install/osx/build_app.py +++ b/CI/install/osx/build_app.py @@ -3,7 +3,7 @@ candidate_paths = "bin obs-plugins data".split() plist_path = "../cmake/osxbundle/Info.plist" -icon_path = "../cmake/osxbundle/obs.icns" +icon_path = "../cmake/osxbundle/ebs.icns" run_path = "../cmake/osxbundle/obslaunch.sh" #not copied @@ -40,12 +40,12 @@ def add_boolean_argument(parser, name, default=False): '--' + name, nargs='?', default=default, const=True, type=_str_to_bool) group.add_argument('--no' + name, dest=name, action='store_false') -parser = argparse.ArgumentParser(description='obs-studio package util') +parser = argparse.ArgumentParser(description='ebs-studio package util') parser.add_argument('-d', '--base-dir', dest='dir', default='rundir/RelWithDebInfo') 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://builds.catchexception.org/ebs-studio') 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) @@ -71,6 +71,10 @@ def add(name, external=False, copy_as=None): copy_as = name.split("/")[-1] if name[0] != "/": name = build_path+"/"+name + if ("cosmo" in name): + name = "/usr/local/lib/" + name.split("/")[len(name.split("/")) - 1] + print "COSMO LIB FOUND - Replacing /Users/cosmo/ path with /usr/local/lib/" + print name t = LibTarget(name, external, copy_as) if t in inspected: return @@ -82,6 +86,16 @@ def add(name, external=False, copy_as=None): 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, @@ -147,6 +161,12 @@ def add_plugins(path, replace): for path, external, copy_as in inspected: if not external: continue #built with install_rpath hopefully + if ("libcrypto" in path): + libcrypto_path = "/Users/cosmo/DEV/libwebrtc-cmake/build_release/openssl_inst/lib/libcrypto.1.1.dylib" + changes.append("-change '%s' '@rpath/%s'"%(libcrypto_path, copy_as)) + if ("libssl" in path): + libssl_path = "/Users/cosmo/DEV/libwebrtc-cmake/build_release/openssl_inst/lib/libssl.1.1.dylib" + changes.append("-change '%s' '@rpath/%s'"%(libssl_path, copy_as)) changes.append("-change '%s' '@rpath/%s'"%(path, copy_as)) changes = " ".join(changes) @@ -188,7 +208,7 @@ def add_plugins(path, replace): prefix = "tmp/Contents/Resources/" sparkle_path = '@loader_path/{0}/Frameworks/Sparkle.framework/Versions/A/Sparkle' -cmd('{0}install_name_tool -change {1} {2} {3}/bin/obs'.format( +cmd('{0}install_name_tool -change {1} {2} {3}/bin/EBS'.format( args.prefix, actual_sparkle_path, sparkle_path.format('../..'), prefix)) diff --git a/CI/install/osx/ebs.png b/CI/install/osx/ebs.png new file mode 100644 index 000000000..9062e061a Binary files /dev/null and b/CI/install/osx/ebs.png differ diff --git a/CI/install/osx/package_util.py b/CI/install/osx/package_util.py index 2ab0b9473..aae96df0a 100644 --- a/CI/install/osx/package_util.py +++ b/CI/install/osx/package_util.py @@ -79,10 +79,10 @@ def prepare_pkg(project, package_id): import argparse -parser = argparse.ArgumentParser(description='obs-studio package util') +parser = argparse.ArgumentParser(description='ebs-studio package util') parser.add_argument('-u', '--user', dest='user', default='jp9000') -parser.add_argument('-p', '--package-id', dest='package_id', default='org.obsproject.pkg.obs-studio') -parser.add_argument('-f', '--project-file', dest='project', default='OBS.pkgproj') +parser.add_argument('-p', '--package-id', dest='package_id', default='evercast.pkg.ebs-studio') +parser.add_argument('-f', '--project-file', dest='project', default='EBS.pkgproj') parser.add_argument('-j', '--jenkins-build', dest='jenkins_build', default='0') parser.add_argument('-b', '--branch', dest='branch', default='master') parser.add_argument('-s', '--stable', dest='stable', required=False, action='store_true', default=False) diff --git a/CI/install/osx/post-install.sh b/CI/install/osx/post-install.sh index f55ae58bb..a0d357bb5 100644 --- a/CI/install/osx/post-install.sh +++ b/CI/install/osx/post-install.sh @@ -1,5 +1,5 @@ #!/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" +# 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 12fb805b9..5dfbb5332 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 @@ -26,7 +34,7 @@ trap cleanup EXIT cd $WORK_DIR -DEPS_DEST=$WORK_DIR/obsdeps +DEPS_DEST=$WORK_DIR/ebsdeps # make dest dirs mkdir $DEPS_DEST @@ -35,54 +43,55 @@ 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" +../configure --disable-shared --enable-static --prefix="/tmp/ebsdeps" make -j 12 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" +../configure --disable-shared --enable-static --prefix="/tmp/ebsdeps" make -j 12 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" +../configure --disable-shared --enable-static --prefix="/tmp/ebsdeps" make -j 12 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/ebsdeps" --libdir="/tmp/ebsdeps/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/ebsdeps" 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/ebsdeps/bin" --prefix="/tmp/ebsdeps" make -j 12 ln -f -s libx264.*.dylib libx264.dylib find . -name \*.dylib -exec cp \{\} $DEPS_DEST/bin/ \; @@ -107,12 +116,12 @@ 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 +../configure --libdir="/tmp/ebsdeps/bin" --enable-shared --disable-static make -j 12 find . -name \*.dylib -exec cp \{\} $DEPS_DEST/bin/ \; rsync -avh --include="*/" --include="*.h" --exclude="*" ../* $DEPS_DEST/include/ @@ -120,16 +129,16 @@ rsync -avh --include="*/" --include="*.h" --exclude="*" ./* $DEPS_DEST/include/ cd $WORK_DIR -export LDFLAGS="-L/tmp/obsdeps/lib" -export CFLAGS="-I/tmp/obsdeps/include" +export LDFLAGS="-L/tmp/ebsdeps/lib" +export CFLAGS="-I/tmp/ebsdeps/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/ebsdeps/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/ @@ -139,14 +148,14 @@ rsync -avh --include="*/" --include="*.h" --exclude="*" ./* $DEPS_DEST/include/ curl -L -O https://luajit.org/download/LuaJIT-2.0.5.tar.gz tar -xf LuaJIT-2.0.5.tar.gz cd LuaJIT-2.0.5 -make PREFIX=/tmp/obsdeps -make PREFIX=/tmp/obsdeps install -find /tmp/obsdeps/lib -name libluajit\*.dylib -exec cp \{\} $DEPS_DEST/lib/ \; +make PREFIX=/tmp/ebsdeps +make PREFIX=/tmp/ebsdeps install +find /tmp/ebsdeps/lib -name libluajit\*.dylib -exec cp \{\} $DEPS_DEST/lib/ \; rsync -avh --include="*/" --include="*.h" --exclude="*" src/* $DEPS_DEST/include/ -make PREFIX=/tmp/obsdeps uninstall +make PREFIX=/tmp/ebsdeps uninstall cd $WORK_DIR -tar -czf osx-deps.tar.gz obsdeps +tar -czf osx-deps.tar.gz ebsdeps -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 d1f48d056..72324bf84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,11 @@ cmake_minimum_required(VERSION 2.8.12) -project(obs-studio) +if (UNIX AND POLICY CMP0072) + # In case of both legacy and glvnd OpenGL libraries found. Prefer GLVND + cmake_policy(SET CMP0072 NEW) +endif() + +project(ebs-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" @@ -45,8 +67,8 @@ if(${CMAKE_C_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} MATCHES "C endif() if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) - set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-field-initializers ${CMAKE_CXX_FLAGS} -fno-strict-aliasing") - set(CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=gnu99 -fno-strict-aliasing") + set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-field-initializers ${CMAKE_CXX_FLAGS} -fno-strict-aliasing") + set(CMAKE_C_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=gnu99 -fno-strict-aliasing") option(USE_LIBC++ "Use libc++ instead of libstdc++" ${APPLE}) if(USE_LIBC++) @@ -60,7 +82,7 @@ elseif(MSVC) endif() # Disable pointless constant condition warnings - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4127 /wd4201") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4127 /wd4201 /wd4456 /wd4457 /wd4458 /wd4459 /wd4595") endif() if(WIN32) @@ -105,11 +127,29 @@ option(BUILD_TESTS "Build test directory (includes test sources and possibly a p mark_as_advanced(BUILD_TESTS) if(NOT INSTALLER_RUN) - option(ENABLE_UI "Enables the OBS user interfaces" ON) + option(ENABLE_UI "Enables the EBS user interfaces" ON) if(DISABLE_UI OR NOT ENABLE_UI) set(UI_ENABLED FALSE) else() set(UI_ENABLED TRUE) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) + else() + set(_lib_suffix 32) + endif() + + if(DEFINED QTDIR${_lib_suffix}) + list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}") + elseif(DEFINED QTDIR) + list(APPEND CMAKE_PREFIX_PATH "${QTDIR}") + elseif(DEFINED ENV{QTDIR${_lib_suffix}}) + list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}") + elseif(DEFINED ENV{QTDIR}) + list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}") + endif() + + find_package(Qt5Widgets ${FIND_MODE}) endif() add_subdirectory(deps) @@ -120,8 +160,8 @@ if(NOT INSTALLER_RUN) add_subdirectory(libobs-opengl) add_subdirectory(libobs) - add_subdirectory(UI) add_subdirectory(plugins) + add_subdirectory(UI) if (BUILD_TESTS) add_subdirectory(test) endif() diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 29518b151..6ea05c288 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 258ea2670..cf2707dfa 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/README.md b/README.md index 576c9c8eb..4e9565433 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ +# OBS-studio WebRTC - Fixed Mac CI Scripts +<<<<<<< HEAD # OBS-studio WebRTC This project is a fork of OBS-studio with an implementation of WebRTC. @@ -36,9 +38,13 @@ Binaries are available [here](https://github.com/CoSMoSoftware/OBS-studio-webrtc Get the Windows installer for libwebrtc version 65 provided by CoSMo: [Windows installer](https://drive.google.com/file/d/1EM0OXGS0Xm61m5Nhb-2nNNJo1JpbBZnB/view?usp=sharing) +======= +See the latest commit for CI script changes. +>>>>>>> e8d484cb... fix CI scripts, fix Cosmo lib path, remove CEF, update readme #### Compiler +<<<<<<< HEAD Make sure the correct tools / Windows 10 SDK are installed. - Windows 7 x64 or later @@ -282,3 +288,28 @@ Configure a JANUS server using the video room plugin with websocket protocol act ### OBS settings Launch OBS, go to settings, select the stream tab and change the URL to your JANUS. +======= + - [`install-dependencies-osx.sh`](./CI/install-dependencies-osx.sh) installs dependencies + - [`before-script-osx.sh`](./CI/before-script-osx.sh) creates a build dir and runs cmake + - [`before-deploy-osx.sh`](./CI/before-deploy-osx.sh) + - calls [`build_app.py`](./CI/install/osx/build_app.py) which fixes all lib paths + - packages app into a .pkg + - signs the app with developer certificate if available + +```bash +mkdir obs-and-dependencies +cd obs-and-dependencies +git clone --recursive https://github.com/ruddell/OBS-studio-webrtc.git +cd OBS-studio-webrtc +git checkout mac-build + +# only run this the once to install dependencies +./CI/install-dependencies-osx.sh + +# run this to rebuild the package, read the scripts to see what they do +./CI/before-script-osx.sh +cd build && make -j 8 && cd .. +./CI/before-deploy-osx.sh +open nightly +``` +>>>>>>> e8d484cb... fix CI scripts, fix Cosmo lib path, remove CEF, update readme diff --git a/README.rst b/README.rst index f43960ddd..f6561c401 100644 --- a/README.rst +++ b/README.rst @@ -15,11 +15,11 @@ Quick Links - Website: https://obsproject.com - - Help/Documentation/Guides: https://github.com/jp9000/obs-studio/wiki + - Help/Documentation/Guides: https://github.com/obsproject/obs-studio/wiki - Forums: https://obsproject.com/forum/ - - Build Instructions: https://github.com/jp9000/obs-studio/wiki/Install-Instructions + - Build Instructions: https://github.com/obsproject/obs-studio/wiki/Install-Instructions - Developer/API Documentation: https://obsproject.com/docs @@ -33,7 +33,7 @@ Contributing - If you wish to contribute code to the project, please make sure to read the coding and commit guidelines: - https://github.com/jp9000/obs-studio/blob/master/CONTRIBUTING.rst + https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.rst - Developer/API documentation can be found here: https://obsproject.com/docs @@ -47,3 +47,9 @@ Contributing you fully understand -- bad advice is worse than no advice. When it comes to something that you don't fully know or understand, please defer to the official help or official channels. + +Credits +------- + - Icons made by `Freepik `_ from + `Flaticon `_ are licensed under + `CC 3.0 BY `_. diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 8f2a2fc7f..9d175f637 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -7,33 +7,21 @@ else() set(FIND_MODE QUIET) endif() +if(BROWSER_AVAILABLE_INTERNAL) + add_definitions(-DBROWSER_AVAILABLE) +endif() + add_subdirectory(obs-frontend-api) # ---------------------------------------------------------------------------- -project(obs) +project(EBS) -set(ENABLE_WIN_UPDATER FALSE CACHE BOOL "Enable the windows updater") - -if(DEFINED QTDIR${_lib_suffix}) - list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}") -elseif(DEFINED QTDIR) - list(APPEND CMAKE_PREFIX_PATH "${QTDIR}") -elseif(DEFINED ENV{QTDIR${_lib_suffix}}) - list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}") -elseif(DEFINED ENV{QTDIR}) - list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}") -endif() +set(DISABLE_UPDATE_MODULE TRUE CACHE BOOL "Disables building the update module") set(CMAKE_INCLUDE_CURRENT_DIR TRUE) set(CMAKE_AUTOMOC TRUE) -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - find_package(Qt5 COMPONENTS Core Widgets ${FIND_MODE}) set(WEBRTC_USE_FILE_INCLUDED 1) find_package(libwebrtc REQUIRED) @@ -42,7 +30,7 @@ include ( "${libwebrtc_DIR}/libwebrtcConfigVersion.cmake" ) find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat) if(APPLE) - find_package(Qt5 COMPONENTS MacExtras ${FIND_MODE}) + find_package(Qt5MacExtras REQUIRED) endif(APPLE) if(NOT Qt5Widgets_FOUND) @@ -59,6 +47,10 @@ 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") +if(BROWSER_AVAILABLE_INTERNAL) + include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/plugins/obs-browser/panel") +endif() find_package(Libcurl REQUIRED) include_directories(${LIBCURL_INCLUDE_DIRS}) @@ -82,10 +74,6 @@ if(WIN32) crypt32 blake2 ${OBS_JANSSON_IMPORT}) - - if(ENABLE_WIN_UPDATER) - add_definitions(-DENABLE_WIN_UPDATER) - endif() elseif(APPLE) set(obs_PLATFORM_SOURCES platform-osx.mm) @@ -135,6 +123,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 @@ -151,15 +140,16 @@ set(obs_SOURCES window-basic-main-transitions.cpp window-basic-main-dropfiles.cpp window-basic-main-profiles.cpp - window-license-agreement.cpp window-basic-status-bar.cpp window-basic-adv-audio.cpp window-basic-transform.cpp window-basic-preview.cpp + window-basic-about.cpp window-namedialog.cpp window-log-reply.cpp window-projector.cpp window-remux.cpp + source-tree.cpp properties-view.cpp focus-list.cpp menu-button.cpp @@ -169,6 +159,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 @@ -184,6 +175,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 @@ -196,7 +188,7 @@ set(obs_HEADERS window-basic-auto-config.hpp window-basic-main-outputs.hpp window-basic-source-select.hpp - window-license-agreement.hpp + window-basic-about.hpp window-basic-status-bar.hpp window-basic-adv-audio.hpp window-basic-transform.hpp @@ -205,9 +197,11 @@ 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 + balance-slider.hpp double-slider.hpp focus-list.hpp menu-button.hpp @@ -217,6 +211,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,7 +223,8 @@ set(obs_HEADERS source-label.hpp remote-text.hpp audio-encoders.hpp - qt-wrappers.hpp) + qt-wrappers.hpp + clickable-label.hpp) set(obs_UI forms/NameDialog.ui @@ -235,7 +232,7 @@ set(obs_UI forms/AutoConfigVideoPage.ui forms/AutoConfigStreamPage.ui forms/AutoConfigTestPage.ui - forms/OBSLicenseAgreement.ui + forms/ColorSelect.ui forms/OBSLogReply.ui forms/OBSBasic.ui forms/OBSBasicTransform.ui @@ -244,7 +241,8 @@ set(obs_UI forms/OBSBasicSourceSelect.ui forms/OBSBasicInteraction.ui forms/OBSUpdate.ui - forms/OBSRemux.ui) + forms/OBSRemux.ui + forms/OBSAbout.ui) set(obs_QRC forms/obs.qrc) @@ -252,7 +250,7 @@ set(obs_QRC qt5_wrap_ui(obs_UI_HEADERS ${obs_UI}) qt5_add_resources(obs_QRC_SOURCES ${obs_QRC}) -add_executable(obs WIN32 +add_executable(EBS WIN32 ${obs_SOURCES} ${obs_HEADERS} ${obs_UI_HEADERS} @@ -265,12 +263,12 @@ if(WIN32) set(_output_suffix "32") endif() - set_target_properties(obs + set_target_properties(EBS PROPERTIES - OUTPUT_NAME "obs${_output_suffix}") + OUTPUT_NAME "EBS${_output_suffix}") endif() -target_link_libraries(obs +target_link_libraries(EBS libobs Qt5::Widgets obs-frontend-api @@ -279,11 +277,11 @@ target_link_libraries(obs ${obs_PLATFORM_LIBRARIES}) if (APPLE) - target_link_libraries(obs + target_link_libraries(EBS Qt5::MacExtras) - set_target_properties(obs PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000") + set_target_properties(EBS PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000") set_property( - TARGET obs + TARGET EBS APPEND PROPERTY INSTALL_RPATH "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/" @@ -292,14 +290,15 @@ if (APPLE) ) endif() -define_graphic_modules(obs) +define_graphic_modules(EBS) -install_obs_core(obs) -install_obs_data(obs data obs-studio) +install_obs_core(EBS) +install_obs_data(EBS data ebs-studio) +install_obs_data_file(EBS ../AUTHORS ebs-studio/authors) if (UNIX AND UNIX_STRUCTURE AND NOT APPLE) install(FILES dist/obs.desktop DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/applications) - install(FILES forms/images/obs.png + install(FILES forms/images/ebs.png DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons/hicolor/256x256/apps) endif() diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index 531b00173..c327a6109 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -4,16 +4,16 @@ #include #include #include -#include #include "qt-wrappers.hpp" #include "obs-app.hpp" #include "adv-audio-control.hpp" +#include "window-basic-main.hpp" #ifndef NSEC_PER_MSEC #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; @@ -25,13 +25,13 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_) forceMonoContainer = new QWidget(); mixerContainer = new QWidget(); - panningContainer = new QWidget(); + balanceContainer = new QWidget(); labelL = new QLabel(); labelR = new QLabel(); nameLabel = new QLabel(); volume = new QSpinBox(); forceMono = new QCheckBox(); - panning = new QSlider(Qt::Horizontal); + balance = new BalanceSlider(); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType = new QComboBox(); #endif @@ -60,8 +60,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_) mixerContainer->setLayout(hlayout); hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); - panningContainer->setLayout(hlayout); - panningContainer->setMinimumWidth(100); + balanceContainer->setLayout(hlayout); + balanceContainer->setMinimumWidth(100); labelL->setText("L"); @@ -81,11 +81,24 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_) forceMonoContainer->layout()->setAlignment(forceMono, Qt::AlignHCenter | Qt::AlignVCenter); - panning->setMinimum(0); - panning->setMaximum(100); - panning->setTickPosition(QSlider::TicksAbove); - panning->setEnabled(false); - panning->setValue(50); /* XXX */ + balance->setOrientation(Qt::Horizontal); + balance->setMinimum(0); + balance->setMaximum(100); + balance->setTickPosition(QSlider::TicksAbove); + balance->setTickInterval(50); + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + const char *speakers = config_get_string(main->Config(), "Audio", + "ChannelSetup"); + + if (strcmp(speakers, "Mono") == 0) + balance->setEnabled(false); + else + balance->setEnabled(true); + + float bal = obs_source_get_balance_value(source) * 100.0f; + balance->setValue((int)bal); int64_t cur_sync = obs_source_get_sync_offset(source); syncOffset->setMinimum(-20000); @@ -118,10 +131,14 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_) mixer6->setText("6"); mixer6->setChecked(mixers & (1<<5)); - panningContainer->layout()->addWidget(labelL); - panningContainer->layout()->addWidget(panning); - panningContainer->layout()->addWidget(labelR); - panningContainer->setMaximumWidth(170); + speaker_layout sl = obs_source_get_speaker_layout(source); + + if (sl == SPEAKERS_STEREO) { + balanceContainer->layout()->addWidget(labelL); + balanceContainer->layout()->addWidget(balance); + balanceContainer->layout()->addWidget(labelR); + balanceContainer->setMaximumWidth(170); + } mixerContainer->layout()->addWidget(mixer1); mixerContainer->layout()->addWidget(mixer2); @@ -134,8 +151,10 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_) this, SLOT(volumeChanged(int))); QWidget::connect(forceMono, SIGNAL(clicked(bool)), this, SLOT(downmixMonoChanged(bool))); - QWidget::connect(panning, SIGNAL(valueChanged(int)), - this, SLOT(panningChanged(int))); + QWidget::connect(balance, SIGNAL(valueChanged(int)), + this, SLOT(balanceChanged(int))); + QWidget::connect(balance, SIGNAL(doubleClicked()), + this, SLOT(ResetBalance())); QWidget::connect(syncOffset, SIGNAL(valueChanged(int)), this, SLOT(syncOffsetChanged(int))); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO @@ -155,20 +174,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() @@ -176,7 +182,7 @@ OBSAdvAudioCtrl::~OBSAdvAudioCtrl() nameLabel->deleteLater(); volume->deleteLater(); forceMonoContainer->deleteLater(); - panningContainer->deleteLater(); + balanceContainer->deleteLater(); syncOffset->deleteLater(); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType->deleteLater(); @@ -184,6 +190,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(balanceContainer, 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 */ @@ -278,12 +302,26 @@ void OBSAdvAudioCtrl::downmixMonoChanged(bool checked) } } -void OBSAdvAudioCtrl::panningChanged(int val) +void OBSAdvAudioCtrl::balanceChanged(int val) { - /* TODO */ - UNUSED_PARAMETER(val); + float bal = (float)val / 100.0f; + + if (abs(50 - val) < 10) { + balance->blockSignals(true); + balance->setValue(50); + bal = 0.5f; + balance->blockSignals(false); + } + + obs_source_set_balance_value(source, bal); } +void OBSAdvAudioCtrl::ResetBalance() +{ + balance->setValue(50); +} + + void OBSAdvAudioCtrl::syncOffsetChanged(int milliseconds) { int64_t cur_val = obs_source_get_sync_offset(source); diff --git a/UI/adv-audio-control.hpp b/UI/adv-audio-control.hpp index 38d760823..80d248e83 100644 --- a/UI/adv-audio-control.hpp +++ b/UI/adv-audio-control.hpp @@ -3,12 +3,12 @@ #include #include #include +#include "balance-slider.hpp" class QGridLayout; class QLabel; class QSpinBox; class QCheckBox; -class QSlider; class QComboBox; class OBSAdvAudioCtrl : public QObject { @@ -19,12 +19,12 @@ class OBSAdvAudioCtrl : public QObject { QPointer forceMonoContainer; QPointer mixerContainer; - QPointer panningContainer; + QPointer balanceContainer; QPointer nameLabel; QPointer volume; QPointer forceMono; - QPointer panning; + QPointerbalance; QPointer labelL; QPointer labelR; QPointer syncOffset; @@ -51,6 +51,7 @@ class OBSAdvAudioCtrl : public QObject { virtual ~OBSAdvAudioCtrl(); inline obs_source_t *GetSource() const {return source;} + void ShowAudioControl(QGridLayout *layout); public slots: void SourceFlagsChanged(uint32_t flags); @@ -60,7 +61,7 @@ public slots: void volumeChanged(int percentage); void downmixMonoChanged(bool checked); - void panningChanged(int val); + void balanceChanged(int val); void syncOffsetChanged(int milliseconds); void monitoringTypeChanged(int index); void mixer1Changed(bool checked); @@ -69,4 +70,5 @@ public slots: void mixer4Changed(bool checked); void mixer5Changed(bool checked); void mixer6Changed(bool checked); + void ResetBalance(); }; diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index a580b721c..e7cc8365c 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/balance-slider.hpp b/UI/balance-slider.hpp new file mode 100644 index 000000000..06be1b5f8 --- /dev/null +++ b/UI/balance-slider.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class BalanceSlider : public QSlider { + Q_OBJECT + +public: + inline BalanceSlider(QWidget *parent = 0) : QSlider(parent) {} + +signals: + void doubleClicked(); + +protected: + void mouseDoubleClickEvent(QMouseEvent *event) + { + emit doubleClicked(); + event->accept(); + } +}; diff --git a/UI/clickable-label.hpp b/UI/clickable-label.hpp new file mode 100644 index 000000000..3c76a8b4c --- /dev/null +++ b/UI/clickable-label.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class ClickableLabel : public QLabel { + Q_OBJECT + +public: + inline ClickableLabel(QWidget *parent = 0) : QLabel(parent) {} + +signals: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *event) + { + emit clicked(); + event->accept(); + } +}; diff --git a/UI/crash-report.cpp b/UI/crash-report.cpp index 6a94692e7..8f5c94efa 100644 --- a/UI/crash-report.cpp +++ b/UI/crash-report.cpp @@ -38,7 +38,7 @@ OBSCrashReport::OBSCrashReport(QWidget *parent, const char *text) this, SLOT(ExitClicked())); resize(800, 600); - setWindowTitle("Oops, OBS has crashed!"); + setWindowTitle("Oops, EBS has crashed!"); } void OBSCrashReport::ExitClicked() diff --git a/UI/data/locale.ini b/UI/data/locale.ini index 4631d78dc..00e4c1fdb 100644 --- a/UI/data/locale.ini +++ b/UI/data/locale.ini @@ -139,3 +139,17 @@ Name=ქართული [nn-NO] Name=Norsk Nynorsk +[fil-PH] +Name=Wikang Filipino + +[sq-AL] +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 d44bfd8d6..ffc182af6 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 e01396337..ae2c6f9c7 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 e62a41c6c..183e2d414 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="Тип Стрийм" @@ -546,12 +542,27 @@ Basic.Settings.Output.Simple.Warn.Encoder="Предупреждение: Зап Basic.Settings.Output.Simple.Warn.Lossless="Предупреждение: Оригиналното качество създава огромни файлове! Запис настроен на Оригинално качество може да заема над 7GB дисково пространство на минута, ако резолюцията и кадрите са високи. Не се препоръчва ако не разполагате със много пространство." 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="Настройка на Енкодера (по-висока = по-малко процесор)" +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" @@ -588,7 +599,9 @@ Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Настройки при сли Basic.Settings.Output.Adv.FFmpeg.GOPSize="Интервал между ключови кадри (кадри)" Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Покажи всички кодеци (дори и ако са несъвместими)" +FilenameFormatting.completer="%ХХГГ-%ММ-%ДД %чч-%мм-%сс\n%ГГ-%ММ-%ДД %чч-%мм-%сс\n%Г-%м-%д %Ч-%М-%С\n%г-%м-%д %Ч-%М-%С\n%а %Г-%м-%д %Ч-%М-%С\n%А %Г-%м-%д %Ч-%М-%С\n%Г-%б-%д %Ч-%М-%С\n%Г-%Б-%д %Ч-%М-%С\n%Г- %м-%д %И-%М-%С-%п\n%Г-%м-%д %Ч-%М-%С-%з\n%Г-%м-%д %Ч-%М-%С-%ст" +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%% % подпис\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="Видео адаптер:" @@ -614,6 +627,10 @@ 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="Средно (ТИП I PPM)" +Basic.Settings.Audio.MeterDecayRate.Slow="Бавно (Type II PPM)" Basic.Settings.Audio.MultiChannelWarning.Enabled="ПРЕДУПРЕЖДЕНИЕ: Включен е Surround sound." 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="Включи записването на съраунд звук?" @@ -635,6 +652,7 @@ 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="Формат на цвета" @@ -644,22 +662,37 @@ 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="Режим на ниска латенция" Basic.AdvAudio="Допълнителни Звукови Характеристики" Basic.AdvAudio.Name="Име" Basic.AdvAudio.Volume="Сила на звука (%)" Basic.AdvAudio.Mono="Премиксирай към 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="Вмъкни" Hotkeys.Delete="Изтрий" @@ -668,9 +701,33 @@ Hotkeys.End="Край" Hotkeys.PageUp="Страница нагоре" Hotkeys.PageDown="Страница надолу" Hotkeys.NumLock="Num Lock" +Hotkeys.ScrollLock="Скрол Лок" +Hotkeys.CapsLock="Капс Лок" +Hotkeys.Backspace="Връщане" +Hotkeys.Tab="Таб" +Hotkeys.Print="Принт" +Hotkeys.Pause="Пауза" +Hotkeys.Left="Ляво" +Hotkeys.Right="Дясно" +Hotkeys.Up="Горе" +Hotkeys.Down="Долу" +Hotkeys.Windows="Windows" +Hotkeys.Super="Супер" +Hotkeys.Menu="Меню" +Hotkeys.Space="Спейс" +Hotkeys.NumpadNum="Нумпад %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="Заглуши" @@ -688,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 b6d333fd5..8039b7342 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 eb8a3823e..1b5825995 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 5665c6fcb..8ae5fd3ba 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,6 +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?" @@ -671,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" @@ -745,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 15bbbe54e..615dcd9fb 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 d0f64dc70..5051ea51f 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 17fe7ff51..5c9954e3c 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 9ed218448..8b00b90ab 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -85,8 +85,8 @@ StudioMode.Program="Program" ShowInMultiview="Show in Multiview" # warning if program already open -AlreadyRunning.Title="OBS is already running" -AlreadyRunning.Text="OBS is already running! Unless you meant to do this, please shut down any existing instances of OBS before trying to run a new instance. If you have OBS set to minimize to the system tray, please check to see if it's still running there." +AlreadyRunning.Title="EBS is already running" +AlreadyRunning.Text="EBS is already running! Unless you meant to do this, please shut down any existing instances of EBS before trying to run a new instance. If you have EBS set to minimize to the system tray, please check to see if it's still running there." AlreadyRunning.LaunchAnyway="Launch Anyway" # copy filters @@ -224,8 +224,8 @@ ConfirmStop.Title="Stop Stream?" ConfirmStop.Text="Are you sure you want to stop the stream?" # confirm exit dialog box -ConfirmExit.Title="Exit OBS?" -ConfirmExit.Text="OBS is currently active. All streams/recordings will be shut down. Are you sure you wish to exit?" +ConfirmExit.Title="Exit EBS?" +ConfirmExit.Text="EBS is currently active. All streams/recordings will be shut down. Are you sure you wish to exit?" # confirm delete dialog box ConfirmRemove.Title="Confirm Remove" @@ -267,20 +267,20 @@ LogReturnDialog.ErrorUploadingLog="Error uploading log file" # license agreement dialog LicenseAgreement="License Agreement" -LicenseAgreement.PleaseReview="Please review the license terms before using OBS. By using this program, you acknowledge that you have read and agree to the terms of the GNU General Public License v2.0. Please scroll down to see the rest of the agreement." -LicenseAgreement.ClickIAgreeToContinue="If you accept the terms of the agreement, click I Agree to continue. You must accept the agreement to use OBS." +LicenseAgreement.PleaseReview="Please review the license terms before using EBS. By using this program, you acknowledge that you have read and agree to the terms of the GNU General Public License v2.0. Please scroll down to see the rest of the agreement." +LicenseAgreement.ClickIAgreeToContinue="If you accept the terms of the agreement, click I Agree to continue. You must accept the agreement to use EBS." LicenseAgreement.IAgree="I Agree" LicenseAgreement.Exit="Exit" # remux dialog -Remux.SourceFile="OBS Recording" +Remux.SourceFile="EBS Recording" Remux.TargetFile="Target File" Remux.Remux="Remux" -Remux.OBSRecording="OBS Recording" +Remux.OBSRecording="EBS Recording" Remux.FinishedTitle="Remuxing finished" Remux.Finished="Recording remuxed" Remux.FinishedError="Recording remuxed, but the file may be incomplete" -Remux.SelectRecording="Select OBS Recording …" +Remux.SelectRecording="Select EBS Recording …" Remux.SelectTarget="Select target file …" Remux.FileExistsTitle="Target file exists" Remux.FileExists="Target file exists, do you want to replace it?" @@ -705,7 +705,7 @@ 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.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="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\nEBS audio filters are compatible with surround sound, though VST plugin support isn't guaranteed." Basic.Settings.Audio.MultichannelWarning.Title="Enable surround sound audio?" Basic.Settings.Audio.MultichannelWarning.Confirm="Are you sure you want to enable surround sound audio?" Basic.Settings.Audio.DesktopDevice="Desktop Audio Device" diff --git a/UI/data/locale/es-ES.ini b/UI/data/locale/es-ES.ini index ac279b076..df65678e9 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 75a3c842f..694f6ada2 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 4e80c29b0..51e163d2e 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 000000000..8593961fe --- /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 d9b37b4f4..8e72cbcb4 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 new file mode 100644 index 000000000..80ec52996 --- /dev/null +++ b/UI/data/locale/fil-PH.ini @@ -0,0 +1,753 @@ + + +OK="Sige" +Apply="Ilagay" +Cancel="Kanselahin" +Close="Sarado" +Save="Mag-impok" +Discard="Ialis" +Disable="Huwag paganahin" +Yes="Oo" +No="Hindi" +Add="Idagdag" +Remove="Tanggalin" +Rename="Baguhin ang pangalan" +Interact="Makipag-ugnayan" +Filters="Mga salaan" +Properties="Mga pag-aari" +MoveUp="Gumalaw pataas" +MoveDown="Bumaba" +Settings="Mga pagtatakda" +Display="Ipamalas" +Name="Pangalan" +Exit="Labasan" +Mixer="Panghalo" +Browse="Supling" +Mono="Mono" +Stereo="Stereo" +DroppedFrames="Bumaba ang mga frame %1 (%2%)" +StudioProgramProjector="Fullscreen Projector (Programa)" +PreviewProjector="Fullscreen Projector (Preview)" +SceneProjector="Fullsreen Projector (Eksena)" +SourceProjector="Fullscreen Projector (Pinagmulan)" +StudioProgramWindow="Windowed Projector (Programa)" +PreviewWindow="Windowed Projector (Preview)" +SceneWindow="Windowed Projector (Eksena)" +SourceWindow="Windowed Projector (Pinagmulan)" +MultiviewProjector="Multiview (Fullscreen)" +MultiviewWindowed="Multiview (Windowed)" +Clear="Linisin" +Revert="Ibalik" +Show="Ipakita" +Hide="Itago" +UnhideAll="Huwag itago lahat" +Untitled="Walang pamagat" +New="Bago" +Duplicate="Katulad" +Enable="Paganahin" +DisableOSXVSync="Huwag Paganahin OSX V-Sync" +ResetOSXVSyncOnExit="I-reset ang OSX V-Sync sa Labasan" +HighResourceUsage="Ang Encoding ay labis ang karga! Isaalang alang ang pagbaba ng video settings o gumamit ng mas mabilis na encoding preset." +Transition="Paglipat" +QuickTransitions="Mabilis na Paglipat" +Left="Kaliwa" +Right="Kanan" +Top="Pinakamataas" +Bottom="Kailaliman" +Reset="Baguhin" +Hours="Oras" +Minutes="Minuto" +Seconds="Segundo" +Deprecated="Hindi na ginagamit" +ReplayBuffer="Replay Buffer" +Import="Mag-angkat" +Export="I-export" +Copy="Kopyahin" +Paste="I-paste" +PasteReference="I-paste (Banggit)" +PasteDuplicate="I-paste (Pangalawang salin)" +RemuxRecordings="Mga Pagtatala ng Remux" +Next="Susunod" +Back="Bumalik" +Defaults="Mga hindi pagsipot" +HideMixer="Itago sa panghalo" +TransitionOverride="Override na ang Paglipat" +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." +AlreadyRunning.LaunchAnyway="Maglunsad parin" + +Copy.Filters="Kopyahin ang mga panala" +Paste.Filters="I-paste ang mga panala" + +BandwidthTest.Region="Rehiyon" +BandwidthTest.Region.US="Estados Unidos" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Asya" +BandwidthTest.Region.Other="Iba pa" + +Basic.FirstStartup.RunWizard="Gusto mo bang mapatakbo ang dalubhasa sa kusang pagkonpigurasyon? Maaari mo ring mano-manuhin ang pagkonpigura ng iyong settings sa pagpindot ng Settings button sa pangunahing window." +Basic.FirstStartup.RunWizard.BetaWarning="(Tandaan: Ang dalubhasang kusang pagkonpigurasyon ay kasalukuyang nasa beta)" +Basic.FirstStartup.RunWizard.NoClicked="Kung magbabago ka ng isip mo, pwede mong patakbuhin ang dalubhasang kusang konpigurasyon anomang oras ulit mula sa mga kasangkapan sa menu." + +Basic.AutoConfig="Dalubhasang Kusang Konpigurasyon" +Basic.AutoConfig.Beta="Dalubhasang Kusang Konpigurasyon (Beta)" +Basic.AutoConfig.ApplySettings="Ilapat ang mga Pagtatakda" +Basic.AutoConfig.StartPage="Paggamit ng Impormasyon" +Basic.AutoConfig.StartPage.SubTitle="Tukuyin ang tamang program na gusto mong gamitin" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimize para sa streaming, pangalawa ay ang recording" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimize para sa recording, Hindi ako mag streaming" +Basic.AutoConfig.VideoPage="Ang mga video settings" +Basic.AutoConfig.VideoPage.SubTitle="Tukuyin ang naayong video settings na gusto mong gamitin" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Kasulukuyang gamitin(%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="I-Display %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Pangsulukuyang Gamitin (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Sa 60 or kaya sa 30, Pero mas mabuti 60 kung maaari" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Sa 60 or kaya sa 30, Pero mas mabuti 60 para sa mas magandang resolusyon" +Basic.AutoConfig.VideoPage.CanvasExplanation="Paalala: Ang kanbas (base) na ito ay hindi kinakailangan na kaparehas ng resolusyon ng iyong stream or record. Ang iyong actual stream/record na resolusyon ay maaaring pagkasyahin para sa resolusyon ng kanbas para mabawasan ang paggamit kinakailangan na bitrate." +Basic.AutoConfig.StreamPage="Mga batis ng impormasyon" +Basic.AutoConfig.StreamPage.SubTitle="Pakiusap ilagay ang iyong impormasyon pang stream" +Basic.AutoConfig.StreamPage.Service="Serbisyo" +Basic.AutoConfig.StreamPage.Service.ShowAll="Ipakita lahat..." +Basic.AutoConfig.StreamPage.Server="Serber" +Basic.AutoConfig.StreamPage.StreamKey="Ang susi ng iyong stream" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="I-estima ang bitrate kasama ang pag eksamina ng bandwidth (maaaring tumagal ng ilang minuto)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Piliin ang hardware encoding" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Ang Hardware Encoding ay tinatanggal lahat ng nagamit na CPU, pero kailangan ng mas maraming bitrate para makuha ang parehong lebel ng kalidad" +Basic.AutoConfig.StreamPage.StreamWarning.Title="Babala sa pag stream" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Ang bandwidth test ay tungkol sa stream randomized bidyo data at walang audio sa iyong channel. Kung maaari, mas inirerekomenda na pansamantalang i-off ang pag save ng vides of streams at itakda sa pribado hanggang matapos makumpleto ang pag eksamin" +Basic.AutoConfig.TestPage="Huling Resulta" +Basic.AutoConfig.TestPage.SubTitle.Testing="Ang program na ito ay isinasagawa ang mga set para eksamin para matantiya ang pinakamainam na settings" +Basic.AutoConfig.TestPage.SubTitle.Complete="Ang iyong pagsusuri ay kumpleto na" +Basic.AutoConfig.TestPage.TestingBandwidth="Pagsasagawa ng bandwidth test, ito ay maaaring tumagal ng ilang minuto..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Kumukunekta sa: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Bigong kumunekta sa alin mang server, paki tignan ang koneksyon ng iyong internet at subukan ulet." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Pagsusuri ng bandwidth para sa: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Testingin ang stream encoder, ito ay maaaring tumagal ng isang minuto..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testingin ang recording encoder, ito ay maaaring tumagal ng isang minuto..." +Basic.AutoConfig.TestPage.TestingRes="Testingin ang resolusyon, ito ay maaaring tumagal ng ilang minuto..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Bigong iandar ang encoder" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Testingin %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streaming Encoder" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Recording Encoder" +Basic.AutoConfig.TestPage.Result.Header="Ang program na ito ay napagkaisahan na ang settings tinatayang lahat ay perpekto para sayo:" +Basic.AutoConfig.TestPage.Result.Footer="Para sa pag gamit ng settings, pindutin ang Apply Settings. Para ma reconfigure ang wizard at simulan muli, pindutin ang Back. Mano-mano i-configure ang settings, at pindutin ang Cancel at buksan ang Settings." + +Basic.Stats="Ang mga Statisktika" +Basic.Stats.CPUUsage="Ang nagamit na CPU" +Basic.Stats.HDDSpaceAvailable="Ang magagamit na espasyo ng HDD" +Basic.Stats.MemoryUsage="Ang nagamit na Memory" +Basic.Stats.AverageTimeToRender="Ang average time para ma render ang frame" +Basic.Stats.SkippedFrames="Laktawin ang frames dahil sa encoding lag" +Basic.Stats.MissedFrames="Nalampasan ang frames dahil sa rendering lag" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Recording" +Basic.Stats.Status="Ang estado" +Basic.Stats.Status.Recording="Recording" +Basic.Stats.Status.Live="Naka LIVE" +Basic.Stats.Status.Reconnecting="Muling kumukonekta" +Basic.Stats.Status.Inactive="Hindi na aktiba" +Basic.Stats.DroppedFrames="Naihulog na Frames (Network)" +Basic.Stats.MegabytesSent="Ang total na Data Output" +Basic.Stats.Bitrate="Bitrate" + +Updater.Title="May bagong update na available" +Updater.Text="May bagong update na magagamit:" +Updater.UpdateNow="Mag update ngayon" +Updater.RemindMeLater="Paalalahanan mo ako mamaya" +Updater.Skip="Laktawan ang Bersyon" +Updater.Running.Title="Programa na kasalukuyang aktibo" +Updater.Running.Text="Ang mga output ay kasalukuyang aktibo, mangyaring i-shut down ang anumang mga aktibong output bago sinusubukang i-update" +Updater.NoUpdatesAvailable.Title="Walang magagamit na mga update" +Updater.NoUpdatesAvailable.Text="Walang mga update ang kasalukuyang magagamit" +Updater.FailedToLaunch="Nabigong ilunsad ang updater" +Updater.GameCaptureActive.Title="Kumuha ng laro na aktibo" +Updater.GameCaptureActive.Text="Kasalukuyang nakukuha sa library ang pagkuha ng hook library. Mangyaring isara ang anumang mga laro / programa na nakukuha (o i-restart ang mga bintana) at subukang muli." + +QuickTransitions.SwapScenes="Swap Preview / Output Scenes Pagkatapos Transitioning" +QuickTransitions.SwapScenesTT="Mag swap ng mga preview at output scenes matapos ang transitioning (Kung may orihinal na output scene na umiiral).\nIto ay hindi pwede baguhin ang orihinal na eksena." +QuickTransitions.DuplicateScene="Gayahin ang eksena" +QuickTransitions.DuplicateSceneTT="Kung mag i-edit ng parehas na eksena. pinapayag ang editing transform/visibility of sources kahit baguhin ang output.\nPara ma edit ang properties wag baguhin ang output, paganahin 'Duplicate Sources'.\nAng pagbago ng kalidad nito ay maaaring ma reset ang eksena (kung ito ay umiiral pa rin)." +QuickTransitions.EditProperties="Gayahin ang mga pinagmulang" +QuickTransitions.EditPropertiesTT="Kung mag i-edit ng kaparehas na eksena. payagan mag edit ng katangian ng mga pinagkukunan nang hindi binabago ang output.\nIto ay magagamit king 'Duplicate Scene' ay pinagana.\nAng mga pinagkukunan(gaya ng nakuhang media sources) hindi suportado at di pwede ma edit nang hiwalay.\nAng pagbago ng value nito ay maaaring ma reset ang kasulukuyang output scene(kung mayroon pa).\n\nBabala: Dahil sa pinagkukunan ay magiging doble, ito ay nangangailangan ng ekstrang sistema or video pagkukunan." +QuickTransitions.HotkeyName="Ilipat ng mabilis: %1" + +Basic.AddTransition="Magdagdag ng configurable na transisyon" +Basic.RemoveTransition="Tangalin ang configurable transition" +Basic.TransitionProperties="Mga Properties ng Transisyon" +Basic.SceneTransitions="Mga transisyon ng mga eksena" +Basic.TransitionDuration="Katagalan" +Basic.TogglePreviewProgramMode="Ang Studio Mode" + +TransitionNameDlg.Text="Pakilagay ang pangalan ng transisyon" +TransitionNameDlg.Title="Pangalan ng Transisyon" + +TitleBar.Profile="Ang Profile" +TitleBar.Scenes="Ang mga Eksena" + +NameExists.Title="Ang pangalan ay umiiral na" +NameExists.Text="Ang pangalan ay nagamit na." + +NoNameEntered.Title="Pakilagay ang balidong pangalan" +NoNameEntered.Text="Hindi pwede gumamit ng walang pangalan." + +ConfirmStart.Title="Magsimula ng mag Stream?" +ConfirmStart.Text="Sigurado ka ba na simulang ang pag stream?" + +ConfirmStop.Title="Itigil ba ang Steam?" +ConfirmStop.Text="Sigurado ka itigil ang pag i-stream?" + +ConfirmExit.Title="Lumabas sa OBS?" +ConfirmExit.Text="Ang OBS ay kasulukuyang aktibo. Lahat ng streams/recordings ay magsasara. Sigurado ka ba gusto mong mag exit?" + +ConfirmRemove.Title="I-kumpirma ang pagtangal" +ConfirmRemove.Text="Sigurado ka bang tangalin ang '$1'?" +ConfirmRemove.TextMultiple="Sigurado ka bang tangalin %1 items?" + +Output.StartStreamFailed="Bigong simulang ang pag stream" +Output.StartRecordingFailed="Bigong simulan ang pag record" +Output.StartReplayFailed="Bigong simulang ang replay buffer" +Output.StartFailedGeneric="Bigong simulang ang output. Pakitingnan ang talaan ng mga detalye.\n\nNote: kung ikaw ay gumagamit ng NVENC or AMD encoders, siguraduhin na nag video drivers ay naka update." + +Output.ConnectFail.Title="Bigung kumunekta" +Output.ConnectFail.BadPath="Hindi wasto ang Path or ang Connection URL. Pakitingnan ang settings para ma kumpirma na ito ay pwede." +Output.ConnectFail.ConnectFailed="Bigong kumunekta sa serber" +Output.ConnectFail.InvalidStream="Di maka pasok sa tinutukoy na channel or stream key, pakitignan ng maayos ang stream key. Kung tama, Maaaring may problema sa pagkunekta sa serber." +Output.ConnectFail.Error="Isang di-inaasahang error ng subukang kumunekta sa serber. Karagdagang impormasyon ay nasa log file." +Output.ConnectFail.Disconnected="Nadiskonek mula sa serber." + +Output.RecordFail.Title="Bigong simulang ang pag record" +Output.RecordFail.Unsupported="Ang output format ay maaring di suportado or di sinusuportahan ang higit sa isang audio track. Pakitingnan ang iyong settings at simulan ulet." +Output.RecordNoSpace.Title="Hindi sapat ang iyong espasyo" +Output.RecordNoSpace.Msg="Di sapat ang espasyo para ipatuloy ang pagrerekord." +Output.RecordError.Title="May error sa pagrekord" +Output.RecordError.Msg="Hindi tiyak na error habang nagrerekord." +Output.ReplayBuffer.NoHotkey.Title="Walang set ng hotkey!" +Output.ReplayBuffer.NoHotkey.Msg="Walang na i-save na hotkey para sa replay buffer. Paki \"Save\" ang gagamiting hotkey para ma i-save ang replay recordings." + +Output.BadPath.Title="Di mabuting File Path" +Output.BadPath.Text="Ang na configured na file output path ay di di-wasto. Pakitignan ang iyong settings para ma kumpirma na balido ang file path at na i-set ito." + +LogReturnDialog="Ang na i-uload na log ay tagumpay" +LogReturnDialog.CopyURL="Kupyahin ang URL" +LogReturnDialog.ErrorUploadingLog="Error sa pag-upload ng log file" + +LicenseAgreement="Ang lisensya ng kasunduan" +LicenseAgreement.PleaseReview="Pakitingnan ang lisensya bago gumamit ng OBS. Sa pag gamit ng program, kinikilala mo na nabasa at sumasang-ayon ka sa mga tuntunin nito GNU General Public License v2.0. Maaaring mag scroll down para makita ang ibang bahagi ng kasunduan." +LicenseAgreement.ClickIAgreeToContinue="Kung iyong tinatanggap ang termino ng kasunuduan, pindutin ang I Agree to continue. Dapat sumang-ayon sa kasunduan sa pag-gamit ng OBS." +LicenseAgreement.IAgree="Sumasang-ayon Ako" +LicenseAgreement.Exit="Lumabas" + +Remux.SourceFile="Obs Recording" +Remux.TargetFile="Target File" +Remux.Remux="Remux" +Remux.OBSRecording="OBS Recording" +Remux.FinishedTitle="Tapos na ang Remuxing" +Remux.Finished="Ang Recording remuxed" +Remux.FinishedError="Ang Recording Remuxed, pero ang file ay hindi kumpleto" +Remux.SelectRecording="Pumuli ng OBS Recording…" +Remux.SelectTarget="Piliin ang gustong file …" +Remux.FileExistsTitle="Ang napiling file any umiiral na" +Remux.FileExists="Ang napiling file any umiiral na, gusto mo ba itong palitan?" +Remux.ExitUnfinishedTitle="Ang remuxing ay naka progress" +Remux.ExitUnfinished="Di pa tapos ang Remuxing, pag itigil ang render ang napiling file ay di magagamit.\nGusto mo bang huminto sa pag remuxing?" + +UpdateAvailable="May bagong update na available" +UpdateAvailable.Text="Ang Version %1.%2.%3 ay available na. Pindutin para i-download" + +Basic.DesktopDevice1="Ang Desktop Audio" +Basic.DesktopDevice2="Ang Desktop Audio 2" +Basic.AuxDevice1="Ang Mic/Aux" +Basic.AuxDevice2="Ang Mic/Aux 2" +Basic.AuxDevice3="Ang Mic/Aux 3" +Basic.AuxDevice4="Ang Mic/Aux 4" + +Basic.Scene="Eksena" +Basic.DisplayCapture="Ang nakunan na display" + +Basic.Main.PreviewConextMenu.Enable="Ipakita muli ang Larawan" + +ScaleFiltering="I-filter iskala" +ScaleFiltering.Point="Punto" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" + +Deinterlacing="Deinterlacing" +Deinterlacing.Discard="Baliwalain" +Deinterlacing.Retro="Retro" +Deinterlacing.Blend="I-timpla" +Deinterlacing.Blend2x="I-timpla ng dalwang beses" +Deinterlacing.Linear="Linear" +Deinterlacing.Linear2x="Linear 2x" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Yadif 2x" +Deinterlacing.TopFieldFirst="Pang unang itaas na field" +Deinterlacing.BottomFieldFirst="Pang unang ibaba na field" + +VolControl.SliderUnmuted="Pandausdos ng volume para '%1': %2" +VolControl.SliderMuted="Pandausdos ng volume para '%1': %2 (kasulukuyang naka mute)" +VolControl.Mute="Mute '%1'" +VolControl.Properties="Mga katangian '%1'" + +Basic.Main.AddSceneDlg.Title="Magdagdag ng mga eksena" +Basic.Main.AddSceneDlg.Text="Pakiusap lagyan ng pangalan ang eksena" + +Basic.Main.DefaultSceneName.Text="Eksena %1" + +Basic.Main.AddSceneCollection.Title="Magdagdag ng Collection ng Eksena" +Basic.Main.AddSceneCollection.Text="Mangyaring ipasok ang pangalan ng koleksyon ng eksena" + +Basic.Main.RenameSceneCollection.Title="I-rename ang Scene Collection" + +AddProfile.Title="Magdagdag ng Profile" +AddProfile.Text="Pakipasok ang pangalan ng profile" + +RenameProfile.Title="Palitan ang pangalan ng Profile" + +Basic.Main.MixerRename.Title="Palitan ang pangalan ng Audio Source" +Basic.Main.MixerRename.Text="Mangyaring ipasok ang pangalan ng pinagmulang audio" + + +Basic.Main.PreviewDisabled="Kasalukuyang hindi pinagana ang pag-preview" + +Basic.SourceSelect="Lumikha / Piliin ang Pinagmulan" +Basic.SourceSelect.CreateNew="Gumawa ng bago" +Basic.SourceSelect.AddExisting="Magdagdag ng Umiiral na" +Basic.SourceSelect.AddVisible="Gawing nakikita ang mapagkukunan" + +Basic.PropertiesWindow="Mga Properties para sa '%1'" +Basic.PropertiesWindow.SelectColor="Pumili ng kulay" +Basic.PropertiesWindow.SelectFont="Piliin ang font" +Basic.PropertiesWindow.ConfirmTitle="Binago ang Mga Setting" +Basic.PropertiesWindow.Confirm="Mayroong mga hindi nai-save na pagbabago. Gusto mo bang panatilihin ang mga ito?" +Basic.PropertiesWindow.NoProperties="Walang magagamit na mga ari-arian" +Basic.PropertiesWindow.AddFiles="Magdagdag ng Mga File" +Basic.PropertiesWindow.AddDir="Magdagdag ng Direktoryo" +Basic.PropertiesWindow.AddURL="Magdagdag ng Path / URL" +Basic.PropertiesWindow.AddEditableListDir="Magdagdag ng direktoryo sa '%1'" +Basic.PropertiesWindow.AddEditableListFiles="Magdagdag ng mga file sa '%1'" +Basic.PropertiesWindow.AddEditableListEntry="Magdagdag ng entry sa '%1'" +Basic.PropertiesWindow.EditEditableListEntry="I-edit ang entry mula sa '%1'" + +Basic.PropertiesView.FPS.Simple="Mga Simpleng FPS na Halaga" +Basic.PropertiesView.FPS.Rational="Mga Rational Value FPS" +Basic.PropertiesView.FPS.ValidFPSRanges="Mga saklaw na wastong FPS:" + +Basic.InteractionWindow="Nakikisalamuha... '%1'" + +Basic.StatusBar.Reconnecting="Nadiskonek, muling kumukonekta sa loob ng %2 segundo(s) (pagtatangka %1)" +Basic.StatusBar.AttemptingReconnect="Sinusubukang kumunekta... (tangka %1)" +Basic.StatusBar.ReconnectSuccessful="Matagumpay na reconnection" +Basic.StatusBar.Delay="Pag antala (%1 segundo)" +Basic.StatusBar.DelayStartingIn="Na antala(magsisimula %1 sec)" +Basic.StatusBar.DelayStoppingIn="Na antala(hihinto %1 sec)" +Basic.StatusBar.DelayStartingStoppingIn="Na antala (hihinto %1 sec, sisimula %2 sec)" + +Basic.Filters="Mga Filter" +Basic.Filters.AsyncFilters="Mga Filter ng Audio / Video" +Basic.Filters.AudioFilters="Mga Filter ng Audio" +Basic.Filters.EffectFilters="Mga Filter ng Epekto" +Basic.Filters.Title="Filter para sa mga '%1'" +Basic.Filters.AddFilter.Title="Salain ang pangalan" +Basic.Filters.AddFilter.Text="Mangyaring tukuyin ang pangalan ng filter" + +Basic.TransformWindow="Pagbabago ng Eksena ng Eksena" +Basic.TransformWindow.Position="Posisyon" +Basic.TransformWindow.Rotation="Pag-ikot" +Basic.TransformWindow.Size="Sukat" +Basic.TransformWindow.Alignment="Positional Alignment" +Basic.TransformWindow.BoundsType="Bounding ng klase ng kahon" +Basic.TransformWindow.BoundsAlignment="Ang pagkahanay ng Bounding Kahon" +Basic.TransformWindow.Bounds="Ang Bounding Box Size" +Basic.TransformWindow.Crop="I-crop ito" + +Basic.TransformWindow.Alignment.TopLeft="Sa taas na kaliwa" +Basic.TransformWindow.Alignment.TopCenter="Sa gintang ibabaw" +Basic.TransformWindow.Alignment.TopRight="Sa taas ng kanan" +Basic.TransformWindow.Alignment.CenterLeft="Naiwan ang Gitna" +Basic.TransformWindow.Alignment.Center="Gitna" +Basic.TransformWindow.Alignment.CenterRight="Tama sa Gitna" +Basic.TransformWindow.Alignment.BottomLeft="Babang Kaliwa" +Basic.TransformWindow.Alignment.BottomCenter="Babang Gitna" +Basic.TransformWindow.Alignment.BottomRight="Baba sa Kanan" + +Basic.TransformWindow.BoundsType.None="Walang hangganan" +Basic.TransformWindow.BoundsType.MaxOnly="Pinakamalaki na sukat lamang" +Basic.TransformWindow.BoundsType.ScaleInner="Sukat sa panloob na hangganan" +Basic.TransformWindow.BoundsType.ScaleOuter="Sukat sa panlabas na hangganan" +Basic.TransformWindow.BoundsType.ScaleToWidth="Sukat sa lapad ng hangganan" +Basic.TransformWindow.BoundsType.ScaleToHeight="Sukat sa taas ng hangganan" +Basic.TransformWindow.BoundsType.Stretch="Mag-stretch sa hangganan" + +Basic.Main.AddSourceHelp.Title="Hindi maaring magdagdag ng pinagmulan" +Basic.Main.AddSourceHelp.Text="Kailangan mong magkaroon ng hindi bababa sa isang eksena sa pinagmulan." + +Basic.Main.Scenes="Mga eksena" +Basic.Main.Sources="Pinagmulan" +Basic.Main.Controls="Mga kontrol" +Basic.Main.Connecting="Kumukonekta..." +Basic.Main.StartRecording="Simula ng Pagtatala" +Basic.Main.StartReplayBuffer="Simulan na ang pag Replay Buffer" +Basic.Main.StartStreaming="Simulan ang mag stream" +Basic.Main.StopRecording="Itigil ang Pagtatala" +Basic.Main.StoppingRecording="Pagtigil sa Pagtatala..." +Basic.Main.StopReplayBuffer="Itigil ang pag Re-replay Buffer" +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" +Basic.MainMenu.File.Import="&Angkat" +Basic.MainMenu.File.ShowRecordings="Ipakita at Pag-&record" +Basic.MainMenu.File.Remux="Re&mux Recordings" +Basic.MainMenu.File.Settings="Mga &Setting" +Basic.MainMenu.File.ShowSettingsFolder="Ipakita ang Folder ng Mga Setting" +Basic.MainMenu.File.ShowProfileFolder="Ipakita ang Folder ng Profile" +Basic.MainMenu.AlwaysOnTop="L&aging Nasa Tuktok" +Basic.MainMenu.File.Exit="E&xit" + +Basic.MainMenu.Edit="I-&edit" +Basic.MainMenu.Edit.Undo="&Pawalang-bisa" +Basic.MainMenu.Edit.Redo="&Mag-redo" +Basic.MainMenu.Edit.UndoAction="I-&undo ang $1" +Basic.MainMenu.Edit.RedoAction="&Redo $1" +Basic.MainMenu.Edit.LockPreview="&I-preview ang I-preview" +Basic.MainMenu.Edit.Scale="Preview &Scaling" +Basic.MainMenu.Edit.Scale.Window="Scale to Window" +Basic.MainMenu.Edit.Scale.Canvas="Kanbas (%1x%2)" +Basic.MainMenu.Edit.Scale.Output="Output (%1x%2)" +Basic.MainMenu.Edit.Transform="&Transform" +Basic.MainMenu.Edit.Transform.EditTransform="&Baguhin ang Transform..." +Basic.MainMenu.Edit.Transform.CopyTransform="Kopyahin ang Transform" +Basic.MainMenu.Edit.Transform.PasteTransform="I-paste ang Transform" +Basic.MainMenu.Edit.Transform.ResetTransform="I-&reset ang Transform" +Basic.MainMenu.Edit.Transform.Rotate90CW="I-rotate ang 90 degrees CW" +Basic.MainMenu.Edit.Transform.Rotate90CCW="I-rotate ang 90 degrees CCW" +Basic.MainMenu.Edit.Transform.Rotate180="I-rotate ang 180 degrees" +Basic.MainMenu.Edit.Transform.FlipHorizontal="Flip &Horizontal" +Basic.MainMenu.Edit.Transform.FlipVertical="Flip &Vertical" +Basic.MainMenu.Edit.Transform.FitToScreen="&Fit sa screen" +Basic.MainMenu.Edit.Transform.StretchToScreen="Mag-&stretch sa screen" +Basic.MainMenu.Edit.Transform.CenterToScreen="&Center sa screen" +Basic.MainMenu.Edit.Order="&Order" +Basic.MainMenu.Edit.Order.MoveUp="Ilipat at Pataas (&U)" +Basic.MainMenu.Edit.Order.MoveDown="Ibaba or bumaba (&D)" +Basic.MainMenu.Edit.Order.MoveToTop="Ilipat sa &Tuktok" +Basic.MainMenu.Edit.Order.MoveToBottom="Ilipat sa Ika (&B)" +Basic.MainMenu.Edit.AdvAudio="&Advanced Audio Properties" + +Basic.MainMenu.View="&View" +Basic.MainMenu.View.Toolbars="Mga &Toolbar" +Basic.MainMenu.View.Docks="Docks" +Basic.MainMenu.View.Docks.ResetUI="I-reset ang UI" +Basic.MainMenu.View.Docks.LockUI="I-lock ang UI" +Basic.MainMenu.View.Toolbars.Listboxes="Mga &Listbox" +Basic.MainMenu.View.SceneTransitions="S&cene Mga Paglilipat" +Basic.MainMenu.View.StatusBar="&Status bar" +Basic.MainMenu.View.Fullscreen.Interface="Fullscreen Interface" + +Basic.MainMenu.SceneCollection="Kolek&syon ng Eksena" +Basic.MainMenu.Profile="&Profile" +Basic.MainMenu.Profile.Import="Mag-import ng Profile" +Basic.MainMenu.Profile.Export="I-export ang Profile" +Basic.MainMenu.SceneCollection.Import="Pag Angkat ng Nakolektang eksena" +Basic.MainMenu.SceneCollection.Export="Magluwas ng Nakolektang Eksena" +Basic.MainMenu.Profile.Exists="Ang Profile ay umiiral na ngayon" +Basic.MainMenu.SceneCollection.Exists="Ang Nakolektang Eksena ay umiiral na ngayon" + +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" +Basic.MainMenu.Help.Logs.UploadLastLog="Mag-upload &Huling pagla-log ng File" +Basic.MainMenu.Help.Logs.ViewCurrentLog="&Tignan ang Kasalukuyang pagla-log" +Basic.MainMenu.Help.CheckForUpdates="Magsiyasat para sa mga update" + +Basic.Settings.ProgramRestart="Ang mga programa ay dapat na-restart para sa mga maaapektuhan na setting." +Basic.Settings.ConfirmTitle="Konpirmahin ang mga pagbabago" +Basic.Settings.Confirm="Hindi mo nai-save ang mga pagbabago. Gusto mo bang i-save ang mga pagbabago?" + +Basic.Settings.General="Pangkalahatan" +Basic.Settings.General.Theme="Tema" +Basic.Settings.General.Language="Lenguwahe" +Basic.Settings.General.EnableAutoUpdates="Awtomatikong pagsusuri para sa mga update tungkol sa startup" +Basic.Settings.General.OpenStatsOnStartup="Magbukas ng palitang-usap ng awtomatikongng stats tungkol sa startup" +Basic.Settings.General.WarnBeforeStartingStream="Ipakita ang konpirmasyon ng palitang-usap nang magsimula ang mga stream" +Basic.Settings.General.WarnBeforeStoppingStream="Ipakita ang konpirmasyon ng palitang-usap nang ihinto ang mga stream" +Basic.Settings.General.Projectors="Mga prodyektor" +Basic.Settings.General.HideProjectorCursor="Itago ang kursor sa kabila ng mga prodyektor" +Basic.Settings.General.ProjectorAlwaysOnTop="Gumawa ng mga prodyektor na laging nakakataas" +Basic.Settings.General.Snapping="Pinagmulan ng Paghahanay na isnaping" +Basic.Settings.General.ScreenSnapping="Paglagot sa mga Pinagmulan ukol sa gilid ng iskrin" +Basic.Settings.General.CenterSnapping="Paglagot sa mga Pinagmulan ukol sa pahalang at patayong sentro" +Basic.Settings.General.SourceSnapping="Paglagot sa mga Pinagmulan ukol sa iba pang mga pinagmulan" +Basic.Settings.General.SnapDistance="Pagkamadamdam na Paglagot" +Basic.Settings.General.RecordWhenStreaming="awtomatikong pagtala nang anod" +Basic.Settings.General.KeepRecordingWhenStreamStops="Panatilihing ang pagtatala kahit tumigil ang stream" +Basic.Settings.General.ReplayBufferWhileStreaming="Awtomatikong pagsisimula ng replay buffer kapag streaming" +Basic.Settings.General.KeepReplayBufferStreamStops="Panatilihing aktibo ang replay buffer kahit tumigil ang stream" +Basic.Settings.General.SysTray="Bandehadong Sistema" +Basic.Settings.General.SysTrayWhenStarted="Magbawas sa bandehadong sistema kapag nagsimula na" +Basic.Settings.General.SystemTrayHideMinimize="Palaging magbawas sa bandehadong sistema sa halip na task bar" +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.Stream="Stream" +Basic.Settings.Stream.StreamType="Mga uri ng Stream" + +Basic.Settings.Output="Ang awput" +Basic.Settings.Output.Format="Pagtatala ng recording" +Basic.Settings.Output.Encoder="Encoder" +Basic.Settings.Output.SelectDirectory="Piliin ang direktoryong pagtatala" +Basic.Settings.Output.SelectFile="Piliin ang file ng pagtatala" +Basic.Settings.Output.EnforceBitrate="Ipatupad ang serbisyo ng limitadong bitrate" +Basic.Settings.Output.Mode="Awput Mode" +Basic.Settings.Output.Mode.Simple="Simple" +Basic.Settings.Output.Mode.Adv="Pagsulong" +Basic.Settings.Output.Mode.FFmpeg="FFmpeg Awput" +Basic.Settings.Output.UseReplayBuffer="Simulan ang pag replay ng buffer" +Basic.Settings.Output.ReplayBuffer.SecondsMax="Pinakamataas na oras ng replay (segundos)" +Basic.Settings.Output.ReplayBuffer.MegabytesMax="Pinakamataas na memorya (Megabytes)" +Basic.Settings.Output.ReplayBuffer.Estimate="Ang na estimang nagamit na memorya: %1 MB" +Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Hindi ma-estima ang nagamit na memorya. Pakilagay ng pinakamataas na limitasyon ng memorya." +Basic.Settings.Output.ReplayBuffer.HotkeyMessage="(Nota: Siguraduhin na naka takda ang hotkey para sa pag replay ng buffer sa bahaging hotkeys)" +Basic.Settings.Output.ReplayBuffer.Prefix="Ang panlapi ng Replay Buffer Filename" +Basic.Settings.Output.ReplayBuffer.Suffix="Suffix" +Basic.Settings.Output.Simple.SavePath="Recording Path" +Basic.Settings.Output.Simple.RecordingQuality="Pagrekord ng Kalidad" +Basic.Settings.Output.Simple.RecordingQuality.Stream="Parehong stream" +Basic.Settings.Output.Simple.RecordingQuality.Small="Mataas na Kalidad, Katamtamang Laki ng File" +Basic.Settings.Output.Simple.RecordingQuality.HQ="Indistinguishable Quality, Large File Size" +Basic.Settings.Output.Simple.RecordingQuality.Lossless="Lossless Quality, Napakalaki ng Laki ng File" +Basic.Settings.Output.Simple.Warn.VideoBitrate="Babala: Ang bitrate ng streaming video ay itatakda sa%1, na kung saan ay ang itaas na limitasyon para sa kasalukuyang streaming service. Kung sigurado ka na gusto mong pumunta sa itaas%1, paganahin ang mga advanced na mga pagpipilian sa encoder at alisan ng tsek ang \"Ipatupad ang mga limitasyong bitrate ng service streaming\"." +Basic.Settings.Output.Simple.Warn.AudioBitrate="Babala: Itatakda ang streaming audio bitrate, na kung saan ay ang itaas na limitasyon para sa kasalukuyang streaming service. Kung sigurado ka na gusto mong pumunta sa itaas, paganahin ang mga advanced na pagpipilian ng encoder at alisan ng tsek ang \"Ipataw ang mga limitasyong bitrate ng service streaming\"." +Basic.Settings.Output.Simple.Warn.Encoder="Babala: Ang pagrekord sa isang encoder ng software sa ibang kalidad kaysa sa stream ay mangangailangan ng dagdag na paggamit ng CPU kung mag-stream at mag-record ka sa parehong oras." +Basic.Settings.Output.Simple.Warn.Lossless="Babala: Hindi mawawalan ng kalidad ang bumubuo ng napakalaking malalaking sukat ng file! Maaaring gumamit ng walang humpay na kalidad ng hanggang 7 gigabytes ng puwang ng disk kada minuto sa mataas na resolution at framerates. Ang hindi nawawala ay hindi inirerekomenda para sa mahabang pag-record maliban kung mayroon kang isang napakalaking halaga ng disk space na magagamit." +Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sigurado ka bang gusto mong gumamit ng kalidad na walang pagkawala?" +Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless quality warning!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="Babala: Hindi ka maaaring gumamit ng maraming magkahiwalay na mga encoder ng QSV kapag nag-stream at nagre-record nang sabay. Kung nais mong i-stream at i-record nang sabay-sabay, mangyaring baguhin ang alinman sa encoder ng pag-record o 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)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" +Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 mababang preset ng paggamit ng CPU, nagpapataas ng laki ng file)" +Basic.Settings.Output.VideoBitrate="Bitrate ng Video" +Basic.Settings.Output.AudioBitrate="Bitrate ng Audio" +Basic.Settings.Output.Reconnect="Awtomatikong mag-reconnect" +Basic.Settings.Output.RetryDelay="Retry Delay (segundo)" +Basic.Settings.Output.MaxRetries="Pinakamataas na Retries" +Basic.Settings.Output.Advanced="Paganahin ang Mga Setting ng Advanced Encoder" +Basic.Settings.Output.EncoderPreset="Encoder Preset (mas mataas = mas CPU)" +Basic.Settings.Output.CustomEncoderSettings="Mga Setting ng Custom Encoder" +Basic.Settings.Output.CustomMuxerSettings="Mga Setting ng Custom Muxer" +Basic.Settings.Output.NoSpaceFileName="Bumuo ng Pangalan ng File nang walang Space" + +Basic.Settings.Output.Adv.Rescale="Rescale Output" +Basic.Settings.Output.Adv.AudioTrack="Audio Track" +Basic.Settings.Output.Adv.Streaming="Streaming" +Basic.Settings.Output.Adv.ApplyServiceSettings="Ipatupad ang mga setting ng encoder ng streaming ng serbisyo" +Basic.Settings.Output.Adv.Audio.Track1="Subaybayan ang 1" +Basic.Settings.Output.Adv.Audio.Track2="Subaybayan ang 2" +Basic.Settings.Output.Adv.Audio.Track3="Subaybayan ang 3" +Basic.Settings.Output.Adv.Audio.Track4="Subaybayan ang 4" +Basic.Settings.Output.Adv.Audio.Track5="Subaybayan ang 5" +Basic.Settings.Output.Adv.Audio.Track6="Subaybayan ang 6" + +Basic.Settings.Output.Adv.Recording="Pagre-record" +Basic.Settings.Output.Adv.Recording.Type="Uri" +Basic.Settings.Output.Adv.Recording.Type.Standard="Standard" +Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Custom Output (FFmpeg)" +Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Gamitin ang stream encoder)" +Basic.Settings.Output.Adv.Recording.Filename="Pag-format ng Filename" +Basic.Settings.Output.Adv.Recording.OverwriteIfExists="I-overwrite kung umiiral ang file" +Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg Output Type" +Basic.Settings.Output.Adv.FFmpeg.Type.URL="Output sa URL" +Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Output to File" +Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="Mga format ng karaniwang recording" +Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Lahat ng Mga File" +Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Landas ng file or ang URL" +Basic.Settings.Output.Adv.FFmpeg.Format="Lalagyang ng Format" +Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Tunog" +Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Bidyo" +Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Default Format" +Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Ang lagayan ng deskripsyon ng format" +Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Tunog/Bidyo Codec guessed mula sa landas ng File or URL" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Default Encoder" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Huwag paganahin ang Encoder" +Basic.Settings.Output.Adv.FFmpeg.VEncoder="Bidyo Encoder" +Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Ang settings ng Bidyo Encoder(kung mayroon)" +Basic.Settings.Output.Adv.FFmpeg.AEncoder="Tonog ng Encoder" +Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Ang Settings ng Tonog Encoder(kung mayroon)" +Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Ang mga Settings ng Muxer (kung mayroon)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Ang pagitan ng Keyframe (frames)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Ipakita lahat ng codecs (kahit itoy posibleng di-kompatibol)" + +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 Taon, apat na numero\n%YY Taon, huling dalawang numero (00-99)\n%MM Buwan bilang decimal na numero (01-12)\n%DD Araw ng buwan, zero-padded (01-31)\n%hh Oras sa 24h format (00-23)\n%mm Minuto (00-59)\n%ss Segundo (00-61)\n%% A % tanda\n%a Paikliin ang pangalan at araw ng trabaho\n%A Buong araw ng trabaho ng pangalan\n%b Paikliin ang pangalan at Buwan ng trabaho\n%B Buong pangalan ng buwan\n%d Araw ng Buwan, zero-padded (01-31)\n%H Oras sa 24h format (00-23)\n%I Oras sa 12h format (01-12)\n%m Buwan bilang decimal na numero (01-12)\n%M Minute (00-59)\n%p AM or PM ng pagtatalaga\n%S Segundo (00-61)\n%y Taon, huling dalawang numero (00-99)\n%Y Taon\n%z ISO 8601 offset mula sa UTC or timezone\n Pangalan or Pangpaikli\n%Z Timezone na pangalan or pangpaikli\n" + +Basic.Settings.Video="Bidyo" +Basic.Settings.Video.Adapter="Adapter ng Bidyo" +Basic.Settings.Video.BaseResolution="Base (Kanbas) Resolusyon" +Basic.Settings.Video.ScaledResolution="Output (Pinaliit) Resolusyon" +Basic.Settings.Video.DownscaleFilter="Downscale Filter" +Basic.Settings.Video.DisableAeroWindows="Huwag Paganahin ang Aero (Windows only)" +Basic.Settings.Video.FPS="FPS" +Basic.Settings.Video.FPSCommon="Karaniwang Balyo ng FPS" +Basic.Settings.Video.FPSInteger="Balyo ng Integer FPS" +Basic.Settings.Video.FPSFraction="Balyo ng Fractional FPS" +Basic.Settings.Video.Numerator="Numerator" +Basic.Settings.Video.Denominator="Denominator" +Basic.Settings.Video.Renderer="Renderer" +Basic.Settings.Video.InvalidResolution="Ang Balyo ng resolusyong ay imbalido. Dapat ito [width]x[height] (i.e. 1920x1080)" +Basic.Settings.Video.CurrentlyActive="Ang kasulukuyang Bidyo output ay aktibo. Paki turn off ang anumang output para mabago ang bidyo settings." +Basic.Settings.Video.DisableAero="Hindi paganahin ang Aero" + +Basic.Settings.Video.DownscaleFilter.Bilinear="Bilinear (Pinakamabilis, pero malabo pag mag i-scaling)" +Basic.Settings.Video.DownscaleFilter.Bicubic="Bicubic (Sharpened scaling, 16 mga halimbawa)" +Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Sharpened scaling, 32 mga halimbawa)" + +Basic.Settings.Audio="Tunog" +Basic.Settings.Audio.SampleRate="Halimbawa ng Antas" +Basic.Settings.Audio.Channels="Mga Channel" +Basic.Settings.Audio.MeterDecayRate="Ang Audio Meter Decay Rate" +Basic.Settings.Audio.MeterDecayRate.Fast="Pabilisin" +Basic.Settings.Audio.MeterDecayRate.Medium="Katamtaman (Tipo I PPM)" +Basic.Settings.Audio.MeterDecayRate.Slow="Mabagal (Tipo II PPM)" +Basic.Settings.Audio.MultiChannelWarning.Enabled="Babala: Ang Surround sound audio ay naka andar." +Basic.Settings.Audio.MultichannelWarning="Kung nag streaming, paki tignan kung ang iyong streaming service ay parehong supportado ang surround sound ingest at surround sound playback. Twitch, Facebook, 360 Live, Mixer RTMP, Smashcast ay ang mga halimbawa kung saan ang surround sound ay ganap na suportado, pati YouTube Live umaandar lamang sa dawalang channels.\n\nOBS audio filters ay kompatibol lamang sa surround sound, pero hindi siguradong supportado ang VST plugin." +Basic.Settings.Audio.MultichannelWarning.Title="Paganahin ang surround sound audio?" +Basic.Settings.Audio.MultichannelWarning.Confirm="Sigurado ka ba gusto mong paganahin ang surround sound audio?" +Basic.Settings.Audio.DesktopDevice="Ang Kagamitan sa Desktop Audio" +Basic.Settings.Audio.DesktopDevice2="Ang 2 Kagamitan sa Desktop Audio" +Basic.Settings.Audio.AuxDevice="Mic / Auxiliary Audio Device" +Basic.Settings.Audio.AuxDevice2="Mic / Auxiliary Audio Device 2" +Basic.Settings.Audio.AuxDevice3="Mic / Auxiliary Audio Device 3" +Basic.Settings.Audio.EnablePushToMute="Paganahin ang Push-to-mute" +Basic.Settings.Audio.PushToMuteDelay="Push-to-mute delay" +Basic.Settings.Audio.EnablePushToTalk="Paganahin ang Push-to-talk" +Basic.Settings.Audio.PushToTalkDelay="Push-to-talk delay" +Basic.Settings.Audio.UnknownAudioDevice="[Hindi konektado o hindi magagamit ang device]" + +Basic.Settings.Advanced="Advanced" +Basic.Settings.Advanced.General.ProcessPriority="Prayoridad sa Proseso" +Basic.Settings.Advanced.General.ProcessPriority.High="Mataas" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Higit sa Normal" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normal" +Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Mas mababa sa normal" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Walang ginagawa" +Basic.Settings.Advanced.FormatWarning="Babala: Ang mga format ng kulay maliban sa NV12 ay pangunahing inilaan para sa pag-record, at hindi inirerekomenda kapag nag-stream. Maaaring mapalawak ng streaming ang paggamit ng CPU dahil sa conversion ng format ng kulay." +Basic.Settings.Advanced.Audio.BufferingTime="Audio Buffering Time" +Basic.Settings.Advanced.Video.ColorFormat="Format ng Kulay" +Basic.Settings.Advanced.Video.ColorSpace="Ang espasyong kulay ng YUV" +Basic.Settings.Advanced.Video.ColorRange="Ang saklaw ng kulay ng YUV" +Basic.Settings.Advanced.Video.ColorRange.Partial="Bahagyang" +Basic.Settings.Advanced.Video.ColorRange.Full="Buo" +Basic.Settings.Advanced.Audio.MonitoringDevice="Ang kagamitan para sa Audio Monitoring" +Basic.Settings.Advanced.Audio.MonitoringDevice.Default="I-Default" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Huwag paganahin ang Windows audio ducking" +Basic.Settings.Advanced.StreamDelay="Ang Antala ng Stream" +Basic.Settings.Advanced.StreamDelay.Duration="Ang Katagalan (segundo)" +Basic.Settings.Advanced.StreamDelay.Preserve="Ingatang ang cutoff point (pataas ng antala) kapang kumokonekta" +Basic.Settings.Advanced.StreamDelay.MemoryUsage="Ang na estimang nagamit na memorya: %1 MB" +Basic.Settings.Advanced.Network="Network" +Basic.Settings.Advanced.Network.BindToIP="Ibigkis sa IP" +Basic.Settings.Advanced.Network.EnableNewSocketLoop="Paganahin ang bagong networking code" +Basic.Settings.Advanced.Network.EnableLowLatencyMode="Mababang latency mode" + +Basic.AdvAudio="Ang aria-arian ng Advanced Audio" +Basic.AdvAudio.Name="Pangalan" +Basic.AdvAudio.Volume="Kalakasan (%)" +Basic.AdvAudio.Mono="Downmix ito sa Mono" +Basic.AdvAudio.Panning="Ang Panning" +Basic.AdvAudio.SyncOffset="Ang Sync Offset (ms)" +Basic.AdvAudio.Monitoring="Ang subaybay ng Audio" +Basic.AdvAudio.Monitoring.None="I-Off ang Monitor" +Basic.AdvAudio.Monitoring.MonitorOnly="Monitor lamang (i-mute ang output)" +Basic.AdvAudio.Monitoring.Both="Monitor at Awput" +Basic.AdvAudio.AudioTracks="Mga Tracks" + +Basic.Settings.Hotkeys="Ang mga Hotkeys" +Basic.Settings.Hotkeys.Pair="Ang nabahaging kombinasyon ng susi na may '%1' akto sa toggles" + +Basic.Hotkeys.SelectScene="Lumipat sa eksena" + +Basic.SystemTray.Show="Ipakita" +Basic.SystemTray.Hide="Itago" + +Basic.SystemTray.Message.Reconnecting="Nadiskonek. Kumokonekta..." + +Hotkeys.Insert="Ipasok" +Hotkeys.Delete="Burahin" +Hotkeys.Home="Pinagmulan" +Hotkeys.End="Tapos" +Hotkeys.PageUp="Itaas ng Pahina" +Hotkeys.PageDown="Ibaba ng pahina" +Hotkeys.NumLock="Num Lock" +Hotkeys.ScrollLock="Scroll Lock" +Hotkeys.CapsLock="Caps Lock" +Hotkeys.Backspace="Backspace" +Hotkeys.Tab="Tab" +Hotkeys.Print="Printa" +Hotkeys.Pause="Itigil" +Hotkeys.Left="Kaliwa" +Hotkeys.Right="Kanan" +Hotkeys.Up="Itaas" +Hotkeys.Down="Ibaba" +Hotkeys.Windows="Windows" +Hotkeys.Super="Super" +Hotkeys.Menu="Pagpipilian" +Hotkeys.Space="Espasyo" +Hotkeys.NumpadNum="Numpad %1" +Hotkeys.NumpadMultiply="Paramihin ang Numpad" +Hotkeys.NumpadDivide="Hatiin ang Numpad" +Hotkeys.NumpadAdd="Magdagdag ng Numpad" +Hotkeys.NumpadSubtract="Magbawas ng Numpad" +Hotkeys.NumpadDecimal="Numero ng Numpad" +Hotkeys.AppleKeypadNum="%1 (Keypad)" +Hotkeys.AppleKeypadMultiply="* (Keypad)" +Hotkeys.AppleKeypadDivide="/ (Keypad)" +Hotkeys.AppleKeypadAdd="+ (Keypad)" +Hotkeys.AppleKeypadSubtract="- (Keypad)" +Hotkeys.AppleKeypadDecimal=". (Keypad)" +Hotkeys.AppleKeypadEqual="= (Keypad)" +Hotkeys.MouseButton="Mouse %1" + +Mute="I-Mute" +Unmute="I-Unmute" +Push-to-mute="Pindutin-para-i-mute" +Push-to-talk="Pindutan-para-magsalita" + +SceneItemShow="Ipakita '%1'" +SceneItemHide="Itago '%1'" + +OutputWarnings.NoTracksSelected="Dapat pumili ka ng kahit isang track" +OutputWarnings.MultiTrackRecording="Babala: Tiyak na pormat (gaya ng FLV) hindi suportado ang maraming tracks kada recording" +OutputWarnings.MP4Recording="Babala: Ang Recording na naka-save sa MP4 ay hindi unrecoverable kung ang file any hindi tinapos (e.g bilang resulta nito BSODs, power losses, atbp.). Kung gusto mo i-record ng maraming audio tracks isaalang-alang ang paggamit ng MKV at remux ang recording para sa mp4 pagkatapos nitong matapos (File->Remux Recordings)" + +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 17e52e017..0e12f988b 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 000000000..0ea4d8659 --- /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 4411f70b1..c90dfc701 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 051cb50bb..0f8e6a029 100644 --- a/UI/data/locale/he-IL.ini +++ b/UI/data/locale/he-IL.ini @@ -28,16 +28,21 @@ Browse="עיון" Mono="מונו" Stereo="סטריאו" DroppedFrames="השמטת תמונות %1 (%2%)" +StudioProgramProjector="מקרן מסך מלא (תוכנה)" PreviewProjector="מקרן מסך מלא (תצוגה מקדימה)" SceneProjector="מקרן מסך מלא (סצנה)" SourceProjector="מקרן מסך מלא (מקור)" +StudioProgramWindow="מקרן חלון (תוכנה)" PreviewWindow="הקרנה בחלון (תצוגה מקדימה)" SceneWindow="הקרנה בחלון (סצנה)" SourceWindow="הקרנה בחלון (מקור)" +MultiviewProjector="תצוגה מרובה (מסך מלא)" +MultiviewWindowed="תצוגה מרובה (חלון)" Clear="נקה" Revert="החזר לקדמותו" Show="הצג" Hide="הסתר" +UnhideAll="הצג הכל" Untitled="ללא כותרת" New="חדש" Duplicate="שכפל" @@ -66,6 +71,13 @@ PasteDuplicate="הדבק (כפול)" RemuxRecordings="רימיקס הקלטות" Next="הבא" Back="קודם" +Defaults="ברירות מחדל" +HideMixer="מוחבא בתוך המיקסר" +TransitionOverride="מעבר דרוס" +None="ללא" +StudioMode.Preview="תצוגה מקדימה" +StudioMode.Program="תוכנה" +ShowInMultiview="הראה בתצוגה מרובה" AlreadyRunning.Title="OBS פועל כבר" AlreadyRunning.Text="OBS פועל כבר! אלא אם התכוונת לעשות את זה, אנא סגור כל מופע קיים לפני נסיון להפעיל מופע חדש. אנא בדוק אם קיים מופע הגדר ממוזער במגש המערכת." @@ -110,6 +122,7 @@ Basic.AutoConfig.StreamPage.PerformBandwidthTest="הערכת קצב נתונים Basic.AutoConfig.StreamPage.PreferHardwareEncoding="העדף קידוד בחומרה" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="קידוד בחומרה מבטלת רוב השימוש ב- CPU, אבל עשויים לדרוש קצב נתונים גבוה יותר בכדי להשיג את אותה רמת איכות." Basic.AutoConfig.StreamPage.StreamWarning.Title="אזהרת זרם" +Basic.AutoConfig.StreamPage.StreamWarning.Text="בדיקת רוחב הפס עומדת להזרים נתוני וידאו אקראיים ללא שמע לערוץ שלך. אם אתה מסוגל, מומלץ לבטל באופן זמני את שמירת קטעי הוידאו של הזרם וקבע את הזרם לפרטי עד לאחר שהבדיקה תסתיים. להמשיך?" Basic.AutoConfig.TestPage="תוצאות סופיות" Basic.AutoConfig.TestPage.SubTitle.Testing="התוכנית מתבצעת עכשיו סט של בדיקות כדי להעריך את ההגדרות המיטביות" Basic.AutoConfig.TestPage.SubTitle.Complete="הבדיקה הסתיימה" @@ -156,6 +169,7 @@ Updater.NoUpdatesAvailable.Title="אין עדכונים זמינים" Updater.NoUpdatesAvailable.Text="אין עדכונים זמינים כעת" Updater.FailedToLaunch="נכשלה הפעלת העידכון" Updater.GameCaptureActive.Title="לכידת משחק פעיל" +Updater.GameCaptureActive.Text="לכידת משחק נמצאת כעת בשימוש. בבקשה סגור את המשחקים/תוכנות שנמצאים בלכידה (או הפעל מחדש את ווינדוס) ונסה שינת." QuickTransitions.SwapScenes="החלף סצינות תצוגה מקדימה/פלט לאחר המעבר" QuickTransitions.SwapScenesTT="החלף הסצינות של התצוגה המקדימה ושל הפלט לאחר המעבר (באם הסצינה המקורית של הפלט עדיין קיימת). \n פעולה זו לא תבטל כל שינוי שייתכן ובוצע לסצינה המקורית של הפלט." @@ -200,6 +214,7 @@ ConfirmRemove.TextMultiple="האם אתה בטוח שברצונך להסיר %1 Output.StartStreamFailed="נכשלה הפעלת זרימה" Output.StartRecordingFailed="נכשלה הפעלת הקלטה" Output.StartReplayFailed="נכשלה הפעלת מאגר החוזר" +Output.StartFailedGeneric="הפלט נכשל. בבקשה בדוק את הרישומים עבור. \n\nNote: אם אתה משתמש בNVNEC או AMD מקודדים, הקפד שמנהלי ההתקן של הכרטיס מסך מעודכנים." Output.ConnectFail.Title="ההתחברות נכשלה" Output.ConnectFail.BadPath="URL לא חוקי של נתיב או חיבור. נא בדוק את ההגדרות שלך כדי לוודא כי הם נכונים." @@ -214,6 +229,8 @@ Output.RecordNoSpace.Title="אין די שטח דיסק" Output.RecordNoSpace.Msg="אין די שטח דיסק כדי להמשיך הקלטה." Output.RecordError.Title="שגיאה הקלטה" Output.RecordError.Msg="אירעה שגיאה לא מוגדרת בזמן ההקלטה." +Output.ReplayBuffer.NoHotkey.Title="אין מקש קיצור שנבחר!" +Output.ReplayBuffer.NoHotkey.Msg="אין hotkey שמור למאגר החוזר. בבקשה הגדר את \"שמור\" hotkey לשימוש עבור שמירת מאגר חוזר להקלטות." Output.BadPath.Title="נתיב קובץ לא תקין" Output.BadPath.Text="נתיב פלט הקובץ שהוגדר אינו חוקי. נא בדוק את הגדרות כדי לוודא שנתיב קובץ תקני נקבע." @@ -295,6 +312,8 @@ AddProfile.Text="אנא הזן את שם הפרופיל" RenameProfile.Title="שנה שם פרופיל" +Basic.Main.MixerRename.Title="שינוי שם מקור שמע" +Basic.Main.MixerRename.Text="בבקשה הכנס את שם קובץ השמע" Basic.Main.PreviewDisabled="תצוגה מקדימה אינה זמינה כעת" @@ -374,6 +393,7 @@ Basic.Main.AddSourceHelp.Text="צריכה להיות לפחות סצנה אחת Basic.Main.Scenes="סצינות" Basic.Main.Sources="מקורות" +Basic.Main.Controls="בקרה" Basic.Main.Connecting="מתחבר..." Basic.Main.StartRecording="התחל הקלטה" Basic.Main.StartReplayBuffer="התחל מאגר החוזר" @@ -429,19 +449,27 @@ Basic.MainMenu.Edit.AdvAudio="מאפייני קול מתקדמים(&A)" 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="אוסף סצינות(&S)" Basic.MainMenu.Profile="פרופיל(&P)" Basic.MainMenu.Profile.Import="ייבא פרופיל" Basic.MainMenu.Profile.Export="ייצא פרופיל" +Basic.MainMenu.SceneCollection.Import="ייבא אוסף סצינות" +Basic.MainMenu.SceneCollection.Export="ייצא אוסף סצנות" Basic.MainMenu.Profile.Exists="הפרופיל קיים כבר" +Basic.MainMenu.SceneCollection.Exists="אוסף הסצנות כבר קיים" Basic.MainMenu.Tools="& כלים" Basic.MainMenu.Help="עזרה(&H)" +Basic.MainMenu.Help.HelpPortal="עזרה & פורטל" Basic.MainMenu.Help.Website="בקר אתר(&W)" Basic.MainMenu.Help.Logs="קבצי יומן רישום(&L)" Basic.MainMenu.Help.Logs.ShowLogs="הצג קבצי יומן רישום(&S)" @@ -458,10 +486,12 @@ 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="הצמד מקורות למרכז אופקי ואנכי" @@ -469,10 +499,15 @@ 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.MultiviewLayout="פריסת תצוגה מרובה" Basic.Settings.Stream="זרם נתונים" Basic.Settings.Stream.StreamType="סוג זרם נתונים" @@ -592,6 +627,14 @@ 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="בינוני (סוג I PPM)" +Basic.Settings.Audio.MeterDecayRate.Slow="איטי (סוג II PPM)" +Basic.Settings.Audio.MultiChannelWarning.Enabled="אזהרה: שמע צליל היקפי מאופשר." +Basic.Settings.Audio.MultichannelWarning="אם הזרימה, בדוק את הזרמת השירות תומך בסראונד סאונד להבלע ואת היקפי הסראונד פלייבק. טוויץ',פייסבוק,360 Liev, מיקסר RTMP, Smashcast דוגמאות לאיפה שסראונד סאונד הוא נתמך. למרות פייסבוק לייב ויוטיוב לייב שניהם מאפשרים סראונד להבלע, פייסבוק לייב משנה לסטריאו ויוטיוב לייב מפעיל בשני ערוצים. \n\nOBS Studio מסנן עם הסראונד סאונד, למרות תמיכת תוסף VST אינו מאובטח." +Basic.Settings.Audio.MultichannelWarning.Title="האם להפעיל שמע צליל היקפי?" +Basic.Settings.Audio.MultichannelWarning.Confirm="אתה בטוח שאתה רוצה לאפשר קול שמע מקיף?" Basic.Settings.Audio.DesktopDevice="התקן שמע בשולחן עבודה" Basic.Settings.Audio.DesktopDevice2="התקן שמע בשולחן עבודה 2" Basic.Settings.Audio.AuxDevice="התקן שמע מיקרופון/עזר" @@ -608,6 +651,7 @@ 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="זמן אוגר שמע" @@ -701,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 000000000..16c8600bb --- /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 c7ca15a24..744d7c6be 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 d8b91f036..6413a5584 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 39d72e9cf..2a121b429 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 f1f4da49a..6dc4ddc78 100644 --- a/UI/data/locale/ja-JP.ini +++ b/UI/data/locale/ja-JP.ini @@ -28,15 +28,15 @@ Browse="参照" Mono="モノラル" Stereo="ステレオ" DroppedFrames="ドロップしたフレーム %1 (%2%)" -StudioProgramProjector="全画面プロジェクター (プログラム)" +StudioProgramProjector="全画面プロジェクター (番組)" PreviewProjector="全画面プロジェクター (プレビュー)" SceneProjector="全画面プロジェクター (シーン)" SourceProjector="全画面プロジェクター (ソース)" -StudioProgramWindow="ウィンドウ付きプロジェクター (プログラム)" +StudioProgramWindow="ウィンドウ プロジェクター (番組)" PreviewWindow="ウィンドウ プロジェクター (プレビュー)" SceneWindow="ウィンドウ プロジェクター (シーン)" SourceWindow="ウィンドウ プロジェクター (ソース)" -MultiviewProjector="マルチビュー (フルスクリーン)" +MultiviewProjector="マルチビュー (全画面)" MultiviewWindowed="マルチビュー (ウィンドウ)" Clear="クリア" Revert="元に戻す" @@ -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 2b8684d83..eb59e36f4 100644 --- a/UI/data/locale/ka-GE.ini +++ b/UI/data/locale/ka-GE.ini @@ -1,6 +1,9 @@ +Language="ქართული" +Region="საქართველო" -OK="კაი" +OK="კარგი" +Apply="მიღება" Cancel="გაუქმება" Close="დახურვა" Save="შენახვა" @@ -11,16 +14,32 @@ 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="ყველაფრის გამოჩენა" @@ -30,118 +49,730 @@ Duplicate="დუბლირება" Enable="ჩართვა" DisableOSXVSync="OSX V-Sync-ის გამორთვა" ResetOSXVSyncOnExit="OSX V-Sync-ის გადატვირთვა გასვლისას" +HighResourceUsage="დამშიფრავი გადაიტვირთა! სცადეთ ვიდეოს პარამეტრების შემცირება, ან უფრო სწრაფი შიფრაციის პარამეტრების გამოყენება." +Transition="გადასვლა" +QuickTransitions="სწრაფი გადასვლები" +Left="მარცხნივ" +Right="მარჯვნივ" +Top="ზემოთ" +Bottom="ქვემოთ" +Reset="განულება" +Hours="საათი" Minutes="წუთი" Seconds="წამი" - - - - - - +Deprecated="მოძველებული" +ReplayBuffer="უკან გადახვევის დრო" +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 fb684d8a7..4abb6a2c1 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 8e5937207..97e889ffd 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 c0b73750f..1413cc024 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 744b7e566..35561a4f5 100644 --- a/UI/data/locale/nb-NO.ini +++ b/UI/data/locale/nb-NO.ini @@ -61,7 +61,7 @@ Hours="Timer" Minutes="Minutter" Seconds="Sekunder" Deprecated="Foreldet" -ReplayBuffer="Omspill Buffer" +ReplayBuffer="Omspillingsbuffer" Import="Importer" Export="Eksporter" Copy="Kopier" @@ -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 07ede936f..34ceac9f1 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 8405b9108..582e19aef 100644 --- a/UI/data/locale/nn-NO.ini +++ b/UI/data/locale/nn-NO.ini @@ -7,6 +7,7 @@ Apply="Bruk" Cancel="Avbryt" Close="Lukk" Save="Lagre" +Discard="Bryt av" Disable="Skrue av" Yes="Ja" No="Nei" @@ -22,9 +23,13 @@ Settings="Innstillingar" Display="Skjerm" Name="Namn" Exit="Avslutt" +Mixer="Blandar" Browse="Bla gjennom" Mono="Mono" Stereo="Stereo" +DroppedFrames="Slopne bilete %1 (%2%)" +StudioProgramProjector="Fullskjermframvisar (program)" +PreviewProjector="Fullskjermframvisar (førehandsvising)" MultiviewProjector="Fleirvising (Fullskjerm)" MultiviewWindowed="Fleirvising (i vindauge)" Clear="Tøm" @@ -33,6 +38,22 @@ Hide="Skjul" Untitled="Utan namn" New="Ny" Enable="Aktiver" +Left="Venstre" +Right="Høgre" +Top="Topp" +Bottom="Botn" +Reset="Attendeset" +Hours="Timar" +Minutes="Minutt" +Seconds="Sekund" +Import="Innfør" +Export="Utfør" +Copy="Kopier" +Paste="Lim inn" +Next="Neste" +Back="Attende" +Defaults="Standardar" +None="Ingen" StudioMode.Preview="Førehandsvising" StudioMode.Program="Program" @@ -176,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 ace22ef71..3ed7ca605 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 a0361013d..593869765 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 25c82a734..5c6cd0ef9 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 0c3b094f3..43208f054 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 7462e4878..cb2a1a7be 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 на сворачивание в системный трей, пожалуйста, проверьте, возможно он до сих пор запущен." @@ -171,11 +173,11 @@ Updater.FailedToLaunch="Не удалось проверить обновлен Updater.GameCaptureActive.Title="Производится захват игры" Updater.GameCaptureActive.Text="Библиотека захвата игр уже используется. Закройте захватываемые игры/программы (или перезапустите Windows) и попробуйте ещё раз." -QuickTransitions.SwapScenes="Замена Просмотра/Вывода Сцены После Перехода" -QuickTransitions.SwapScenesTT="Замена просмотра и вывода сцены после перехода (если выходная оригинальная сцена до сих пор существует).\nЭто будет не отмена каких-либо изменений, что, возможно, было сделано в выходной оригинальной сцены." -QuickTransitions.DuplicateScene="Повторяющиеся Сцены" +QuickTransitions.SwapScenes="Менять местами сцены предпросмотра и вывода после перехода" +QuickTransitions.SwapScenesTT="Меняет местами сцены предпросмотра и вывода после перехода (если оригинальная выходная сцена до сих пор существует).\nЭто не отменяет никаких изменений, которые возможно были сделаны в оригинальной выходной сцене." +QuickTransitions.DuplicateScene="Дублировать сцену" QuickTransitions.DuplicateSceneTT="При редактировании одной и той же сцены, функция позволяет трансформировать редактирования/видимости источников без изменения выхода.\nДля редактирования свойств источников без изменения выходного сигнала, включить \"дублировать источники'.\nИзменение этого параметра приведет к сбросу выходного сигнала в сцене (если оно еще существует)." -QuickTransitions.EditProperties="Дублировать Источники" +QuickTransitions.EditProperties="Дублировать источники" QuickTransitions.EditPropertiesTT="При редактировании одной и той же сцены, функция позволяет редактировать свойства источников без изменения выхода.\nЭто может только использоваться, если \"Повторяющиеся Сцены\" включен.\nНекоторые источники (такие как захват или медиа-источники) не поддерживаются и не могут быть отредактированы отдельно.\nИзменение этого параметра приведет к сбросу выходного сигнала в сцене (если оно еще существует).\n\nПредупреждение: поскольку источники будут дублироваться, это может потребовать дополнительных системных ресурсов или видео." QuickTransitions.HotkeyName="Быстрый переход: %1" @@ -274,10 +276,10 @@ Basic.DisplayCapture="Захват экрана" Basic.Main.PreviewConextMenu.Enable="Включить предпросмотр" -ScaleFiltering="Фильтрация масштаба" -ScaleFiltering.Point="Точечная" -ScaleFiltering.Bilinear="Билинейная" -ScaleFiltering.Bicubic="Бикубическая" +ScaleFiltering="Фильтр масштабирования" +ScaleFiltering.Point="Точечный" +ScaleFiltering.Bilinear="Билинейный" +ScaleFiltering.Bicubic="Бикубический" ScaleFiltering.Lanczos="Метод Ланцоша" Deinterlacing="Устранение чересстрочности" @@ -399,12 +401,15 @@ Basic.Main.StartRecording="Начать запись" Basic.Main.StartReplayBuffer="Запустить повтор" Basic.Main.StartStreaming="Запустить трансляцию" Basic.Main.StopRecording="Остановить запись" -Basic.Main.StoppingRecording="Остановка Записи..." +Basic.Main.StoppingRecording="Остановка записи..." Basic.Main.StopReplayBuffer="Остановить повтор" Basic.Main.StoppingReplayBuffer="Остановка повтора..." Basic.Main.StopStreaming="Остановить трансляцию" Basic.Main.StoppingStreaming="Остановка вещания..." -Basic.Main.ForceStopStreaming="Остановить передачу (отменить задержку)" +Basic.Main.ForceStopStreaming="Остановить трансляцию (сбросить задержку)" +Basic.Main.Group="Группа %1" +Basic.Main.GroupItems="Сгруппировать выбранные элементы" +Basic.Main.Ungroup="Разгруппировать" Basic.MainMenu.File="&Файл" Basic.MainMenu.File.Export="&Экспорт" @@ -413,7 +418,7 @@ Basic.MainMenu.File.ShowRecordings="Показать &записи" Basic.MainMenu.File.Remux="Ре&мультиплексирование записей" Basic.MainMenu.File.Settings="&Настройки" Basic.MainMenu.File.ShowSettingsFolder="Показать папку с настройками" -Basic.MainMenu.File.ShowProfileFolder="Показать папку профиля" +Basic.MainMenu.File.ShowProfileFolder="Показать папку с профилями" Basic.MainMenu.AlwaysOnTop="&Поверх других окон" Basic.MainMenu.File.Exit="&Выход" @@ -423,15 +428,15 @@ 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="&Масштабирование предпросмотра" 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="&Трансформировать" +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 градусов" @@ -441,7 +446,7 @@ 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.MoveUp="Переместить &выше" Basic.MainMenu.Edit.Order.MoveDown="Переместить &Ниже" Basic.MainMenu.Edit.Order.MoveToTop="Переместить &Наверх" Basic.MainMenu.Edit.Order.MoveToBottom="Переместить &Вниз" @@ -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="Подтверждить Изменения" @@ -491,27 +500,32 @@ Basic.Settings.General.WarnBeforeStartingStream="Показывать окно Basic.Settings.General.WarnBeforeStoppingStream="Показывать окно подтверждения при остановке трансляции" Basic.Settings.General.Projectors="Проекторы" Basic.Settings.General.HideProjectorCursor="Скрыть курсор за проекторы" -Basic.Settings.General.ProjectorAlwaysOnTop="Показывать проекторы поверх всего остального" +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.RecordWhenStreaming="Автоматически включать запись во время трансляции" +Basic.Settings.General.KeepRecordingWhenStreamStops="Продолжать запись после завершения трансляции" Basic.Settings.General.ReplayBufferWhileStreaming="Автоматически запускать буфер повтора во время трансляции" -Basic.Settings.General.KeepReplayBufferStreamStops="Сохранять буфер повтора активным когда останавливается трансляция" +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="Горизонтально, вверху" -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="Тип вещания" @@ -534,7 +548,7 @@ 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.SavePath="Путь записи" Basic.Settings.Output.Simple.RecordingQuality="Качество записи" Basic.Settings.Output.Simple.RecordingQuality.Stream="То же, что у трансляции" Basic.Settings.Output.Simple.RecordingQuality.Small="Высокое качество, средний размер файла" @@ -552,8 +566,8 @@ Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Аппаратный (QSV)" Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Аппаратный (AМD)" 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.VideoBitrate="Битрейт видео" +Basic.Settings.Output.AudioBitrate="Битрейт аудио" Basic.Settings.Output.Reconnect="Автопереподключение" Basic.Settings.Output.RetryDelay="Переподключиться через (секунд)" Basic.Settings.Output.MaxRetries="Количество попыток подключиться" @@ -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 fc9bb9915..fd45ee8a2 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 ea3a3bdd9..d749b2c02 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 new file mode 100644 index 000000000..7a888404e --- /dev/null +++ b/UI/data/locale/sq-AL.ini @@ -0,0 +1,173 @@ + +Language="Gjuha Angleze" +Region="Shtetet e Bashkuara" + +OK="OK" +Apply="Aplikoni" +Cancel="Anuloni" +Close="Mbylle" +Save="Ruaj" +Discard="Mos e ruaj" +Disable="Çaktivizo" +Yes="Po" +No="Jo" +Add="Shto" +Remove="Largoje" +Rename="Riemërto" +Interact="Bashkëveprim" +Filters="Filterar" +Properties="Vetitë" +MoveUp="Lëvize lart" +MoveDown="Lëvize poshtë" +Settings="Cilësimet" +Display="Ekrani" +Name="Emri" +Exit="Dil" +Mixer="Mikser" +Browse="Shfleto" +Mono="Mono" +Stereo="Stereo" +DroppedFrames="Kornizat e rëna %1 (%2%)" +StudioProgramProjector="Projektor me ekran të plotë (Program)" +PreviewProjector="Projektorë me ekran të plotë (Parashikim)" +SceneProjector="Ekran me ekran të plotë (Skena)" +SourceProjector="Projektorë me ekran të plotë (Burimi)" +StudioProgramWindow="Projektor Windowed (Program)" +PreviewWindow="Projektor Windowed (Parashikim)" +SceneWindow="Projektor Windowed (Skena)" +SourceWindow="Projektor Windowed (Burimi)" +MultiviewProjector="Shumë pamje (Ekran i plotë)" +MultiviewWindowed="Shumë pamje (Windowed)" +Clear="Pastro" +Revert="Rikthe" +Show="Shfaq" +Hide="Fsheh" +UnhideAll="Zbuloni të gjitha" +Untitled="I patitulluar" +New="I ri" +Duplicate="Dublikatë" +Enable="Aktivizo" +DisableOSXVSync="Çaktivizo OSX V-Sync" +ResetOSXVSyncOnExit="Rivendos OSX V-Sync në Mbyllje" +HighResourceUsage="Kodimi i mbingarkuar! Merrni parasysh uljen e cilësimeve të videos ose duke përdorur një parazgjedhje më të shpejtë të kodimit." +Transition="Tranzicion" +QuickTransitions="Tranzicione të shpejta" +Left="Majtas" +Right="Djathtas" +Top="Kreu" +Bottom="Fundi" +Reset="Rivë" +Hours="Orë" +Minutes="Minuta" +Seconds="Sekonda" +Deprecated="I pazëvëndësueshëm" +ReplayBuffer="Rifillimi Lustrues" +Import="Importo" +Export="Eksporto" +Copy="Kopjo" +Paste="Ngjit" +PasteReference="Ngjit (Referim)" +PasteDuplicate="Ngjit (Duplikatë)" +RemuxRecordings="Regjistrimet e Remux" +Next="Tjeter" +Back="Prapa" +Defaults="Parazgjedhura" +HideMixer="Fshiheni në Mixer" +TransitionOverride="Zhvendosja e tranzicionit" +None="Asnje" +StudioMode.Preview="Inspektim" +StudioMode.Program="Program" +ShowInMultiview="Shfaqe në Shumë Pamje" + +AlreadyRunning.Title="OBS tashmë po funksion" +AlreadyRunning.Text="OBS tashmë po kandidon! Nëse nuk keni ndërmend ta bëni këtë, mbyllni çdo rast ekzistues të OBS para se të provoni të shkoni në një instancë të re. Nëse keni OBS vendosur për të minimizuar në tabaka e sistemit, ju lutemi kontrolloni për të parë nëse vazhdon të ekzekutohet atje." +AlreadyRunning.LaunchAnyway="Filloje ne nje menyre" + +Copy.Filters="Kopjo Filterat" +Paste.Filters="Ngjit Filterat" + +BandwidthTest.Region="Regjioni" +BandwidthTest.Region.US="Shtetet e Bashkuara" +BandwidthTest.Region.EU="Evropë" +BandwidthTest.Region.Asia="Azi" +BandwidthTest.Region.Other="Të tjera" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/data/locale/sr-CS.ini b/UI/data/locale/sr-CS.ini index d7fecd47e..44ea89e66 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 45c724202..49b2f82c1 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 bd704aaf5..b1b065be4 100644 --- a/UI/data/locale/sv-SE.ini +++ b/UI/data/locale/sv-SE.ini @@ -36,8 +36,8 @@ StudioProgramWindow="Fönsterprojektor (program)" PreviewWindow="Fönsterprojektor (förhandsvisning)" SceneWindow="Fönsterprojektor (scen)" SourceWindow="Fönsterprojektor (källa)" -MultiviewProjector="Flervy (helskärm)" -MultiviewWindowed="Flervy (fönster)" +MultiviewProjector="Multivy (helskärm)" +MultiviewWindowed="Multivy (fönster)" Clear="Rensa" Revert="Återgå" Show="Visa" @@ -77,7 +77,9 @@ TransitionOverride="Övergångsåsidosättande" None="Ingen" StudioMode.Preview="Förhandsvisning" StudioMode.Program="Program" -ShowInMultiview="Visa i flervy" +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.MultiviewLayout="Multivisningslayout" -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.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 (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 a93f85a67..b41973821 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 cbbb8e657..7ab922ad6 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 new file mode 100644 index 000000000..c4d56281d --- /dev/null +++ b/UI/data/locale/tl-PH.ini @@ -0,0 +1,750 @@ + +Language="Ingles" +Region="Estados Unidos" + +OK="OK" +Apply="Gamitin" +Cancel="Kanselahin" +Close="Isara" +Save="I-save" +Discard="Alisin" +Disable="I-disable" +Yes="Oo" +No="Hindi" +Add="Idagdag" +Remove="Tanggalin" +Rename="Palitan ang pangalan" +Interact="Interact" +Filters="Pansala" +Properties="Mga Katangian" +MoveUp="I-taas" +MoveDown="I-baba" +Settings="Mga Setting" +Display="Ipakita" +Name="Pangalan" +Exit="Lumabas" +Mixer="Mixer" +Browse="Browse" +Mono="Mono" +Stereo="Stereo" +DroppedFrames="Mga imaheng hindi sinali %1 (%2%)" +StudioProgramProjector="Fullscreen Projector (Programa)" +PreviewProjector="Fullscreen Projector (Preview)" +SceneProjector="Fullscreen Projector (Eksena)" +SourceProjector="Fullscreen Projector (Pinagmulan)" +StudioProgramWindow="Windowed Projector (Programa)" +PreviewWindow="Windowed Projector (Preview)" +SceneWindow="Windowed Projector (Eksena)" +SourceWindow="Windowed Projector (Pinagmulan)" +MultiviewProjector="Multiview (Fullscreen)" +MultiviewWindowed="Multiview (Windowed)" +Clear="Linisin" +Revert="Ibalik" +Show="Ipakita" +Hide="Itago" +UnhideAll="Ipakita Lahat" +Untitled="Walang pamagat" +New="Bago" +Duplicate="Kahalintulad" +Enable="I-enable" +DisableOSXVSync="I-disable ang OSX V-Sync" +ResetOSXVSyncOnExit="I-set muli ang OSX V-Sync sa Exit" +HighResourceUsage="Labis na ang karga sa pag-eencode! Pagisipan ang pagpapababa ng mga video setting o ang pag-gamit ng mas mabilis na encoding preset." +Transition="Transisyon" +QuickTransitions="Mabilis na mga Transisyon" +Left="Kaliwa" +Right="Kanan" +Top="Tuktok" +Bottom="Pinakababa" +Reset="I-set muli" +Hours="Mga oras" +Minutes="Mga minuto" +Seconds="Mga segundo" +Deprecated="Hindi na ginagamit" +ReplayBuffer="Replay Buffer" +Import="I-angkat" +Export="I-export" +Copy="Kopyahin" +Paste="I-paste" +PasteReference="I-paste (Reperensya)" +PasteDuplicate="I-paste (Kopya)" +RemuxRecordings="Remux Recordings" +Next="Sunod" +Back="Bumalik" +Defaults="Mga Default" +HideMixer="Itago sa Mixer" +TransitionOverride="Pagpapawalang-bisa ng Transisyon" +None="Wala" +StudioMode.Preview="Preview" +StudioMode.Program="Programa" +ShowInMultiview="Ipakita sa Multiview" + +AlreadyRunning.Title="Ang OBS ay tumatakbo na" +AlreadyRunning.Text="Ang OBS ay tumatakbo na! Kung hindi mo ito sinasadya, mangyari lamang patayin ang mga umiiral na OBS bago subukang magpatakbo ng bago. Kung ikaw ay may OBS set na kailangan i-minimize sa system tray, mangyari lamang tingnan kung ito ay tumatakbo pa doon." +AlreadyRunning.LaunchAnyway="Ilunsad pa rin" + +Copy.Filters="Kopyahin ang mga Panala" +Paste.Filters="I-paste ang mga Panala" + +BandwidthTest.Region="Rehiyon" +BandwidthTest.Region.US="Estados Unidos" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Asya" +BandwidthTest.Region.Other="Iba pa" + +Basic.FirstStartup.RunWizard="Nais mo bang patakbuhin ang auto-configuration wizard? Maaari mo ring i-configure ang iyong mga setting ng mano-mano sa pamamagitan ng pagpindot ng Settings na button sa pangunahing window." +Basic.FirstStartup.RunWizard.BetaWarning="(Tandaan: Ang auto-configuration wizard ay kasukuyang nasa beta)" +Basic.FirstStartup.RunWizard.NoClicked="Kung magbabago ang iyong isip, maaari mong patakbuhin ang auto-configuration wizard sa anumang oras mula muli sa Tools na menu." + +Basic.AutoConfig="Auto-Configuration Wizard" +Basic.AutoConfig.Beta="Auto-Configuration Wizard (Beta)" +Basic.AutoConfig.ApplySettings="Gamitin ang mga Setting" +Basic.AutoConfig.StartPage="Impormasyon ukol sa Paggamit" +Basic.AutoConfig.StartPage.SubTitle="Tukuyin kung para saan mo gustong gamitin ang programa" +Basic.AutoConfig.StartPage.PrioritizeStreaming="I-optimize para sa pag-stream, pangalawa lamang pag-rerekord" +Basic.AutoConfig.StartPage.PrioritizeRecording="I-optimize para lamang sa pag-rerekord, Hindi ako mag-iistream" +Basic.AutoConfig.VideoPage="Mga Setting sa Video" +Basic.AutoConfig.VideoPage.SubTitle="Tukuyin ang mga setting ng video na gusto mong gamitin" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Gamitin ang Pangkasalukuyang (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Ipakita ang %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Gamitin ang Pangkasalukuyang (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 o 30, pero mas piliin ang 60 kung maaari" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 o 30, pero mas piliin ang mataas na resolution" +Basic.AutoConfig.VideoPage.CanvasExplanation="Tandaan: Ang kanbas (base) na resolution ay hindi kinakailangang katulad sa resolution na gagamitin mo sa pag-stream o pagrekord. Ang actual na resolution ng iyong stream/rekord ay maaaring pababain upang mabawasan ang gamit sa mga resource o mga kakailanganing bitrate." +Basic.AutoConfig.StreamPage="Mag-stream ng Impormasyon" +Basic.AutoConfig.StreamPage.SubTitle="Mangyari lamang ilagay ang impormasyon ng iyong pagstream" +Basic.AutoConfig.StreamPage.Service="Serbisyo" +Basic.AutoConfig.StreamPage.Service.ShowAll="Ipakita Lahat..." +Basic.AutoConfig.StreamPage.Server="Serber" +Basic.AutoConfig.StreamPage.StreamKey="Stream Key" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Estimahin ang bitrate gamit ang bandwith test (maaaring tumagal ng ilang minuto)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Mas piliin ang hardware encoding" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Ang Hardware Encoding ay nagtatanggal ng karamihan sa paggamit ng CPU, ngunit maaaring nangangailangan ito ng mas maraming bitrate upang magkaroon ng katulad na lebel ng kalidad." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Babala sa stream" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Ang bandwith test ay mag-iistream ng datos ng video nang walang audio sa iyong channel. Kung kaya mo, minumungkahi namin na pansamantala mong i-off ang pag-save ng mga video ng mga stream at gawing pribado ang stream hanggang sa matapos ang test. Magpatuloy?" +Basic.AutoConfig.TestPage="Mga Huling Resulta" +Basic.AutoConfig.TestPage.SubTitle.Testing="Ang programang ito ay nagapapatupad ngayon ng mga pagsusuri upang matantiya ang pinakamainam na mga setting" +Basic.AutoConfig.TestPage.SubTitle.Complete="Natapos na ang pagsusuri" +Basic.AutoConfig.TestPage.TestingBandwidth="Nagsasagaw ng bandwidth test, maaaring itong magtagal ng ilang minuto..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Kumukonekta sa: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Bigong maka-konekta sa kahit anong mga serber, mangyari lamang suriin ang iyong koneksyon sa internet at subukan muli." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Sinusuri ang bandwidth para sa: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Sinusuri ang encoder ng stream, maaari itong magtagal ng isang minuto..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Sinusuri ang encoder para sa pagrekord, maaari itong magtagal ng isang minuto..." +Basic.AutoConfig.TestPage.TestingRes="Sinusuri ang mga resolusyon, maaari itong magtagal ng ilang minuto..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Bigong mapatakbo ang encoder" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Sinusuri ang %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Encoder para sa Pag-stream" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Encoder para sa Pagrekord" +Basic.AutoConfig.TestPage.Result.Header="Napagtanto ng programa na ang mga setting na ito ang pinakamainam para sa iyo:" +Basic.AutoConfig.TestPage.Result.Footer="Upang magamit ang mga setting, pindutin ang Apply Settings. Upang ma-configure muli ang wizard at subukang muli, pindutin ang Back. Upang ma-configure mo mismo nang mano-mano ang mga setting, pindutin ang Cancel at buksan ang mga Setting." + +Basic.Stats="Mga Statistika" +Basic.Stats.CPUUsage="Paggamit ng CPU" +Basic.Stats.HDDSpaceAvailable="Puwang sa HDD na magagamit pa" +Basic.Stats.MemoryUsage="Paggamit ng Memorya" +Basic.Stats.AverageTimeToRender="Karaniwang bilis upang ma-render ang frame" +Basic.Stats.SkippedFrames="Mga nalaktawang imahe dahil sa antala sa pag-encode" +Basic.Stats.MissedFrames="Mga imaheng di nakuha dahil sa antala sa pag-render" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Pag-rerekord" +Basic.Stats.Status="Estado" +Basic.Stats.Status.Recording="Pagrerekord" +Basic.Stats.Status.Live="LIVE" +Basic.Stats.Status.Reconnecting="Muling kumukunekta" +Basic.Stats.Status.Inactive="Hindi aktibo" +Basic.Stats.DroppedFrames="Mga Imaheng hindi sinali (Network)" +Basic.Stats.MegabytesSent="Kabuuan ng Output ng mga Datos" +Basic.Stats.Bitrate="Bitrate" + +Updater.Title="Mga bagong update na magagamit" +Updater.Text="Mayroong bagong update na magagamit:" +Updater.UpdateNow="Iupdate na Ngayon" +Updater.RemindMeLater="Paalalahanan ako Maya-maya" +Updater.Skip="Laktawan ang Bersyon" +Updater.Running.Title="Ang programa ay kasalukuyang aktibo" +Updater.Running.Text="Ang mga output ay kasalukuyang aktibo, mangyari lamang i-shut down ang anumang mga output na aktibo bago subukang mag-update" +Updater.NoUpdatesAvailable.Title="Walang mga update na magagamit" +Updater.NoUpdatesAvailable.Text="Walang mga update ang kasalukuyang magagamit" +Updater.FailedToLaunch="Bigong malunsad ang updater" +Updater.GameCaptureActive.Title="Ang Game capture ay aktibo" +Updater.GameCaptureActive.Text="Ang hook library ng game capture ay kasalukuyang ginagamit. Mangyari lamang isara ang anumang mga laro/programang nahuli (o i-start muli ang windows) at subukan muli." + +QuickTransitions.SwapScenes="Pagpalitin ang Preview/Output ng mga Eksena Matapos ang Pag-transisyon" +QuickTransitions.SwapScenesTT="Pinagpapalit ang preview at output ng mga eksena matapos ang pagtransisyon (kung ang orihinal na eksena ng output ay nariyan pa).\nHindi nito mapapawalang-bisa ang mga pagbabagong napatupad sa orihinal na eksena ng output." +QuickTransitions.DuplicateScene="Gayahin ang Eksena" +QuickTransitions.DuplicateSceneTT="Kapag nag-eedit ng kaparehong eksena, pinapahintulutan ang pag-edit ng transform/visibility ng mga pinanggalingan nang hindi binabago ang output.\nUpang ma-edit ang mga katangian ng mga pinagmulan nang hindi binabago ang output, paganahin ang 'Duplicate Sources'.\nAng pag-bago ng value na ito ang magseset muli ng pangkasalukuyang output na eksena (kung ito ay nariyan pa)." +QuickTransitions.EditProperties="Gayahin ang mga Source" +QuickTransitions.EditPropertiesTT="Kapag nag-eedit ng kaparehong eksena, pinapahintulutan ang pag-edit ng mga pinagmulan nang hindi binabago ang output.\nMaaari lamang itong gamitin kung ang 'Duplicate Scene' ay gumagana.\nMayroong mga source (tulad ng capture o mga media source) na hindi hindi ito sinusuportahan at hindi maaaring i-edit nang nakahiwalay.\nAng pagbabago ng value na ito ang magseset muli ng kasalukuyang output na eksena (kung nariyan pa ito).\n\nBabala: Dahil kokopyahin ang mga source, maaari itong mangailangan ng dagdag na system o mga video source." +QuickTransitions.HotkeyName="Mabilis na Transisyon: %1" + +Basic.AddTransition="Idagdag ang Configurable na Transisyon" +Basic.RemoveTransition="Alisin ang Configurable na Transisyon" +Basic.TransitionProperties="Mga Katangian ng Transisyon" +Basic.SceneTransitions="Mga Transisyon ng Eksena" +Basic.TransitionDuration="Tagal" +Basic.TogglePreviewProgramMode="Studio Mode" + +TransitionNameDlg.Text="Mangyari lamang ilagay ang pangalan ng transisyon" +TransitionNameDlg.Title="Pangalan ng Transition" + +TitleBar.Profile="Profile" +TitleBar.Scenes="Mga Eksena" + +NameExists.Title="Ang pangalan ay umiiral na" +NameExists.Text="Ang pangalan na ito ay ginagamit na." + +NoNameEntered.Title="Mangyari lamang magbigay ng balidong pangalan" +NoNameEntered.Text="Hindi maaaring walang pangalan." + +ConfirmStart.Title="Umpisahan na ang pag-stream?" +ConfirmStart.Text="Sigurado ka bang gusto mo nang simulan ang pag-stream?" + +ConfirmStop.Title="Itigil ang pag-stream?" +ConfirmStop.Text="Sigurado ka bang gusto mong itigil ang pag-stream?" + +ConfirmExit.Title="Lumabas sa OBS?" +ConfirmExit.Text="Ang OBS ang kasalukuyang aktibo. Ang lahat ng pag-stream/pagrerekord ay magsasara. Sigurado ka bang gusto mong lumabas?" + +ConfirmRemove.Title="Kumpirmahin ang pagtanggal" +ConfirmRemove.Text="Sigurado ka bang gusto mong tanggalin ang '$1\"?" +ConfirmRemove.TextMultiple="Sigurado ka bang gusto mong tanggalin ang %1 na mga item?" + +Output.StartStreamFailed="Bigong masimulan ang pag-stream" +Output.StartRecordingFailed="Bigong masimulan ang pag-rerekord" +Output.StartReplayFailed="Bigong masimulan ang replay buffer" +Output.StartFailedGeneric="Bigong masimulan ang output. Mangyari lamang tingnan ang log para sa mga detalye. \n\nTandaan: Kung ikaw ay gumagamit ng NVENC o AMD na mga encoder, siguraduhing ang iyong mga video driver ay naka-update." + +Output.ConnectFail.Title="Bigong kumonekta" +Output.ConnectFail.BadPath="Hindi wasto ang Path o Connection URL. Mangyari lamang tingnan ang iyong mga setting upang makumpirma na sila ay balido." +Output.ConnectFail.ConnectFailed="Bigong kumonekta sa serber" +Output.ConnectFail.InvalidStream="Hindi madaanan ang tinutukoy na channel o stream key, mangyari lamang tingnan muli ang iyong stream key. Kung ito ay wasto, maaaring mayroong problema sa pagkonekta sa serber." +Output.ConnectFail.Error="Isang di-inaasahang error ang naganap habang sinusubukang kumonekta sa serber. Karagdagang impormasyon ay nasa log file." +Output.ConnectFail.Disconnected="Nadiskonek mula sa serber." + +Output.RecordFail.Title="Bigong masimulan ang pa-rerekord" +Output.RecordFail.Unsupported="Ang output format ay maaaring hindi suportado o di kaya'y hindi nagsusuporta ng higit sa isang audio track. Mangyari lamang tingnan ang iyong mga setting at subukan muli." +Output.RecordNoSpace.Title="Hindi sapat ang espasyo sa disk" +Output.RecordNoSpace.Msg="Hindi sapat ang espasyo sa disk upang magpatuloy sa pag-rerekord." +Output.RecordError.Title="Error sa Pag-rerekord" +Output.RecordError.Msg="Isang hindi matukoy na error ang naganap habang nag-rerekord." +Output.ReplayBuffer.NoHotkey.Title="Walang set ng hotkey!" +Output.ReplayBuffer.NoHotkey.Msg="Walang naka-save na hotkey set para sa replay buffer. Mangyari lamang magtalaga ng \"Save\" hotkey na gagamitin para sa pag-save ng mga replay recording." + +Output.BadPath.Title="Maling File Path" +Output.BadPath.Text="Ang na-configure na output path ay hindi wasto. Mangyari lamang tingnan ang iyong mga setting upang makumpirma na isang balidong file path ay nakatalaga." + +LogReturnDialog="Matagumpay na na-upload ang log" +LogReturnDialog.CopyURL="Kopyahin ang URL" +LogReturnDialog.ErrorUploadingLog="Error sa pag-uupload ng log file" + +LicenseAgreement="Kasunduan sa Lisensya" +LicenseAgreement.PleaseReview="Mangyari lamang basahin ang mga termino sa lisensya bago gamitin ang OBS. Sa paggamit ng programang ito, iyong kinikilala na binasa at sumasang-ayon ka sa mga termino ng GNU General Public License v2.0. Paki-scroll down upang makita ang iba pang bahagi ng kasunduan." +LicenseAgreement.ClickIAgreeToContinue="Kung iyong tinatanggap ang mga termino ng kasunduan, pindutin ang I Agree upang makapagpatulay. Kailangan mong sumang-ayon sa kasunduan upang magamit ang OBS." +LicenseAgreement.IAgree="Sumasang-ayon Ako" +LicenseAgreement.Exit="Lumabas" + +Remux.SourceFile="OBS Recording" +Remux.TargetFile="Target File" +Remux.Remux="Remux" +Remux.OBSRecording="OBS Recording" +Remux.FinishedTitle="Tapos na ang pag-remux" +Remux.Finished="Na-remux na ang recording" +Remux.FinishedError="Na-remux na ang recording, ngunit ay file ay maaaring hindi kumpleto" +Remux.SelectRecording="Pumili ng OBS Recording …" +Remux.SelectTarget="Pumili ng target file …" +Remux.FileExistsTitle="Ang target file ay umiiral na" +Remux.FileExists="Ang target file na ito ay umiiral na, gusto ma ba itong palitan?" +Remux.ExitUnfinishedTitle="Ang pagremux ay tinutuloy pa" +Remux.ExitUnfinished="Hindi pa tapos ang pag-remux. Kung ihihinto ito ngayon, maaaring hindi na magagamit ang target file.\nSigurado ka bang gusto mong ihinto ang pagremux?" + +UpdateAvailable="May Bagong Update na Magagamit" +UpdateAvailable.Text="Ang bersyon %1.%2.%3 ay maaari nang gamiting ngayon. Pindutin ito upang ma-download" + +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="Eksena" +Basic.DisplayCapture="Ipakita ang Kuha" + +Basic.Main.PreviewConextMenu.Enable="Paganahin ang Preview" + +ScaleFiltering="I-scale ang pag-fifilter" +ScaleFiltering.Point="Punto" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" + +Deinterlacing="Deinterlacing" +Deinterlacing.Discard="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="Itaas ang Patlang Una" +Deinterlacing.BottomFieldFirst="Ibaba ang Patlang Una" + +VolControl.SliderUnmuted="Dami ng islayder para sa '%1':%2" +VolControl.SliderMuted="Dami ng islayder para sa '%1':%2 (na kasalukuyang mahina)" +VolControl.Mute="Mahina '%1'" +VolControl.Properties="Ari-arian na para sa '%1'" + +Basic.Main.AddSceneDlg.Title="Idagdag sa Eksena" +Basic.Main.AddSceneDlg.Text="Pakiusap idagdag ang pangalan ng mga eksena" + +Basic.Main.DefaultSceneName.Text="Eksena %1" + +Basic.Main.AddSceneCollection.Title="Idagdag ang Eksena sa Koleksyon" +Basic.Main.AddSceneCollection.Text="Mangyari lamang ilagay ang pangalan ng koleksyon ng mga eksena" + +Basic.Main.RenameSceneCollection.Title="Palitan ang pangalan ng Koleksyon ng mga Eksena" + +AddProfile.Title="Idagdag ang Profile" +AddProfile.Text="Mangyari lamang ilagay ang pangalan ng profile" + +RenameProfile.Title="Palitan ang pangalan ng Profile" + +Basic.Main.MixerRename.Title="Palitan ang pangalan ng Audio Source" +Basic.Main.MixerRename.Text="Mangyari lamang ilagay ang pangalan ng audio source" + + +Basic.Main.PreviewDisabled="Ang preview ay kasalukuyang hindi gumagana" + +Basic.SourceSelect="Lumikha/Pumili ng Source" +Basic.SourceSelect.CreateNew="Lumikha ng bago" +Basic.SourceSelect.AddExisting="Idagdag ang Umiiral na" +Basic.SourceSelect.AddVisible="Palitawin ang source" + +Basic.PropertiesWindow="Mga Katangian para sa '%1'" +Basic.PropertiesWindow.AutoSelectFormat="%1 (autoselect: %2)" +Basic.PropertiesWindow.SelectColor="Pumili ng Kulay" +Basic.PropertiesWindow.SelectFont="Pumili ng font" +Basic.PropertiesWindow.ConfirmTitle="Binago ang mga Setting" +Basic.PropertiesWindow.Confirm="Mayroong mga pagbabagong hindi na-save. Nais mo bang panatilihin ang mga ito?" +Basic.PropertiesWindow.NoProperties="Walang mga katangiang magagamit" +Basic.PropertiesWindow.AddFiles="Idagdag ang mga File" +Basic.PropertiesWindow.AddDir="Idagdag ang Direktory" +Basic.PropertiesWindow.AddURL="Idagdag ang Path/URL" +Basic.PropertiesWindow.AddEditableListDir="Idagdag ang directory sa '%1'" +Basic.PropertiesWindow.AddEditableListFiles="Idagdag ang mga file sa '%1'" +Basic.PropertiesWindow.AddEditableListEntry="Magdagdag ng entry sa '%1'" +Basic.PropertiesWindow.EditEditableListEntry="Baguhin ang entry mula sa '%1'" + +Basic.PropertiesView.FPS.Simple="Simpleng mga FPS Value" +Basic.PropertiesView.FPS.Rational="Rasyonal na mga FPS Value" +Basic.PropertiesView.FPS.ValidFPSRanges="Balidong mga FPS Range:" + +Basic.InteractionWindow="Nakikipag-interact sa '%1'" + +Basic.StatusBar.Reconnecting="Na-diskonek, muling magku-kunek sa loob ng %2 segundo(mga) (pagtangka%1)" +Basic.StatusBar.AttemptingReconnect="Sinusubukang uling maka-konek... (tangka %1)" +Basic.StatusBar.ReconnectSuccessful="Matagumpay na muling naka-konek" +Basic.StatusBar.Delay="Antala (%1 segundo)" +Basic.StatusBar.DelayStartingIn="Antala (magsisimula sa loob ng %1 segundo)" +Basic.StatusBar.DelayStoppingIn="Antala (hihinto sa loob ng %1 segundo)" +Basic.StatusBar.DelayStartingStoppingIn="Antala (hihinto sa loob ng %1 segundo, magsisimula sa loob ng %2 segundo)" + +Basic.Filters="Mga Filter" +Basic.Filters.AsyncFilters="Audio/Video na mga Filter" +Basic.Filters.AudioFilters="Audio na mga Filter" +Basic.Filters.EffectFilters="Efeect na mga Filter" +Basic.Filters.Title="Mga Filter para sa '%1'" +Basic.Filters.AddFilter.Title="Pangalan ng Filter" +Basic.Filters.AddFilter.Text="Mangyari lamang tukuyin ang pangalan ng filter" + +Basic.TransformWindow="Pagbabago ng mga Bagay sa Eksena" +Basic.TransformWindow.Position="Posisyon" +Basic.TransformWindow.Rotation="Pag-ikot" +Basic.TransformWindow.Size="Sukat" +Basic.TransformWindow.Alignment="Pagkahanay-hanay ng mga posisyon" +Basic.TransformWindow.BoundsType="Tipo ng Nakagagapos na Kahon" +Basic.TransformWindow.BoundsAlignment="Pagkakahanay sa Bounding Kahon" +Basic.TransformWindow.Bounds="Kahon Sukat ng Bounding" +Basic.TransformWindow.Crop="I-krop" + +Basic.TransformWindow.Alignment.TopLeft="Itaas sa kaliwa" +Basic.TransformWindow.Alignment.TopCenter="Itaas sa Gitna" +Basic.TransformWindow.Alignment.TopRight="Itaas sa Kanan" +Basic.TransformWindow.Alignment.CenterLeft="Gitna sa Kaliwa" +Basic.TransformWindow.Alignment.Center="Gitna" +Basic.TransformWindow.Alignment.CenterRight="Gitna sa Kanan" +Basic.TransformWindow.Alignment.BottomLeft="Baba sa Kaliwa" +Basic.TransformWindow.Alignment.BottomCenter="Baba sa Gitna" +Basic.TransformWindow.Alignment.BottomRight="Baba sa Kanan" + +Basic.TransformWindow.BoundsType.None="Walang Hangganan" +Basic.TransformWindow.BoundsType.MaxOnly="Pinakamataas na sukat lamang" +Basic.TransformWindow.BoundsType.ScaleInner="Panloob na hangganan ng scale" +Basic.TransformWindow.BoundsType.ScaleOuter="Panlabas na hangganan ng scale" +Basic.TransformWindow.BoundsType.ScaleToWidth="Lapad ng hangganan ng scale" +Basic.TransformWindow.BoundsType.ScaleToHeight="Taas ng hangganan ng scale" +Basic.TransformWindow.BoundsType.Stretch="Kahabaan sa hangganan" + +Basic.Main.AddSourceHelp.Title="Hindi pwede idagdag sa Pinagmulan" +Basic.Main.AddSourceHelp.Text="Kailangan mo na magkaroon ng hindi bababa sa isang eksena na idadagdag sa pinagmulan." + +Basic.Main.Scenes="Eksena" +Basic.Main.Sources="Pinagmulan" +Basic.Main.Controls="Mga kontrol" +Basic.Main.Connecting="Pagkonekta..." +Basic.Main.StartRecording="Magsimula sa Pagtatala" +Basic.Main.StartReplayBuffer="Magsimula Mag replay Buffer" +Basic.Main.StartStreaming="Magsimula na mag Streaming" +Basic.Main.StopRecording="Huminto sa Pagtatala" +Basic.Main.StoppingRecording="Pagtigil sa Pagtatala..." +Basic.Main.StopReplayBuffer="Huminto Mag-replay Buffer" +Basic.Main.StoppingReplayBuffer="Pagtigil Mag-replay Buffer..." +Basic.Main.StopStreaming="Ihinto Mag-streaming" +Basic.Main.StoppingStreaming="Pagtigil ng Daloy..." +Basic.Main.ForceStopStreaming="Itigil ang pag-stream (tanggalin ang antala)" + +Basic.MainMenu.File="&File" +Basic.MainMenu.File.Export="&I-export" +Basic.MainMenu.File.Import="&I-angkat" +Basic.MainMenu.File.ShowRecordings="Ipakita ang mga &Recording" +Basic.MainMenu.File.Remux="Re&mux na mga Recording" +Basic.MainMenu.File.Settings="&Mga Setting" +Basic.MainMenu.File.ShowSettingsFolder="Ipakita ang Folder ng mga Setting" +Basic.MainMenu.File.ShowProfileFolder="Ipakita ang Profile Folder" +Basic.MainMenu.AlwaysOnTop="&Palaging Nasa Tuktok" +Basic.MainMenu.File.Exit="E&xit" + +Basic.MainMenu.Edit="I&edit" +Basic.MainMenu.Edit.Undo="Ipawalang-bisa (&U)" +Basic.MainMenu.Edit.Redo="Gawin Muli (&R)" +Basic.MainMenu.Edit.UndoAction="Ipawalang-bisa $1 (&U)" +Basic.MainMenu.Edit.RedoAction="Gawing Muli $1 (&R)" +Basic.MainMenu.Edit.LockPreview="&Naka-lock na Preview" +Basic.MainMenu.Edit.Scale="Preview &Scaling" +Basic.MainMenu.Edit.Scale.Window="Gawing Kasing-Laki ng Window" +Basic.MainMenu.Edit.Scale.Canvas="Kanbas (%1x%2)" +Basic.MainMenu.Edit.Scale.Output="Output (%1x%2)" +Basic.MainMenu.Edit.Transform="&Transform" +Basic.MainMenu.Edit.Transform.EditTransform="&Baguhin ang Transform..." +Basic.MainMenu.Edit.Transform.CopyTransform="Kopyahin ang Transform" +Basic.MainMenu.Edit.Transform.PasteTransform="I-paste ang Transform" +Basic.MainMenu.Edit.Transform.ResetTransform="&I-set muli ang Transform" +Basic.MainMenu.Edit.Transform.Rotate90CW="Iikot ng 90 degrees CW" +Basic.MainMenu.Edit.Transform.Rotate90CCW="Iikot ng 90 degrees CCW" +Basic.MainMenu.Edit.Transform.Rotate180="Iikot ng 180 degrees" +Basic.MainMenu.Edit.Transform.FlipHorizontal="Baliktarin ng &pahalang" +Basic.MainMenu.Edit.Transform.FlipVertical="Baliktarin ng &patayo" +Basic.MainMenu.Edit.Transform.FitToScreen="&Pagkasyahin sa Screen" +Basic.MainMenu.Edit.Transform.StretchToScreen="&I-stretch sa screen" +Basic.MainMenu.Edit.Transform.CenterToScreen="&I-sentro sa screen" +Basic.MainMenu.Edit.Order="&Pagkakasunod-sunod" +Basic.MainMenu.Edit.Order.MoveUp="Ilipat &Pataas" +Basic.MainMenu.Edit.Order.MoveDown="Ilipat &Pababa" +Basic.MainMenu.Edit.Order.MoveToTop="Ilipat sa &Pinaka taas" +Basic.MainMenu.Edit.Order.MoveToBottom="Ilipat sa &Pinaka baba" +Basic.MainMenu.Edit.AdvAudio="&Advanced na mga Katangian ng Audio" + +Basic.MainMenu.View="&View" +Basic.MainMenu.View.Toolbars="&Mga Toolbar" +Basic.MainMenu.View.Docks="Mga Dock" +Basic.MainMenu.View.Docks.ResetUI="I-set muli ang UI" +Basic.MainMenu.View.Docks.LockUI="I-lock UI" +Basic.MainMenu.View.Toolbars.Listboxes="&Mga Listbox" +Basic.MainMenu.View.SceneTransitions="Mga Transisyon ng S&cene" +Basic.MainMenu.View.StatusBar="&Status Bar" +Basic.MainMenu.View.Fullscreen.Interface="Interface gamit ang buong screen" + +Basic.MainMenu.SceneCollection="&Kolekysyon ng mga Scene" +Basic.MainMenu.Profile="&Profile" +Basic.MainMenu.Profile.Import="I-import ang Profile" +Basic.MainMenu.Profile.Export="I-export ang Profile" +Basic.MainMenu.SceneCollection.Import="I-import ang Koleksyon ng mga Eksena" +Basic.MainMenu.SceneCollection.Export="I-export ang Koleksyon ng mga Eksena" +Basic.MainMenu.Profile.Exists="May ganito ng Profile" +Basic.MainMenu.SceneCollection.Exists="Mayroon ng ganitong koleksyon ng mga eksena" + +Basic.MainMenu.Tools="&Mga Kasangkapan" + +Basic.MainMenu.Help="&Tulong" +Basic.MainMenu.Help.HelpPortal="Portal Para sa &Tulong" +Basic.MainMenu.Help.Website="Bisitahin ang &Website" +Basic.MainMenu.Help.Logs="&Mga Log File" +Basic.MainMenu.Help.Logs.ShowLogs="&Ipakita ang mga Log File" +Basic.MainMenu.Help.Logs.UploadCurrentLog="I-upload ang &Pangkasalukuyang Log File" +Basic.MainMenu.Help.Logs.UploadLastLog="I-upload ang Huling Log File" +Basic.MainMenu.Help.Logs.ViewCurrentLog="&Tingnan ang Pangkasalukuyang Log" +Basic.MainMenu.Help.CheckForUpdates="Maghanap ng mga Update" + +Basic.Settings.ProgramRestart="Ang programa ay kailangan i-start muli para gumana ang mga setting na ito." +Basic.Settings.ConfirmTitle="Kumpirmahin ang mga Pagbabago" +Basic.Settings.Confirm="Mayroon kang mga binago na hindi pa na-save. I-save ang mga pagbabago?" + +Basic.Settings.General="Pangkalahatan" +Basic.Settings.General.Theme="Tema" +Basic.Settings.General.Language="Lenggwahe" +Basic.Settings.General.EnableAutoUpdates="Awtomatikong maghanap ng mga update sa pag-start up" +Basic.Settings.General.OpenStatsOnStartup="Buksan ang dialogong panstatistiko sa pag-startup" +Basic.Settings.General.WarnBeforeStartingStream="Ipakita ang kumpirmasyon ng dialogo kapag nagumpisang mag-stream" +Basic.Settings.General.WarnBeforeStoppingStream="Ipakita ang kumpirmasyon ng dialogo kapag naghihinto ng pag-stream" +Basic.Settings.General.Projectors="Mga Projector" +Basic.Settings.General.HideProjectorCursor="Itago ang cursor sa ibabaw ng mga projector" +Basic.Settings.General.ProjectorAlwaysOnTop="Ilagay palagi sa tuktok ang mga projector" +Basic.Settings.General.Snapping="Pag-snap ng pagkakahanay ng source" +Basic.Settings.General.ScreenSnapping="I-snap ang mga source sa gilid ng screen" +Basic.Settings.General.CenterSnapping="I-snap ang mga source sa pahalang at patayong sentro" +Basic.Settings.General.SourceSnapping="I-snap ang mga Source sa iba pang mga source" +Basic.Settings.General.SnapDistance="I-snap ang pagka-sensitibo" +Basic.Settings.General.RecordWhenStreaming="Awtomatikong mag-rekord kapag nag-stream" +Basic.Settings.General.KeepRecordingWhenStreamStops="Patuloy na mag-rekord kapag tumigil ang pag-stream" +Basic.Settings.General.ReplayBufferWhileStreaming="Awtomatikong simulang ang replay buffer kapag nag-stream" +Basic.Settings.General.KeepReplayBufferStreamStops="Panatilihing aktibo ang replay buffer kapag huminto ang pagstream" +Basic.Settings.General.SysTray="System Tray" +Basic.Settings.General.SysTrayWhenStarted="Paliitin sa system tray kapag inumpisahan" +Basic.Settings.General.SystemTrayHideMinimize="Palaging paliitin sa system tray sa halip na task bar" +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.Stream="Mag-stream" +Basic.Settings.Stream.StreamType="Uri ng Pag-stream" + +Basic.Settings.Output="Output" +Basic.Settings.Output.Format="Format ng Recording" +Basic.Settings.Output.Encoder="Encoder" +Basic.Settings.Output.SelectDirectory="Pumili ng Recording Directory" +Basic.Settings.Output.SelectFile="Pumili ng Recording File" +Basic.Settings.Output.EnforceBitrate="Ipatupad ang mga limitasyon sa serbisyo ng bitrate sa pag-stream" +Basic.Settings.Output.Mode="Paraan ng Output" +Basic.Settings.Output.Mode.Simple="Payak" +Basic.Settings.Output.Mode.Adv="Mas Mahusay" +Basic.Settings.Output.Mode.FFmpeg="FFmpeg Output" +Basic.Settings.Output.UseReplayBuffer="Paganahin ang Replay Buffer" +Basic.Settings.Output.ReplayBuffer.SecondsMax="Pinakamataas na Oras ng Replay (Segundo)" +Basic.Settings.Output.ReplayBuffer.MegabytesMax="Pinakamataas na Memorya (Megabytes)" +Basic.Settings.Output.ReplayBuffer.Estimate="Tantiyang gamit sa memorya: %1 MB" +Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Hindi matantiya ang gamit sa memorya. Mangyari lamang mag-takda ng pinakamataas na limitasyon sa memorya." +Basic.Settings.Output.ReplayBuffer.HotkeyMessage="(Tandaan: Siguruhing nakapagtakda ka ng hotkey para sa replay buffer sa hotkeys section)" +Basic.Settings.Output.ReplayBuffer.Prefix="Panlapi para Filename ng Replay Buffer" +Basic.Settings.Output.ReplayBuffer.Suffix="Hulapi" +Basic.Settings.Output.Simple.SavePath="Landas ng Recording" +Basic.Settings.Output.Simple.RecordingQuality="Kalidad ng Recording" +Basic.Settings.Output.Simple.RecordingQuality.Stream="Katulad ng pag-stream" +Basic.Settings.Output.Simple.RecordingQuality.Small="Mataas na Kalidad, Katamtamang Laki ng File" +Basic.Settings.Output.Simple.RecordingQuality.HQ="Hindi Matukoy na Kalidad, Malaking File" +Basic.Settings.Output.Simple.RecordingQuality.Lossless="Walang nabago sa Kalidad, Lubhang napakalaking File" +Basic.Settings.Output.Simple.Warn.VideoBitrate="Babala: Ang video bitrate ng pag-stream ay itatakda sa %1, na siyang mataas na limitasyon para sa pangkasalukuyang serbisyo ng pagstream. Kung sigurado kang nais mong lumagpas sa %1, paganahin ang advanced encoder options at tanggalin ang check sa \"Enforce streaming bitrate limits\"." +Basic.Settings.Output.Simple.Warn.AudioBitrate="Babala: Ang audio bitrate ng pag-stream ay itatakda sa %1, na siyang mataas na limitasyon para sa pangkasalukuyang serbisyo ng pag-strean. Kung sigurado kang nais mong lumagpas sa %1, paganahin ang advanced encoder options at tanggalin ang check sa \"Enforce streaming service bitrate limits\"." +Basic.Settings.Output.Simple.Warn.Encoder="Babal: Ang pag-rekord gamit ang isang software encoder na iba ang kalidad sa pag-stream ay mangangailangan ng dagdag na pag-gamit sa CPU kung ikaw ay mag-stream at mag-rekord nang sabay." +Basic.Settings.Output.Simple.Warn.Lossless="Babala: Ang kalidad na lossless ay nagbibigay ng lubhang malalaking file. Ang kalidad na lossless ay maaaring gumamit ng 7 gigabytes ng espasyo ng disk kada minuto o higit pa sa matataas na mga resolusyon o mga framerate. Ang lossless ay hindi minumungkahi para sa mga mahahabang recording maliban na lamang kung mayroon kang napakalaking espasyo sa disk na magagamit." +Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sigurado ka bang gusto mong gamitin ang lossless na kalidad?" +Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless na kalidad babala!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="Babala: Hindi ka maaaring gumamit ng maraming magkahiwalay na QSV na mga encoder kapag say na nag-stream at nag-rerekord. Kung nais pagsabayin ang pag-stream at pag-rekord, mangyari lamang ibahin ang encoder para sa pag-rekord o ang encoder para sa pag-stream." +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)" +Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" +Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 mababa ang nakatakdang paggamit ng CPU, dagdagan ang laki ng file)" +Basic.Settings.Output.VideoBitrate="Video Bitrate" +Basic.Settings.Output.AudioBitrate="Audio Bitrate" +Basic.Settings.Output.Reconnect="Pakusa Makipagkonek muli" +Basic.Settings.Output.RetryDelay="Subukan muling Maantala (Mga segundo)" +Basic.Settings.Output.MaxRetries="Pinakamaraming Retries" +Basic.Settings.Output.Advanced="Paganahin Pauna ang Enkoder Settings" +Basic.Settings.Output.EncoderPreset="Pangkasalukuyan enkoder (Nakatataas = Mas mababa CPU)" +Basic.Settings.Output.CustomEncoderSettings="Pasadyang enkoder Mga setting" +Basic.Settings.Output.CustomMuxerSettings="Pasadyang mga Setting ng Muxer" +Basic.Settings.Output.NoSpaceFileName="Gumawa ng Pangalan ng File nang walang Pagitan" + +Basic.Settings.Output.Adv.Rescale="I-re-iskala ang Output" +Basic.Settings.Output.Adv.AudioTrack="Pangsubaybay ng Audio" +Basic.Settings.Output.Adv.Streaming="Anod" +Basic.Settings.Output.Adv.ApplyServiceSettings="I-enpors ang anod ng serbisyo ng mga enkoder seting" +Basic.Settings.Output.Adv.Audio.Track1="Subaybayan 1" +Basic.Settings.Output.Adv.Audio.Track2="Subaybayan 2" +Basic.Settings.Output.Adv.Audio.Track3="Subaybayan 3" +Basic.Settings.Output.Adv.Audio.Track4="Subaybayan 4" +Basic.Settings.Output.Adv.Audio.Track5="Subaybayan 5" +Basic.Settings.Output.Adv.Audio.Track6="Subaybayan 6" + +Basic.Settings.Output.Adv.Recording="Pagtatala" +Basic.Settings.Output.Adv.Recording.Type="Uri" +Basic.Settings.Output.Adv.Recording.Type.Standard="Pamantayan" +Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Pasadyang Palabas (FFmpeg)" +Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Gamitin ang stream encoder)" +Basic.Settings.Output.Adv.Recording.Filename="Pangalan ng File ng Pag-format" +Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Higit na pasulat kung ang file ay umiiral" +Basic.Settings.Output.Adv.FFmpeg.Type="Ang Palabas na Uri ng FFmpeg" +Basic.Settings.Output.Adv.FFmpeg.Type.URL="Output sa URL" +Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Output sa File" +Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="Mga Karaniwang format sa pagrekord" +Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Lahat ng mga File" +Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Landas ng file o URL" +Basic.Settings.Output.Adv.FFmpeg.Format="Format ng Container" +Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Audio" +Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Video" +Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Default na Format" +Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Paglalarawan ng Container Format" +Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Audio/Video Codec na hinulaan mula sa landas ng File o URL" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Default Encoder" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Huwag paganahin ang Encoder" +Basic.Settings.Output.Adv.FFmpeg.VEncoder="Video Encoder" +Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Mga Setting para sa Video Encoder (kung mayroon)" +Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audio Encoder" +Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Mga Setting ng Audio Encoder(kung mayroon)" +Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Mga Setting ng Muxer (kung mayroon)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Agwat ng Keyframe (mga imahe)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Ipakita ang lahat ng mga code (kahit na maaaring hindi akma)" + +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 Taon, apat na tambilang\n%YY Taon, huling dalawang tambilang (00-99)\n%MM Buwan bilang isang decimal na numero (01-12)\n%DD Araw ng buwan, zero-padded (01-31)\n%hh Oras sa 24h na format (00-23)\n%mm Minuto(00-59)\n%ss Segundo (00-61)\n%% A % sign\n%a Pina-ikling ngalan ng araw\n%A Buong ngalan ng araw\n%b Pinaikling ngalan ng buwan\n%B Buong ngalan ng buwan\n%d Araw ng Buwan, zero-padded (01-31)\n%H Oras sa 24h na format (00-23)\n%I Oras sa 12h na format (01-12)\n%m Buwang bilang isang decimal na numero (01-12)\n%M Minuto (00-59)\n%p AM o PM na paghirang\n%S Segundo (00-61)\n%y Taon, huling dalawang tambilang (00-99)\n%Y Taon\n%z ISO 8601 kanselahin mula sa UTC o timezone\n pangalan ng pinaikli \n%Z Ngalan ng Timezone o abbreviation\n" + +Basic.Settings.Video="Video" +Basic.Settings.Video.Adapter="Video Adapter" +Basic.Settings.Video.BaseResolution="Resolusyon ng Base (Kanbas)" +Basic.Settings.Video.ScaledResolution="Resolution ng Output (Na-scale)" +Basic.Settings.Video.DownscaleFilter="Pababaan ang Filter" +Basic.Settings.Video.DisableAeroWindows="Huwag paganahin ang Aero (Windows lamang)" +Basic.Settings.Video.FPS="FPS" +Basic.Settings.Video.FPSCommon="Karaniwang mga FPS Value" +Basic.Settings.Video.FPSInteger="Integer ng FPS Value" +Basic.Settings.Video.FPSFraction="Praksyonal na FPS Value" +Basic.Settings.Video.Numerator="Numerador" +Basic.Settings.Video.Denominator="Denominador" +Basic.Settings.Video.Renderer="Renderer" +Basic.Settings.Video.InvalidResolution="Walang bisa ang value ng resolusyon. Kailangang [width]x[height] (hal. 1920x1080)" +Basic.Settings.Video.CurrentlyActive="Kasalukuyang aktibo ang video output. Mangyari lamang patayin ang anumang mga output upang mabago ang mga video setting." +Basic.Settings.Video.DisableAero="Huwag paganahin ang Aero" + +Basic.Settings.Video.DownscaleFilter.Bilinear="Bilinear (Pinakamabilis, ngunit hindi malinaw pag nag-scale)" +Basic.Settings.Video.DownscaleFilter.Bicubic="Bicubic (Matalas na pag-scale, 16 na mga halimbawa)" +Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Pinatalas na pag-scale, 32 na mga halimbawa)" + +Basic.Settings.Audio="Audio" +Basic.Settings.Audio.SampleRate="Sample Rate" +Basic.Settings.Audio.Channels="Mga Channel" +Basic.Settings.Audio.MeterDecayRate="Audio Meter Decay Rate" +Basic.Settings.Audio.MeterDecayRate.Fast="Mabilis" +Basic.Settings.Audio.MeterDecayRate.Medium="Medium (Type I PPM)" +Basic.Settings.Audio.MeterDecayRate.Slow="Mabagal (Type II PPM)" +Basic.Settings.Audio.MultiChannelWarning.Enabled="BABALA: Naka-enable ang surround sound audio." +Basic.Settings.Audio.MultichannelWarning="Kung nagsi-stream, tingnan kung ang streaming service ay sinusuportahan ang parehong surround sound ingest pat ang surround sound playback. Ang Twitch, Facebook 360 Live, Mixer RTMP, Smashcast ay mga halimbawa kung saan ang surround sound ay sinusuportahan ng buo. Kahit na ang Facebook Live at YouTube live ay parehong tumatanggap ng surround ingest, ang Facebook Live ay nagda-downmix sa stereo, at ang YouTube Live ay nagpapakita lamang ng dalawang channel. \n\nAng OBS audio na mga filter ay akma sa surround sound, pero ang suporta sa VST plugin ay hindi garantisado." +Basic.Settings.Audio.MultichannelWarning.Title="Mapapagana ba ang tunog ng palibot ng audio?" +Basic.Settings.Audio.MultichannelWarning.Confirm="Sigurado ka ba na gusto mong paganahin ang tunog ng palibot ng audio?" +Basic.Settings.Audio.DesktopDevice="Apatarong Desktop Awdiyo" +Basic.Settings.Audio.DesktopDevice2="Aparatong Desktop Awdiyo 2" +Basic.Settings.Audio.AuxDevice="Mic/Katulong na Aparato ng Awdiyo" +Basic.Settings.Audio.AuxDevice2="Mic/Katulong na Aparato ng Awdiyo 2" +Basic.Settings.Audio.AuxDevice3="Mic/Katulong na Aparato ng Awdiyo 3" +Basic.Settings.Audio.EnablePushToMute="Paganahin Itulak-para-ma-i-mute" +Basic.Settings.Audio.PushToMuteDelay="Pag-antala sa pagtulak-sa-walang tunog" +Basic.Settings.Audio.EnablePushToTalk="Paganahin itulak-sa-usapan" +Basic.Settings.Audio.PushToTalkDelay="Pag-antala sa pagtulak-sa-usapan" +Basic.Settings.Audio.UnknownAudioDevice="[Ang aparato ay hindi nakakonekta o hindi magagamit]" + +Basic.Settings.Advanced="Pauna" +Basic.Settings.Advanced.General.ProcessPriority="Prayoridad na Pagproseso" +Basic.Settings.Advanced.General.ProcessPriority.High="Mataas" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Sa Taas ng Karaniwan" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Karaniwan" +Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Sa ibaba ng karaniwan" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Walang ginagawa" +Basic.Settings.Advanced.FormatWarning="Babala: Ang mga format ng mga kulay maliban sa NV12 ay ginawa unsa lahat para sa pagrerekord, at hindi nirerekomenda para sa pag-stream. Ang pag-stream ay maaaring magdulot ng dagdag na gamit sa CPU dahil sa pagbago ng format ng kulay." +Basic.Settings.Advanced.Audio.BufferingTime="Bilis ng Pag-buffer ng Audio" +Basic.Settings.Advanced.Video.ColorFormat="Fomat ng Kulay" +Basic.Settings.Advanced.Video.ColorSpace="YUV Espasyo ng Kulay" +Basic.Settings.Advanced.Video.ColorRange="YUV Hanay ng Kulay" +Basic.Settings.Advanced.Video.ColorRange.Partial="Panguna" +Basic.Settings.Advanced.Video.ColorRange.Full="Buo" +Basic.Settings.Advanced.Audio.MonitoringDevice="Kagamitang Pangsubaybay ng Audio" +Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Default" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Huwag paganahin ang audio ducking ng Windows" +Basic.Settings.Advanced.StreamDelay="Antala sa Pag-stream" +Basic.Settings.Advanced.StreamDelay.Duration="Bilis (segundo)" +Basic.Settings.Advanced.StreamDelay.Preserve="Balikan kung saan huling pinutol (dagadagan ang antala) pagka konekta muli" +Basic.Settings.Advanced.StreamDelay.MemoryUsage="Tantiyang Gamit sa Memorya: %1 MB" +Basic.Settings.Advanced.Network="Network" +Basic.Settings.Advanced.Network.BindToIP="Bumigkis sa IP" +Basic.Settings.Advanced.Network.EnableNewSocketLoop="Paganahin ang bagong code ng pag-network" +Basic.Settings.Advanced.Network.EnableLowLatencyMode="Mode para sa low latency" + +Basic.AdvAudio="Pinahusay na mga Katangian ng Audio" +Basic.AdvAudio.Name="Pangalan" +Basic.AdvAudio.Volume="Lakas ng tunog (%)" +Basic.AdvAudio.Mono="I-downmix para maging Mono" +Basic.AdvAudio.Panning="Panning" +Basic.AdvAudio.SyncOffset="Sync Offset (ms)" +Basic.AdvAudio.Monitoring="Pagsubaybay sa Audio" +Basic.AdvAudio.Monitoring.None="Naka-off ang Monitor" +Basic.AdvAudio.Monitoring.MonitorOnly="Monitor Lamang (naka-mute na output)" +Basic.AdvAudio.Monitoring.Both="Monitor at Output" +Basic.AdvAudio.AudioTracks="Mga Track" + +Basic.Settings.Hotkeys="Mga Hotkey" +Basic.Settings.Hotkeys.Pair="Ang mga kombinasyon ng mga key kasama ang '%1' ay nagsisilbing mga toggle" + +Basic.Hotkeys.SelectScene="Lumipat sa eksena" + +Basic.SystemTray.Show="Ipakita" +Basic.SystemTray.Hide="Itago" + +Basic.SystemTray.Message.Reconnecting="Nadiskonekta. Kumukonekta muli..." + +Hotkeys.Insert="Isingit" +Hotkeys.Delete="Burahin" +Hotkeys.Home="Home" +Hotkeys.End="Wakas" +Hotkeys.PageUp="Itaas ng Pahina" +Hotkeys.PageDown="Ibaba ng Pahina" +Hotkeys.NumLock="Num Lock" +Hotkeys.ScrollLock="Scroll Lock" +Hotkeys.CapsLock="Caps Lock" +Hotkeys.Backspace="Backspace" +Hotkeys.Tab="Tab" +Hotkeys.Print="Ilimbag" +Hotkeys.Pause="Ihinto" +Hotkeys.Left="Kaliwa" +Hotkeys.Right="Kanan" +Hotkeys.Up="Taas" +Hotkeys.Down="Baba" +Hotkeys.Windows="Windows" +Hotkeys.Super="Super" +Hotkeys.Menu="Menu" +Hotkeys.Space="Espasyo" +Hotkeys.NumpadNum="Numpad %1" +Hotkeys.NumpadMultiply="Numpad Multiply" +Hotkeys.NumpadDivide="Numpad Divide" +Hotkeys.NumpadAdd="Numpad Add" +Hotkeys.NumpadSubtract="Numpad Subtract" +Hotkeys.NumpadDecimal="Numpad Decimal" +Hotkeys.AppleKeypadNum="%1 (Keypad)" +Hotkeys.AppleKeypadMultiply="* (Keypad)" +Hotkeys.AppleKeypadDivide="/ (Keypad)" +Hotkeys.AppleKeypadAdd="+ (Keypad)" +Hotkeys.AppleKeypadSubtract="- (Keypad)" +Hotkeys.AppleKeypadDecimal=". (Keypad)" +Hotkeys.AppleKeypadEqual="= (Keypad)" +Hotkeys.MouseButton="Mouse %1" + +Mute="I-mute" +Unmute="Ibalik ang Tunog" +Push-to-mute="Pindutin-para-i-mute" +Push-to-talk="Pindutin-para-makipagusap" + +SceneItemShow="Ipakita ang '%1'" +SceneItemHide="Itago '%1'" + +OutputWarnings.NoTracksSelected="Kailangan mong pumili ng kahit isang track" +OutputWarnings.MultiTrackRecording="Babala: May ilang mga format (tulad ng FLV) na hindi sumusuporta ng maramihang track kada isang pag-rekord" +OutputWarnings.MP4Recording="Babala: Ang mga rekording na naka-save sa MP4 ay hindi na muling mababawi kung ang file ay hindi maaaring tapusin (hal. bilang resulta ng mga BSOD, nawalan ng kuryente, at iba pa). Kung gusto mong mag-rekord ng maraming audio track, maaari mong gamitin ang MKV at i-remux ang recording sa mp4 pag natapos na ito (File->Remux Recordings)" + +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 1f8c4719d..7aee010dd 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ü" @@ -631,6 +645,13 @@ Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Keskinleştirilmiş boyut Basic.Settings.Audio="Ses" Basic.Settings.Audio.SampleRate="Örnekleme Sıklığı" Basic.Settings.Audio.Channels="Kanallar" +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?" @@ -671,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" @@ -745,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 f45890bf8..57ff3fccf 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 000000000..d0afe8d77 --- /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 b4caec9fd..763874da5 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 87fb75130..86fb2460d 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="流类型" @@ -631,6 +645,13 @@ 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型)" +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="是否启用环绕立体声?" @@ -671,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="名称" @@ -678,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="热键" @@ -745,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 3e0643752..bc2a21572 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="串流類型" @@ -631,6 +645,13 @@ 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="中 (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="是否啟用環繞聲音訊?" @@ -671,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="名稱" @@ -745,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 af3eb9070..33c627802 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; } @@ -225,7 +283,7 @@ QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add- QScrollBar:horizontal { background-color: transparent; - height: 10px; + height: 20px; margin-left: -3px; margin-right: -3px; } @@ -233,7 +291,7 @@ QScrollBar:horizontal { QScrollBar::handle:horizontal { background-color: #2f2f2f; min-width: 20px; - margin: 0px 0px -3px; + margin: 3px 0px; border-radius: 0px; border: none; } @@ -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; } @@ -774,3 +860,29 @@ FocusList::item { background: transparent; min-height: 26px; } + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: 36px; + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: 16px; + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: rgb(8, 8, 11); +} + +/* Preview background color */ + +* [themeID="displayBackgroundColor"] { + qproperty-displayBackgroundColor: #28282A; +} diff --git a/UI/data/themes/Acri/cogwheel@2x.png b/UI/data/themes/Acri/cogwheel@2x.png new file mode 100644 index 000000000..49c4070a7 Binary files /dev/null and b/UI/data/themes/Acri/cogwheel@2x.png differ diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index 4a9326c6a..7014ff340 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 */ @@ -48,7 +86,7 @@ QWidget::disabled { color: 2px solid rgb(200,199,200); /* lighter */ } -QAbstractItemView { +QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { background-color: rgb(31,30,31); /* veryDark */ } @@ -110,11 +148,9 @@ QGroupBox { } QScrollBar:vertical { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(54, 53, 54), - stop: 1 rgb(58,57,58)); /* dark */ + background-color: rgb(58,57,58); /* dark */ width: 14px; + margin: 0px; } QScrollBar::handle:vertical { @@ -141,11 +177,9 @@ QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add- } QScrollBar:horizontal { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(54, 53, 54), - stop: 1 rgb(58,57,58)); /* dark */ + background-color: rgb(58,57,58); /* dark */ height: 14px; + margin: 0px; } QScrollBar::handle:horizontal { @@ -281,6 +315,13 @@ QComboBox { padding-left: 10px; } +QComboBox:hover { + background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 rgb(111, 110, 101), + stop: 0.25 rgb(100, 99, 100), + stop: 1 rgb(88,87,88)); +} + QComboBox::drop-down { border:none; border-left: 1px solid rgba(31,30,31,155); /* veryDark */ @@ -319,7 +360,12 @@ QComboBox::down-arrow:editable { QLineEdit, QTextEdit, QPlainTextEdit { background-color: rgb(31,30,31); /* veryDark */ border: none; - padding-left: 2px; + border-radius: 3px; + padding: 2px 2px 3px 7px; +} + +OBSHotkeyWidget QLineEdit { + margin: 0px 3px 0px 0px; } @@ -328,9 +374,9 @@ QLineEdit, QTextEdit, QPlainTextEdit { QSpinBox, QDoubleSpinBox { background-color: rgb(31,30,31); /* veryDark */ border: none; - padding-left: 2px; - padding-right: 15px; - margin-right: 10px; + border-radius: 3px; + margin: 0px 3px 0px 0px; + padding: 2px 2px 3px 7px; } QSpinBox::up-button, QDoubleSpinBox::up-button { @@ -420,12 +466,16 @@ QPushButton::menu-indicator { width: 25px; } +QPushButton[themeID="hotkeyButtons"] { + margin: 1px 2px; +} + /* 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 +500,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 +540,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 +556,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 { @@ -527,6 +581,18 @@ QStatusBar::item { border: none; } +/* Table View */ + +QTableView { + gridline-color: rgb(88,87,88); /* kindaDark */ +} + +QHeaderView::section { + background-color: rgb(58,57,58); /* dark */ + color: rgb(225,224,225); /* veryLight */ + border: 1px solid rgb(31,30,31); /* veryDark */; + border-radius: 5px; +} /* Mute CheckBox */ @@ -547,6 +613,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 { @@ -573,3 +660,29 @@ QLabel#errorLabel { color: rgb(0, 192, 0); font-weight: bold; } + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: 36px; + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: 16px; + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: rgb(31, 30, 31); /* veryDark */ +} + +/* Preview background color */ + +* [themeID="displayBackgroundColor"] { + qproperty-displayBackgroundColor: rgb(76, 76, 76); +} diff --git a/UI/data/themes/Dark/cogwheel@2x.png b/UI/data/themes/Dark/cogwheel@2x.png new file mode 100644 index 000000000..49c4070a7 Binary files /dev/null and b/UI/data/themes/Dark/cogwheel@2x.png differ diff --git a/UI/data/themes/Dark/collapse.png b/UI/data/themes/Dark/collapse.png new file mode 100644 index 000000000..1fd72a116 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 000000000..dd5d2b5ec 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 0b88dd451..c5a010ab5 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; } @@ -97,3 +115,29 @@ QLabel#errorLabel { color: rgb(0, 128, 0); font-weight: bold; } + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: 36px; + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: 16px; + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: #DCD9D7; +} + +/* Preview background color */ + +* [themeID="displayBackgroundColor"] { + qproperty-displayBackgroundColor: rgb(76, 76, 76); +} diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index 5c7887cdb..62be5decb 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,25 +1106,80 @@ 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:hover { - background-color: rgb(200, 199, 200); +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::sub-page { +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; +} + +QSlider::handle:hover { + background-color: rgb(200, 199, 200); +} + QSlider::handle:disabled { background-color: rgb(122, 121, 122); } +/**********************/ +/* --- Table View --- */ +/**********************/ + +QTableView { + gridline-color: rgb(118, 121, 124); /* Light Gray */ +} + +QHeaderView::section { + background-color: rgb(35, 38, 41); /* Dark Gray */ + color: rgb(239, 240, 241); /* "White" */ + border: 1px solid rgb(118, 121, 124); /* Light Gray */ + border-radius: 2px; + padding: 4px; +} /****************/ /* --- Misc --- */ @@ -1067,8 +1206,15 @@ QFrame[frameShape="0"] { border: 1px transparent; } - /* 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; } @@ -1082,3 +1228,29 @@ QToolTip { background-color: rgb(49, 54, 59); /* Blue-gray */ color: rgb(240, 98, 146); /* Pink (Secondary) */ } + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: 36px; + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: 16px; + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: rgb(35, 38, 41); /* Dark Gray */ +} + +/* Preview background color */ + +* [themeID="displayBackgroundColor"] { + qproperty-displayBackgroundColor: rgb(35, 38, 41); +} diff --git a/UI/expand-checkbox.hpp b/UI/expand-checkbox.hpp new file mode 100644 index 000000000..375a4ce55 --- /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 000000000..b07e1f968 --- /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/OBSAbout.ui b/UI/forms/OBSAbout.ui new file mode 100644 index 000000000..53ac93b32 --- /dev/null +++ b/UI/forms/OBSAbout.ui @@ -0,0 +1,166 @@ + + + OBSAbout + + + + 0 + 0 + 792 + 389 + + + + About + + + + + 30 + 30 + 261 + 261 + + + + + + + :res/images/ebs.png + + + + + + 320 + 30 + 441 + 261 + + + + + 0 + + + + + OBS Studio + + + + + + + Version + + + + + + + About.Info + + + true + + + + + + + Contribute + + + true + + + + + + + Donate + + + + + + + Get Involved + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 320 + 791 + 71 + + + + + 0 + + + + + About + + + Qt::AlignCenter + + + + + + + Authors + + + Qt::AlignCenter + + + + + + + License + + + Qt::AlignCenter + + + + + + + + + ClickableLabel + QLabel +
clickable-label.hpp
+
+
+ + +
diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index f6a7c32ac..6c945d491 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -28,7 +28,7 @@ - :/res/images/obs.png:/res/images/obs.png + :/res/images/ebs.png:/res/images/ebs.png @@ -137,11 +137,23 @@ + + + Basic.MainMenu.Help.CrashLogs + + + + + + + + + @@ -185,7 +197,7 @@ - + Copy @@ -283,6 +295,7 @@ + @@ -304,7 +317,6 @@ Basic.MainMenu.Tools - @@ -509,7 +521,7 @@ 0 - + 0 @@ -606,63 +618,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 - - - 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 + + + @@ -962,31 +1023,6 @@ - - - - true - - - - 0 - 0 - - - - - 130 - 0 - - - - Basic.Main.StartRecording - - - true - - - @@ -1491,16 +1527,6 @@ PasteDuplicate - - - Basic.AutoConfig - - - - - Basic.AutoConfig.Beta - - Basic.Stats @@ -1582,6 +1608,37 @@ Basic.MainMenu.Help.HelpPortal + + + Basic.MainMenu.Help.CrashLogs.ShowLogs + + + + + Basic.MainMenu.Help.CrashLogs.UploadLastLog + + + + + Basic.MainMenu.Help.Discord + + + + + true + + + true + + + Basic.Stats + + + + + Basic.MainMenu.Help.About + + @@ -1595,6 +1652,12 @@ QStatusBar
window-basic-status-bar.hpp
+ + HScrollArea + QScrollArea +
horizontal-scroll-area.hpp
+ 1 +
VScrollArea QScrollArea @@ -1602,9 +1665,9 @@ 1 - SourceListWidget - QListWidget -
source-list-widget.hpp
+ SourceTree + QListView +
source-tree.hpp
diff --git a/UI/forms/OBSBasicFilters.ui b/UI/forms/OBSBasicFilters.ui index b25d5d373..572a31f9d 100644 --- a/UI/forms/OBSBasicFilters.ui +++ b/UI/forms/OBSBasicFilters.ui @@ -425,6 +425,42 @@
+ + + + :/res/images/list_remove.png:/res/images/list_remove.png + + + Remove + + + Del + + + + + + :/res/images/up.png:/res/images/up.png + + + Basic.MainMenu.Edit.Order.MoveUp + + + Ctrl+Up + + + + + + :/res/images/down.png:/res/images/down.png + + + Basic.MainMenu.Edit.Order.MoveDown + + + Ctrl+Down + + diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index ea5cfb8c8..9aaf6249b 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -145,8 +145,8 @@ 0 0 - 801 - 741 + 804 + 1072 @@ -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 @@ -704,20 +768,6 @@ - - - - - - Modifying the bandwidth manualy will not affect the quality of the stream as it is managed automaticaly by WebRTC. - - - Qt::AlignCenter - - - - - @@ -747,8 +797,8 @@ 0 0 - 603 - 640 + 813 + 770 @@ -1339,7 +1389,7 @@ - 0 + 1 true @@ -1630,285 +1680,285 @@ 0 - - - - - 0 - - - 0 + + + + + 0 + 0 + + + + + QFormLayout::AllNonFixedFieldsGrow - - 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + 0 - - + + - + 0 0 - - - QFormLayout::AllNonFixedFieldsGrow + + + 170 + 0 + + + + Basic.Settings.Output.Simple.SavePath + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + advOutRecPath + + + + + + + + + true + + + + + + + true + + + Browse + + + + + + + + + Basic.Settings.Output.NoSpaceFileName + + + true + + + + + + + Basic.Settings.Output.Format + + + advOutRecFormat + + + + + + + + flv + + + + + mp4 + + + + + mov + + + + + mkv + + + + + ts + + + + + m3u8 - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Basic.Settings.Output.Adv.AudioTrack + + + + + + + + 0 + 0 + + + + + 0 0 - - - - - 0 - 0 - - - - - 170 - 0 - - + + 0 + + + 0 + + + - Basic.Settings.Output.Simple.SavePath - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - advOutRecPath + 1 - - - - - - true - - - - - - - true - - - Browse - - - - - - - + + - Basic.Settings.Output.NoSpaceFileName - - - true + 2 - - + + - Basic.Settings.Output.Format + 3 - - advOutRecFormat - - - - - - - - flv - - - - - mp4 - - - - - mov - - - - - mkv - - - - - ts - - - - - m3u8 - - - - + + - Basic.Settings.Output.Adv.AudioTrack - - - - - - - - 0 - 0 - + 4 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - - - 2 - - - - - - - 3 - - - - - - - 4 - - - - - - - 5 - - - - - - - 6 - - - - - - + + - Basic.Settings.Output.Encoder - - - advOutRecEncoder + 5 - - - - - - - - 0 - 0 - - - - Qt::RightToLeft - + + - Basic.Settings.Output.Adv.Rescale + 6 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - false - - - true - - - - - - - - - - Basic.Settings.Output.CustomMuxerSettings + + + + + + + Basic.Settings.Output.Encoder + + + advOutRecEncoder + + + + + + + + + + + 0 + 0 + + + + Qt::RightToLeft + + + Basic.Settings.Output.Adv.Rescale + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false - - advOutMuxCustom + + true - - - + + + + Basic.Settings.Output.CustomMuxerSettings + + + advOutMuxCustom + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + @@ -3376,60 +3426,17 @@ - - - - true - - - - - 0 - 0 - 98 - 28 - - - - - - - - - color: rgb(255, 0, 4); - + + - + Basic.Settings.Audio.MeterDecayRate - - true + + meterDecayRate - - - - - - - true - - - warning - - - - - - - Basic.Settings.Audio.MeterDecayRate - - - meterDecayRate - - - - + Basic.Settings.Audio.MeterDecayRate.Fast @@ -3454,6 +3461,106 @@ + + + + Basic.Settings.Audio.PeakMeterType + + + peakMeterType + + + + + + + 0 + + + + Basic.Settings.Audio.PeakMeterType.SamplePeak + + + + + Basic.Settings.Audio.PeakMeterType.TruePeak + + + + + + + + true + + + + + 0 + 0 + 98 + 28 + + + + + + + + + + + + true + + + error + + + + + + + + + + true + + + warning + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + + + + Basic.Settings.Audio.AuxDevice4 + + + auxAudioDevice4 + + + @@ -3736,15 +3843,15 @@ 0 - - color: rgb(255, 0, 4); - true + + error + @@ -3776,7 +3883,7 @@ - + 0 @@ -3805,11 +3912,11 @@ 0 0 - 596 - 761 + 594 + 807 - + 0 @@ -3822,9 +3929,9 @@ 9 - + - + @@ -3834,6 +3941,12 @@ QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + @@ -3847,6 +3960,19 @@ + + + + Qt::Horizontal + + + + 170 + 0 + + + + @@ -3862,6 +3988,9 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + 2 + @@ -4034,6 +4163,19 @@ + + + + Qt::Horizontal + + + + 170 + 20 + + + + @@ -4046,6 +4188,12 @@ QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + @@ -4066,6 +4214,19 @@ + + + + Qt::Horizontal + + + + 170 + 20 + + + + @@ -4081,6 +4242,9 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + 2 + @@ -4101,7 +4265,7 @@ - + 0 @@ -4133,7 +4297,7 @@ - + Basic.Settings.Output.ReplayBuffer.Prefix @@ -4143,6 +4307,39 @@ + + + + Qt::Horizontal + + + + 170 + 20 + + + + + + + + Basic.Settings.Advanced.AutoRemux + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -4155,6 +4352,9 @@ QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 2 @@ -4244,6 +4444,19 @@ + + + + Qt::Horizontal + + + + 170 + 20 + + + + @@ -4256,6 +4469,12 @@ QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + @@ -4325,6 +4544,19 @@ + + + + Qt::Horizontal + + + + 170 + 20 + + + + @@ -4337,9 +4569,12 @@ QFormLayout::AllNonFixedFieldsGrow - - - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + @@ -4350,6 +4585,9 @@ + + + @@ -4367,33 +4605,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 + + + + + @@ -4403,6 +4692,48 @@ + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + + + true + + + error + + + + + + + + + + true + + + error + + + + + diff --git a/UI/forms/OBSLicenseAgreement.ui b/UI/forms/OBSLicenseAgreement.ui deleted file mode 100644 index e9c2d4970..000000000 --- a/UI/forms/OBSLicenseAgreement.ui +++ /dev/null @@ -1,108 +0,0 @@ - - - OBSLicenseAgreement - - - - 0 - 0 - 457 - 430 - - - - - 200 - 300 - - - - LicenseAgreement - - - - - - LicenseAgreement.PleaseReview - - - Qt::RichText - - - true - - - true - - - Qt::TextBrowserInteraction - - - - - - - - - - true - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - OK - - - - - - - - - - - - agree - clicked() - OBSLicenseAgreement - accept() - - - 138 - 419 - - - 40 - 424 - - - - - diff --git a/UI/forms/OBSRemux.ui b/UI/forms/OBSRemux.ui index 4c74e56b1..0f762b30c 100644 --- a/UI/forms/OBSRemux.ui +++ b/UI/forms/OBSRemux.ui @@ -6,77 +6,62 @@ 0 0 - 491 - 124 + 850 + 400 RemuxRecordings - - - - - Remux.SourceFile - - - - - - - Remux.TargetFile - - - - - - - - - - - - - Browse - - - - - - - - - - - - - - - Browse - - - - - - - - - 24 - - - - - - - - - QDialogButtonBox::Ok|QDialogButtonBox::Close - - - - - - + + + + + Remux.HelpText + + + + + + + 6 + + + + + QDialogButtonBox::Close|QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults + + + + + + + + + QAbstractItemView::NoSelection + + + 23 + + + 23 + + + false + + + 23 + + + + + + + 24 + + + + diff --git a/UI/forms/images/collapse.png b/UI/forms/images/collapse.png new file mode 100644 index 000000000..04707a44d Binary files /dev/null and b/UI/forms/images/collapse.png differ diff --git a/UI/forms/images/configuration21_16@2x.png b/UI/forms/images/configuration21_16@2x.png new file mode 100644 index 000000000..fb5656036 Binary files /dev/null and b/UI/forms/images/configuration21_16@2x.png differ diff --git a/UI/forms/images/ebs.png b/UI/forms/images/ebs.png new file mode 100644 index 000000000..309e541e1 Binary files /dev/null and b/UI/forms/images/ebs.png differ diff --git a/UI/forms/images/expand.png b/UI/forms/images/expand.png new file mode 100644 index 000000000..1222bcdc1 Binary files /dev/null and b/UI/forms/images/expand.png differ diff --git a/UI/forms/images/invisible_mask@2x.png b/UI/forms/images/invisible_mask@2x.png new file mode 100644 index 000000000..dffe83aa6 Binary files /dev/null and b/UI/forms/images/invisible_mask@2x.png differ diff --git a/UI/forms/images/locked_mask@2x.png b/UI/forms/images/locked_mask@2x.png new file mode 100644 index 000000000..9cad6a4ac Binary files /dev/null and b/UI/forms/images/locked_mask@2x.png differ diff --git a/UI/forms/images/obs.png b/UI/forms/images/obs.png old mode 100755 new mode 100644 diff --git a/UI/forms/images/properties@2x.png b/UI/forms/images/properties@2x.png new file mode 100644 index 000000000..fb5656036 Binary files /dev/null and b/UI/forms/images/properties@2x.png differ diff --git a/UI/forms/images/settings/advanced@2x.png b/UI/forms/images/settings/advanced@2x.png new file mode 100644 index 000000000..f4e19cbb0 Binary files /dev/null and b/UI/forms/images/settings/advanced@2x.png differ diff --git a/UI/forms/images/settings/applications-system-2@2x.png b/UI/forms/images/settings/applications-system-2@2x.png new file mode 100644 index 000000000..b99e3d0d1 Binary files /dev/null and b/UI/forms/images/settings/applications-system-2@2x.png differ diff --git a/UI/forms/images/settings/decibel_audio_player@2x.png b/UI/forms/images/settings/decibel_audio_player@2x.png new file mode 100644 index 000000000..d55529004 Binary files /dev/null and b/UI/forms/images/settings/decibel_audio_player@2x.png differ diff --git a/UI/forms/images/settings/network-bluetooth@2x.png b/UI/forms/images/settings/network-bluetooth@2x.png new file mode 100644 index 000000000..98cbd0374 Binary files /dev/null and b/UI/forms/images/settings/network-bluetooth@2x.png differ diff --git a/UI/forms/images/settings/network@2x.png b/UI/forms/images/settings/network@2x.png new file mode 100644 index 000000000..ded3efd6c Binary files /dev/null and b/UI/forms/images/settings/network@2x.png differ diff --git a/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts@2x.png b/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts@2x.png new file mode 100644 index 000000000..37d5c5c4c Binary files /dev/null and b/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts@2x.png differ diff --git a/UI/forms/images/settings/preferences-system-network-3@2x.png b/UI/forms/images/settings/preferences-system-network-3@2x.png new file mode 100644 index 000000000..75090280a Binary files /dev/null and b/UI/forms/images/settings/preferences-system-network-3@2x.png differ diff --git a/UI/forms/images/settings/system-settings-3@2x.png b/UI/forms/images/settings/system-settings-3@2x.png new file mode 100644 index 000000000..6baeb0ae2 Binary files /dev/null and b/UI/forms/images/settings/system-settings-3@2x.png differ diff --git a/UI/forms/images/settings/video-display-3@2x.png b/UI/forms/images/settings/video-display-3@2x.png new file mode 100644 index 000000000..fbe85632c Binary files /dev/null and b/UI/forms/images/settings/video-display-3@2x.png differ diff --git a/UI/forms/images/tray_active.png b/UI/forms/images/tray_active.png old mode 100755 new mode 100644 index 22a8b92fa..3f7d0d806 Binary files a/UI/forms/images/tray_active.png and b/UI/forms/images/tray_active.png differ diff --git a/UI/forms/images/unlocked_mask@2x.png b/UI/forms/images/unlocked_mask@2x.png new file mode 100644 index 000000000..de5a6ad08 Binary files /dev/null and b/UI/forms/images/unlocked_mask@2x.png differ diff --git a/UI/forms/images/visible_mask@2x.png b/UI/forms/images/visible_mask@2x.png new file mode 100644 index 000000000..540180fdd Binary files /dev/null and b/UI/forms/images/visible_mask@2x.png differ diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc index 367fc13ba..d667780f7 100644 --- a/UI/forms/obs.qrc +++ b/UI/forms/obs.qrc @@ -4,28 +4,44 @@ images/unmute.png images/refresh.png images/configuration21_16.png + images/configuration21_16@2x.png images/invisible_mask.png + images/invisible_mask@2x.png images/visible_mask.png + images/visible_mask@2x.png images/list_remove.png images/add.png images/down.png images/editscene.png images/live.png images/properties.png + images/properties@2x.png images/up.png - images/obs.png + images/ebs.png images/tray_active.png images/locked_mask.png + images/locked_mask@2x.png images/unlocked_mask.png + images/unlocked_mask@2x.png + images/collapse.png + images/expand.png images/settings/advanced.png + images/settings/advanced@2x.png images/settings/network.png + images/settings/network@2x.png images/settings/video-display-3.png + images/settings/video-display-3@2x.png images/settings/decibel_audio_player.png + images/settings/decibel_audio_player@2x.png images/settings/applications-system-2.png + images/settings/applications-system-2@2x.png images/settings/system-settings-3.png + images/settings/system-settings-3@2x.png images/settings/network-bluetooth.png + images/settings/network-bluetooth@2x.png images/settings/preferences-desktop-keyboard-shortcuts.png + images/settings/preferences-desktop-keyboard-shortcuts@2x.png diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 37a022a88..ca29cad87 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-mssapi-stream.cpp b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp index f8b642534..2861949c7 100644 --- a/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp +++ b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp @@ -249,8 +249,9 @@ STDMETHODIMP CaptionStream::Stat(STATSTG *stg, DWORD flag) stg->cbSize.QuadPart = (ULONGLONG)buf->size; if (flag == STATFLAG_DEFAULT) { - stg->pwcsName = (wchar_t*)CoTaskMemAlloc(sizeof(stat_name)); - memcpy(stg->pwcsName, stat_name, sizeof(stat_name)); + size_t byte_size = (wcslen(stat_name) + 1) * sizeof(wchar_t); + stg->pwcsName = (wchar_t*)CoTaskMemAlloc(byte_size); + memcpy(stg->pwcsName, stat_name, byte_size); } return S_OK; diff --git a/UI/frontend-plugins/frontend-tools/captions.cpp b/UI/frontend-plugins/frontend-tools/captions.cpp index 43ebf94e2..5c68d8781 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 76847958b..0fa26e4be 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/fil-PH.ini b/UI/frontend-plugins/frontend-tools/data/locale/fil-PH.ini new file mode 100644 index 000000000..b0c8450d5 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/fil-PH.ini @@ -0,0 +1,42 @@ +SceneSwitcher="Awyomatikong Eskana Tagalipat" +SceneSwitcher.OnNoMatch="Kapag walang tumutugma sa bintana:" +SceneSwitcher.OnNoMatch.DontSwitch="Huwag lumipat" +SceneSwitcher.OnNoMatch.SwitchTo="Lumipat sa:" +SceneSwitcher.CheckInterval="Suriin ang aktibong pamagat ng bintana bawat:" +SceneSwitcher.ActiveOrNotActive="Ang Eskana Tagalipat ay:" +InvalidRegex.Title="Di-wastong Regular Expression" +InvalidRegex.Text="Siya ay regular na expression na ipinasok mo ay hindi wasto." +Active="Aktibo" +Inactive="Di-aktibo" +Start="Magsimula" +Stop="Ihinto" + +Captions="Mga Caption (Experimental)" +Captions.AudioSource="Pinagmulan ng audio" +Captions.CurrentSystemLanguage="Kasalukuyang Wika ng Wika (%1)" +Captions.Provider="Tagapagbigay" +Captions.Error.GenericFail="Nabigong magsimula ng mga caption" + +OutputTimer="Output ng Timer" +OutputTimer.Stream="Itigil ang streaming pagkatapos:" +OutputTimer.Record="Itigil ang pag-record pagkatapos ng:" +OutputTimer.Stream.StoppingIn="Ang pagtigil ng streaming sa:" +OutputTimer.Record.StoppingIn="Pagre-record ng pagtigil sa:" +OutputTimer.Stream.EnableEverytime="Paganahin ang streaming timer sa bawat oras" +OutputTimer.Record.EnableEverytime="Paganahin ang timer ng pag-record sa bawat oras" + +Scripts="Mga script" +LoadedScripts="Mga Loaded na Mga Script" +AddScripts="Magdagdag ng Mga Script" +RemoveScripts="Alisin ang Mga Script" +ReloadScripts="I-reload ang Mga Script" +PythonSettings="Mga Setting ng Python" +PythonSettings.PythonInstallPath32bit="Path ng Pag-install ng Python (32bit)" +PythonSettings.PythonInstallPath64bit="Path ng Pag-install ng Python (64bit)" +PythonSettings.BrowsePythonPath="Mag-browse sa Python Path" +ScriptLogWindow="Mag-log ng Script" +Description="Paglalarawan" + +FileFilter.ScriptFiles="Mga File ng Script" +FileFilter.AllFiles="Lahat ng Mga File" + 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 000000000..c7597a6b8 --- /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/he-IL.ini b/UI/frontend-plugins/frontend-tools/data/locale/he-IL.ini index d100f7f54..1e2aaeb60 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/he-IL.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/he-IL.ini @@ -1,7 +1,9 @@ +SceneSwitcher="החלפת סצנה אוטומטית" SceneSwitcher.OnNoMatch="כאשר אין חלון מתאים:" SceneSwitcher.OnNoMatch.DontSwitch="אל תעבור" SceneSwitcher.OnNoMatch.SwitchTo="עבור ל:" SceneSwitcher.CheckInterval="בדוק כותרת חלון פעיל בכל:" +SceneSwitcher.ActiveOrNotActive="החלפת סצנה זה:" InvalidRegex.Title="ביטוי רגולרי לא חוקי" InvalidRegex.Text="הביטוי הרגולרי שהזנת אינו חוקי." Active="פעיל" @@ -23,6 +25,15 @@ OutputTimer.Record.StoppingIn="הקלטה עוצרת ב:" OutputTimer.Stream.EnableEverytime="הפעל טיימר הזרמה כל פעם" OutputTimer.Record.EnableEverytime="הפעל טיימר הקלטה כל פעם" +Scripts="תסריטים" +LoadedScripts="תסריטים טעונים" +AddScripts="הוסף תסריט" +RemoveScripts="מחק תסריטים" +ReloadScripts="טען מחדש תסריטים" +PythonSettings="הגדרות פייתון" +PythonSettings.PythonInstallPath32bit="נתיב התקנת פייתון (32 סיביות)" +PythonSettings.PythonInstallPath64bit="התקנת נתיב פייתון (64 סיביות)" +PythonSettings.BrowsePythonPath="עיון בנתיב פייתון" ScriptLogWindow="סקריפט לוג" Description="תיאור" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini b/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini index bbf555709..d6288a3ed 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini @@ -1,11 +1,42 @@ +SceneSwitcher="სცენის თვითგადამრთველი" +SceneSwitcher.OnNoMatch="როცა არცერთი ფანჯარა არ ემთხვევა:" +SceneSwitcher.OnNoMatch.DontSwitch="არ გადაირთოს" +SceneSwitcher.OnNoMatch.SwitchTo="გადაირთოს:" +SceneSwitcher.CheckInterval="მოქმედი ფანჯრის დასახელების გადამოწმების დროის შუალედი:" +SceneSwitcher.ActiveOrNotActive="სცენის თვითგადამრთველი:" +InvalidRegex.Title="არამართებული რეგულარული გამოსახულება" +InvalidRegex.Text="შეყვანილი რეგულარული გამოსახულება არასწორია." Active="ჩართული" Inactive="გამორთული" Start="დაწყება" Stop="შეწყვეტა" +Captions="წარწერები (საცდელი)" Captions.AudioSource="აუდიოს წყარო" +Captions.CurrentSystemLanguage="სისტემის მიმდინარე ენა (%1)" +Captions.Provider="მომწოდებელი" +Captions.Error.GenericFail="წარწერების დადება ვერ მოხერხდა" +OutputTimer="ჩაწერის და ნაკადის წამზომი" +OutputTimer.Stream="ნაკადი გაეშვას არაუმეტეს:" +OutputTimer.Record="ჩაწერა გაგრძელდეს არაუმეტეს:" +OutputTimer.Stream.StoppingIn="ნაკადის შეჩერების დროა:" OutputTimer.Record.StoppingIn="ჩაწერის შეწყვეტის დრო:" +OutputTimer.Stream.EnableEverytime="ნაკადის წამზომის ჩართვა ყოველ ჯერზე" +OutputTimer.Record.EnableEverytime="ჩაწერის წამზომის ჩართვა ყოველ ჯერზე" +Scripts="სკრიპტები" +LoadedScripts="ჩატვირთული სკრიპტები" +AddScripts="სკრიპტების დამატება" +RemoveScripts="სკრიპტების მოცილება" +ReloadScripts="სკრიპტების გადატვირთვა" +PythonSettings="Python-ის პარამეტრები" +PythonSettings.PythonInstallPath32bit="Python-ის დასაყენებელი მდებარეობა (32bit)" +PythonSettings.PythonInstallPath64bit="Python-ის დასაყენებელი მდებარეობა (64bit)" +PythonSettings.BrowsePythonPath="Python-ის მდებარეობის მოძიება" +ScriptLogWindow="სკრიპტის აღრიცხვა" +Description="აღწერილობა" +FileFilter.ScriptFiles="სკრიპტის ფაილები" +FileFilter.AllFiles="ყველა ფაილი" 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 18ab93e1a..2de18faa1 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/nb-NO.ini b/UI/frontend-plugins/frontend-tools/data/locale/nb-NO.ini index 8ce8952f0..b9ecf01d9 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/nb-NO.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/nb-NO.ini @@ -1,4 +1,4 @@ -SceneSwitcher="Automatisk Scene Skifter" +SceneSwitcher="Automatisk sceneskifter" SceneSwitcher.OnNoMatch="Når ingen vindu passer overens:" SceneSwitcher.OnNoMatch.DontSwitch="Ikke bytt" SceneSwitcher.OnNoMatch.SwitchTo="Bytt til:" @@ -22,8 +22,23 @@ OutputTimer.Stream="Stopp streaming etter:" OutputTimer.Record="Stopp opptak etter:" OutputTimer.Stream.StoppingIn="Streaming stopper om:" OutputTimer.Record.StoppingIn="Opptak stopper om:" -OutputTimer.Stream.EnableEverytime="Aktiver streaming timer hver gang" -OutputTimer.Record.EnableEverytime="Aktiver opptaks timer hver gang" +OutputTimer.Stream.EnableEverytime="Aktiver strømmeklokken hver gang" +OutputTimer.Record.EnableEverytime="Aktiver opptaksklokken hver gang" + +Scripts="Skripter" +LoadedScripts="Innlastede skripter" +AddScripts="Legg til skripter" +RemoveScripts="Fjern skripter" +ReloadScripts="Last inn skripter på nytt" +PythonSettings="Python-innstillinger" +PythonSettings.PythonInstallPath32bit="Python-installasjonsfilbane (32-bit)" +PythonSettings.PythonInstallPath64bit="Python-installasjonsfilbane (64-bit)" +PythonSettings.BrowsePythonPath="Finn Python-filbanen" +ScriptLogWindow="Skripthistorikk" +Description="Beskrivelse" + +FileFilter.ScriptFiles="Skriptfiler" +FileFilter.AllFiles="Alle filer" Scripts="Skripter" LoadedScripts="Innlastede skripter" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini b/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini index 42e53e6a3..b5de83b0f 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini @@ -17,12 +17,12 @@ Captions.CurrentSystemLanguage="Текущий язык системы (%1)" Captions.Provider="Поставщик" Captions.Error.GenericFail="Не удалось запустить субтитры" -OutputTimer="Таймер записи и стрима" -OutputTimer.Stream="Завершить стрим через:" +OutputTimer="Таймер записи и трансляции" +OutputTimer.Stream="Завершить трансляцию через:" OutputTimer.Record="Завершить запись через:" -OutputTimer.Stream.StoppingIn="Стрим будет завершён через:" +OutputTimer.Stream.StoppingIn="Трансляция будет завершена через:" OutputTimer.Record.StoppingIn="Запись будет завершена через:" -OutputTimer.Stream.EnableEverytime="Включать таймер стрима каждый раз" +OutputTimer.Stream.EnableEverytime="Включать таймер трансляции каждый раз" OutputTimer.Record.EnableEverytime="Включать таймер записи каждый раз" Scripts="Скрипты" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/tl-PH.ini b/UI/frontend-plugins/frontend-tools/data/locale/tl-PH.ini new file mode 100644 index 000000000..3725caf61 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/tl-PH.ini @@ -0,0 +1,42 @@ +SceneSwitcher="Awtomatikong Taga-palit ng Eksena" +SceneSwitcher.OnNoMatch="Kapag walang tumugmang window:" +SceneSwitcher.OnNoMatch.DontSwitch="Huwag lumipat" +SceneSwitcher.OnNoMatch.SwitchTo="Lumipat sa:" +SceneSwitcher.CheckInterval="Suriin ang pamagat ng aktibong window kada:" +SceneSwitcher.ActiveOrNotActive="Ang Tag-palit ng Eksena ay:" +InvalidRegex.Title="Di-wastong Regular Expression" +InvalidRegex.Text="Ang regular na ekspresyon na iyong ipinasok ay imbalido." +Active="Aktibo" +Inactive="Hindi aktibo" +Start="Simula" +Stop="Ihinto" + +Captions="Mga Pamagat (Eksparimento)" +Captions.AudioSource="Pinanggalingan ng Audio" +Captions.CurrentSystemLanguage="Kasalukuyang linggwahe ng sistema (%1)" +Captions.Provider="Tagapagtustos" +Captions.Error.GenericFail="Nabigo sa pasisimula ng mga pamagat" + +OutputTimer="Pamlabas na Orasan" +OutputTimer.Stream="Itigil ang pag-stream pagkatapos:" +OutputTimer.Record="Itigil ang pagtatala pagkatapos:" +OutputTimer.Stream.StoppingIn="Ang pagtigil ng stream sa:" +OutputTimer.Record.StoppingIn="Ang pagtigil ng pagtatala sa:" +OutputTimer.Stream.EnableEverytime="Paganahin ang streaming timer sa bawat oras" +OutputTimer.Record.EnableEverytime="Paganahin ang timer ng pagtatala bawat oras" + +Scripts="Mga iskrip" +LoadedScripts="Mga iskrip na naikarga" +AddScripts="Magdagdag ng iskrip" +RemoveScripts="Tanggalin ang mga iskrip" +ReloadScripts="Ikargang muli ang mga Iskrip" +PythonSettings="Mga Setting Python" +PythonSettings.PythonInstallPath32bit="I-install ang daan sa Python (32bit)" +PythonSettings.PythonInstallPath64bit="I-install ang Daan sa Python (64bit)" +PythonSettings.BrowsePythonPath="Daan ng Browse Python" +ScriptLogWindow="Mag-log sa Iskrip" +Description="Paglalarawan" + +FileFilter.ScriptFiles="Mga File ng Iskrip" +FileFilter.AllFiles="Lahat ng mga File" + 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 bbaac9c7f..6ea363b87 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,10 +17,28 @@ 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" +FileFilter.AllFiles="Tất cả tập tin" AddScripts="Thêm script" Description="Mô tả" 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 274a488da..316d41257 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/data/scripts/instant-replay.lua b/UI/frontend-plugins/frontend-tools/data/scripts/instant-replay.lua index 8137990d6..b1f82ff32 100644 --- a/UI/frontend-plugins/frontend-tools/data/scripts/instant-replay.lua +++ b/UI/frontend-plugins/frontend-tools/data/scripts/instant-replay.lua @@ -2,6 +2,7 @@ obs = obslua source_name = "" hotkey_id = obs.OBS_INVALID_HOTKEY_ID attempts = 0 +last_replay = "" ---------------------------------------------------------- @@ -22,27 +23,45 @@ function try_play() obs.obs_output_release(replay_buffer) + if path == last_replay then + path = nil + end + -- If the path is valid and the source exists, update it with the -- replay file to play back the replay. Otherwise, stop attempting to - -- replay after 10 seconds + -- replay after 10 retries if path == nil then attempts = attempts + 1 if attempts >= 10 then obs.remove_current_callback() end else + last_replay = path local source = obs.obs_get_source_by_name(source_name) if source ~= nil then local settings = obs.obs_data_create() - obs.obs_data_set_string(settings, "local_file", path) - obs.obs_data_set_bool(settings, "is_local_file", true) - obs.obs_data_set_bool(settings, "close_when_inactive", true) - obs.obs_data_set_bool(settings, "restart_on_activate", true) - - -- updating will automatically cause the source to - -- refresh if the source is currently active, otherwise - -- the source will play whenever its scene is activated - obs.obs_source_update(source, settings) + source_id = obs.obs_source_get_id(source) + if source_id == "ffmpeg_source" then + obs.obs_data_set_string(settings, "local_file", path) + obs.obs_data_set_bool(settings, "is_local_file", true) + + -- updating will automatically cause the source to + -- refresh if the source is currently active + obs.obs_source_update(source, settings) + elseif source_id == "vlc_source" then + -- "playlist" + array = obs.obs_data_array_create() + item = obs.obs_data_create() + obs.obs_data_set_string(item, "value", path) + obs.obs_data_array_push_back(array, item) + obs.obs_data_set_array(settings, "playlist", array) + + -- updating will automatically cause the source to + -- refresh if the source is currently active + obs.obs_source_update(source, settings) + obs.obs_data_release(item) + obs.obs_data_array_release(array) + end obs.obs_data_release(settings) obs.obs_source_release(source) @@ -65,11 +84,11 @@ function instant_replay(pressed) local ph = obs.obs_output_get_proc_handler(replay_buffer) obs.proc_handler_call(ph, "save", nil) - -- Set a 1-second timer to attempt playback every 1 second + -- Set a 2-second timer to attempt playback every 1 second -- until the replay is available if obs.obs_output_active(replay_buffer) then attempts = 0 - obs.timer_add(try_play, 1000) + obs.timer_add(try_play, 2000) else obs.script_log(obs.LOG_WARNING, "Tried to save an instant replay, but the replay buffer is not active!") end @@ -90,7 +109,7 @@ end -- A function named script_description returns the description shown to -- the user function script_description() - return "When the \"Instant Replay\" hotkey is triggered, saves a replay with the replay buffer, and then plays it in a media source as soon as the replay is ready. Requires an active replay buffer.\n\nMade by Jim" + return "When the \"Instant Replay\" hotkey is triggered, saves a replay with the replay buffer, and then plays it in a media source as soon as the replay is ready. Requires an active replay buffer.\n\nMade by Jim and Exeldro" end -- A function named script_properties defines the properties that the user @@ -106,6 +125,11 @@ function script_properties() if source_id == "ffmpeg_source" then local name = obs.obs_source_get_name(source) obs.obs_property_list_add_string(p, name, name) + elseif source_id == "vlc_source" then + local name = obs.obs_source_get_name(source) + obs.obs_property_list_add_string(p, name, name) + else + -- obs.script_log(obs.LOG_INFO, source_id) end end end diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index d923dcf61..364757e48 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 000000000..8f927fc59 --- /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 000000000..8a64c3ea8 --- /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 9a4d43e41..776c2fb7b 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/locked-checkbox.cpp b/UI/locked-checkbox.cpp index 91a049fac..a5f6b5649 100644 --- a/UI/locked-checkbox.cpp +++ b/UI/locked-checkbox.cpp @@ -7,10 +7,17 @@ LockedCheckBox::LockedCheckBox() : QCheckBox() { - lockedImage = - QPixmap::fromImage(QImage(":/res/images/locked_mask.png")); - unlockedImage = - QPixmap::fromImage(QImage(":/res/images/unlocked_mask.png")); + QString lockedFile; + QString unlockedFile; + if (devicePixelRatio() >= 2) { + lockedFile = ":/res/images/locked_mask@2x.png"; + unlockedFile = ":/res/images/unlocked_mask@2x.png"; + } else { + lockedFile = ":/res/images/locked_mask.png"; + unlockedFile = ":/res/images/unlocked_mask.png"; + } + lockedImage = QPixmap::fromImage(QImage(lockedFile)); + unlockedImage = QPixmap::fromImage(QImage(unlockedFile)); setMinimumSize(16, 16); setStyleSheet("outline: none;"); @@ -31,6 +38,5 @@ void LockedCheckBox::paintEvent(QPaintEvent *event) palette().color(foregroundRole())); QPainter p(this); - p.drawPixmap(0, 0, image.width(), image.height(), - QPixmap::fromImage(image)); + p.drawPixmap(0, 0, 16, 16, QPixmap::fromImage(image)); } diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index d5fc09a21..255089252 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 @@ -38,7 +39,6 @@ #include "obs-app.hpp" #include "window-basic-main.hpp" #include "window-basic-settings.hpp" -#include "window-license-agreement.hpp" #include "crash-report.hpp" #include "platform.hpp" @@ -60,6 +60,7 @@ static log_handler_t def_log_handler; static string currentLogFile; static string lastLogFile; +static string lastCrashLogFile; bool portable_mode = false; static bool multi = false; @@ -76,8 +77,12 @@ string opt_starting_collection; string opt_starting_profile; string opt_starting_scene; -// AMD PowerXpress High Performance Flags +bool remuxAfterRecord = false; +string remuxFilename; + +// 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 @@ -344,6 +349,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) { @@ -365,6 +371,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", @@ -417,9 +424,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__ @@ -444,34 +467,34 @@ static bool MakeUserDirs() { char path[512]; - if (GetConfigPath(path, sizeof(path), "obs-studio/basic") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/basic") <= 0) return false; if (!do_mkdir(path)) return false; - if (GetConfigPath(path, sizeof(path), "obs-studio/logs") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/logs") <= 0) return false; if (!do_mkdir(path)) return false; - if (GetConfigPath(path, sizeof(path), "obs-studio/profiler_data") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/profiler_data") <= 0) return false; if (!do_mkdir(path)) return false; #ifdef _WIN32 - if (GetConfigPath(path, sizeof(path), "obs-studio/crashes") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/crashes") <= 0) return false; if (!do_mkdir(path)) return false; - if (GetConfigPath(path, sizeof(path), "obs-studio/updates") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/updates") <= 0) return false; if (!do_mkdir(path)) return false; #endif - if (GetConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/plugin_config") <= 0) return false; if (!do_mkdir(path)) return false; @@ -483,12 +506,12 @@ static bool MakeUserProfileDirs() { char path[512]; - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/basic/profiles") <= 0) return false; if (!do_mkdir(path)) return false; - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/basic/scenes") <= 0) return false; if (!do_mkdir(path)) return false; @@ -502,7 +525,7 @@ static string GetProfileDirFromName(const char *name) os_glob_t *glob; char path[512]; - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/basic/profiles") <= 0) return outputPath; strcat(path, "/*"); @@ -548,7 +571,7 @@ static string GetSceneCollectionFileFromName(const char *name) os_glob_t *glob; char path[512]; - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/basic/scenes") <= 0) return outputPath; strcat(path, "/*.json"); @@ -587,13 +610,49 @@ 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]; bool changed = false; int len = GetConfigPath(path, sizeof(path), - "obs-studio/global.ini"); + "ebs-studio/global.ini"); if (len <= 0) { return false; } @@ -652,6 +711,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); @@ -726,6 +792,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; @@ -734,7 +986,7 @@ bool OBSApp::SetTheme(std::string name, std::string path) if (path == "") { char userDir[512]; name = "themes/" + name + ".qss"; - string temp = "obs-studio/" + name; + string temp = "ebs-studio/" + name; int ret = GetConfigPath(userDir, sizeof(userDir), temp.c_str()); @@ -747,12 +999,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) { @@ -805,13 +1063,13 @@ static void move_basic_to_profiles(void) os_glob_t *glob; /* if not first time use */ - if (GetConfigPath(path, 512, "obs-studio/basic") <= 0) + if (GetConfigPath(path, 512, "ebs-studio/basic") <= 0) return; if (!os_file_exists(path)) return; /* if the profiles directory doesn't already exist */ - if (GetConfigPath(new_path, 512, "obs-studio/basic/profiles") <= 0) + if (GetConfigPath(new_path, 512, "ebs-studio/basic/profiles") <= 0) return; if (os_file_exists(new_path)) return; @@ -858,12 +1116,12 @@ static void move_basic_to_scene_collections(void) char path[512]; char new_path[512]; - if (GetConfigPath(path, 512, "obs-studio/basic") <= 0) + if (GetConfigPath(path, 512, "ebs-studio/basic") <= 0) return; if (!os_file_exists(path)) return; - if (GetConfigPath(new_path, 512, "obs-studio/basic/scenes") <= 0) + if (GetConfigPath(new_path, 512, "ebs-studio/basic/scenes") <= 0) return; if (os_file_exists(new_path)) return; @@ -915,6 +1173,9 @@ void OBSApp::AppInit() EnableOSXVSync(false); #endif + enableHotkeysInFocus = !config_get_bool(globalConfig, "General", + "DisableHotkeysInFocus"); + move_basic_to_profiles(); move_basic_to_scene_collections(); @@ -935,54 +1196,66 @@ static bool StartupOBS(const char *locale, profiler_name_store_t *store) { char path[512]; - if (GetConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0) + if (GetConfigPath(path, sizeof(path), "ebs-studio/plugin_config") <= 0) return false; 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"); - bool licenseAccepted = config_get_bool(globalConfig, "General", - "LicenseAccepted"); - OBSLicenseAgreement agreement(nullptr); + setAttribute(Qt::AA_UseHighDpiPixmaps); - if (licenseAccepted || agreement.exec() == QDialog::Accepted) { - if (!licenseAccepted) { - config_set_bool(globalConfig, "General", - "LicenseAccepted", true); - config_save(globalConfig); - } + if (!StartupOBS(locale.c_str(), GetProfilerNameStore())) + return false; - if (!StartupOBS(locale.c_str(), GetProfilerNameStore())) - return false; +#ifdef _WIN32 + bool browserHWAccel = config_get_bool(globalConfig, "General", + "BrowserHWAccel"); - blog(LOG_INFO, "Portable mode: %s", - portable_mode ? "true" : "false"); + obs_data_t *settings = obs_data_create(); + obs_data_set_bool(settings, "BrowserHWAccel", browserHWAccel); + obs_apply_private_data(settings); + obs_data_release(settings); - setQuitOnLastWindowClosed(false); + blog(LOG_INFO, "Browser Hardware Acceleration: %s", + browserHWAccel ? "true" : "false"); +#endif - mainWindow = new OBSBasic(); + blog(LOG_INFO, "Portable mode: %s", + portable_mode ? "true" : "false"); - mainWindow->setAttribute(Qt::WA_DeleteOnClose, true); - connect(mainWindow, SIGNAL(destroyed()), this, SLOT(quit())); + setQuitOnLastWindowClosed(false); - mainWindow->OBSInit(); + mainWindow = new OBSBasic(); - connect(this, &QGuiApplication::applicationStateChanged, - [](Qt::ApplicationState state) - { - obs_hotkey_enable_background_press( - state != Qt::ApplicationActive); - }); - obs_hotkey_enable_background_press( - applicationState() != Qt::ApplicationActive); - return true; - } else { - return false; - } + mainWindow->setAttribute(Qt::WA_DeleteOnClose, true); + connect(mainWindow, SIGNAL(destroyed()), this, SLOT(quit())); + + mainWindow->OBSInit(); + + connect(this, &QGuiApplication::applicationStateChanged, + [this](Qt::ApplicationState state) + { + ResetHotkeyState( + state != Qt::ApplicationActive); + }); + ResetHotkeyState(applicationState() != Qt::ApplicationActive); + return true; } string OBSApp::GetVersionString() const @@ -1001,7 +1274,9 @@ string OBSApp::GetVersionString() const #ifdef _WIN32 if (sizeof(void*) == 8) - ver << "64bit, "; + ver << "64-bit, "; + else + ver << "32-bit, "; ver << "windows)"; #elif __APPLE__ @@ -1051,6 +1326,11 @@ const char *OBSApp::GetCurrentLog() const return currentLogFile.c_str(); } +const char *OBSApp::GetLastCrashLog() const +{ + return lastCrashLogFile.c_str(); +} + bool OBSApp::TranslateString(const char *lookupVal, const char **out) const { for (obs_frontend_translate_ui_cb cb : translatorHooks) { @@ -1097,13 +1377,18 @@ static bool expect_token(lexer *lex, const char *str, base_token_type type) return strref_cmp(&token.text, str) == 0; } -static uint64_t convert_log_name(const char *name) +static uint64_t convert_log_name(bool has_prefix, const char *name) { BaseLexer lex; string year, month, day, hour, minute, second; lexer_start(lex, name); + if (has_prefix) { + string temp; + if (!get_token(lex, temp, BASETOKEN_ALPHA)) return 0; + } + if (!get_token(lex, year, BASETOKEN_DIGIT)) return 0; if (!expect_token(lex, "-", BASETOKEN_OTHER)) return 0; if (!get_token(lex, month, BASETOKEN_DIGIT)) return 0; @@ -1120,7 +1405,7 @@ static uint64_t convert_log_name(const char *name) return std::stoull(timestring.str()); } -static void delete_oldest_file(const char *location) +static void delete_oldest_file(bool has_prefix, const char *location) { BPtr logDir(GetConfigPathPtr(location)); string oldestLog; @@ -1138,7 +1423,8 @@ static void delete_oldest_file(const char *location) if (entry->directory || *entry->d_name == '.') continue; - uint64_t ts = convert_log_name(entry->d_name); + uint64_t ts = convert_log_name(has_prefix, + entry->d_name); if (ts) { if (ts < oldest_ts) { @@ -1161,9 +1447,10 @@ static void delete_oldest_file(const char *location) } } -static void get_last_log(void) +static void get_last_log(bool has_prefix, const char *subdir_to_use, + std::string &last) { - BPtr logDir(GetConfigPathPtr("obs-studio/logs")); + BPtr logDir(GetConfigPathPtr(subdir_to_use)); struct os_dirent *entry; os_dir_t *dir = os_opendir(logDir); uint64_t highest_ts = 0; @@ -1173,11 +1460,12 @@ static void get_last_log(void) if (entry->directory || *entry->d_name == '.') continue; - uint64_t ts = convert_log_name(entry->d_name); + uint64_t ts = convert_log_name(has_prefix, + entry->d_name); if (ts > highest_ts) { - lastLogFile = entry->d_name; - highest_ts = ts; + last = entry->d_name; + highest_ts = ts; } } @@ -1208,8 +1496,18 @@ string GenerateTimeDateFilename(const char *extension, bool noSpace) string GenerateSpecifiedFilename(const char *extension, bool noSpace, const char *format) { + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + bool autoRemux = config_get_bool(main->Config(), "Video", "AutoRemux"); + + if ((strcmp(extension, "mp4") == 0) && autoRemux) + extension = "mkv"; + BPtr filename = os_generate_formatted_filename(extension, !noSpace, format); + + remuxFilename = string(filename); + remuxAfterRecord = autoRemux; + return string(filename); } @@ -1240,10 +1538,13 @@ static void create_log_file(fstream &logFile) { stringstream dst; - get_last_log(); + get_last_log(false, "ebs-studio/logs", lastLogFile); +#ifdef _WIN32 + get_last_log(true, "ebs-studio/crashes", lastCrashLogFile); +#endif currentLogFile = GenerateTimeDateFilename("txt"); - dst << "obs-studio/logs/" << currentLogFile.c_str(); + dst << "ebs-studio/logs/" << currentLogFile.c_str(); BPtr path(GetConfigPathPtr(dst.str().c_str())); @@ -1258,7 +1559,7 @@ static void create_log_file(fstream &logFile) #endif if (logFile.is_open()) { - delete_oldest_file("obs-studio/logs"); + delete_oldest_file(false, "ebs-studio/logs"); base_set_log_handler(do_log, &logFile); } else { blog(LOG_ERROR, "Failed to open log file"); @@ -1304,7 +1605,7 @@ static void SaveProfilerData(const ProfilerSnapshot &snap) #define LITERAL_SIZE(x) x, (sizeof(x) - 1) ostringstream dst; - dst.write(LITERAL_SIZE("obs-studio/profiler_data/")); + dst.write(LITERAL_SIZE("ebs-studio/profiler_data/")); dst.write(currentLogFile.c_str(), pos); dst.write(LITERAL_SIZE(".csv.gz")); #undef LITERAL_SIZE @@ -1349,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(); + delete_oldest_file(false, "ebs-studio/profiler_data"); OBSTranslator translator; - - create_log_file(logFile); - delete_oldest_file("obs-studio/profiler_data"); - 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")); @@ -1384,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."); + "running multiple instances of EBS."); + } else { + blog(LOG_WARNING, "================================"); + blog(LOG_WARNING, "Warning: EBS is already running!"); + blog(LOG_WARNING, "================================"); + blog(LOG_WARNING, "User is now running multiple " + "instances of EBS!"); } /* --------------------------------------- */ +run: #endif + + if (!created_log) { + create_log_file(logFile); + created_log = true; + } + if (argc > 1) { stringstream stor; stor << argv[1]; @@ -1430,7 +1744,7 @@ static int run_program(fstream &logFile, int argc, char *argv[]) #ifdef _WIN32 #define CRASH_MESSAGE \ - "Woops, OBS has crashed!\n\nWould you like to copy the crash log " \ + "Woops, EBS has crashed!\n\nWould you like to copy the crash log " \ "to the clipboard? (Crash logs will still be saved to the " \ "%appdata%\\obs-studio\\crashes directory)" @@ -1441,9 +1755,9 @@ static void main_crash_handler(const char *format, va_list args, void *param) vsnprintf(text, MAX_CRASH_REPORT_SIZE, format, args); text[MAX_CRASH_REPORT_SIZE - 1] = 0; - delete_oldest_file("obs-studio/crashes"); + delete_oldest_file(true, "ebs-studio/crashes"); - string name = "obs-studio/crashes/Crash "; + string name = "ebs-studio/crashes/Crash "; name += GenerateTimeDateFilename("txt"); BPtr path(GetConfigPathPtr(name.c_str())); @@ -1462,7 +1776,7 @@ static void main_crash_handler(const char *format, va_list args, void *param) file << text; file.close(); - int ret = MessageBoxA(NULL, CRASH_MESSAGE, "OBS has crashed!", + int ret = MessageBoxA(NULL, CRASH_MESSAGE, "EBS has crashed!", MB_YESNO | MB_ICONERROR | MB_TASKMODAL); if (ret == IDYES) { @@ -1656,7 +1970,7 @@ static void move_to_xdg(void) if (os_mkdirs(new_path) == MKDIR_ERROR) return; - if (GetConfigPath(new_path, 512, "obs-studio") <= 0) + if (GetConfigPath(new_path, 512, "ebs-studio") <= 0) return; if (os_file_exists(old_path) && !os_file_exists(new_path)) { @@ -1780,7 +2094,7 @@ static void convert_14_2_encoder_setting(const char *encoder, const char *file) modified = true; } - if (!rc_item && astrcmpi(encoder, "obs_x264") == 0) { + if (!rc_item && astrcmpi(encoder, "ebs_x264") == 0) { if (!cbr_item) obs_data_set_string(data, "rate_control", "CBR"); else if (!cbr) @@ -1800,7 +2114,7 @@ static void convert_14_2_encoder_setting(const char *encoder, const char *file) static void upgrade_settings(void) { char path[512]; - int pathlen = GetConfigPath(path, 512, "obs-studio/basic/profiles"); + int pathlen = GetConfigPath(path, 512, "ebs-studio/basic/profiles"); if (pathlen <= 0) return; @@ -1870,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); @@ -1881,6 +2196,8 @@ int main(int argc, char *argv[]) move_to_xdg(); #endif + obs_set_cmdline_args(argc, argv); + for (int i = 1; i < argc; i++) { if (arg_is(argv[i], "--portable", "-p")) { portable_mode = true; @@ -1947,7 +2264,7 @@ int main(int argc, char *argv[]) exit(0); } else if (arg_is(argv[i], "--version", "-V")) { - std::cout << "OBS Studio - " << + std::cout << "EBS Studio - " << App()->GetVersionString() << "\n"; exit(0); } diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index 53043ac3f..27b6cc3e7 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -73,13 +73,26 @@ class OBSApp : public QApplication { 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 @@ class OBSApp : public QApplication { void AppInit(); bool OBSInit(); + void EnableInFocusHotkeys(bool enable); + inline QMainWindow *GetMainWindow() const {return mainWindow.data();} inline config_t *GlobalConfig() const {return globalConfig;} @@ -116,6 +131,8 @@ class OBSApp : public QApplication { const char *GetLastLog() const; const char *GetCurrentLog() const; + const char *GetLastCrashLog() const; + std::string GetVersionString() const; bool IsPortableMode(); @@ -148,6 +165,9 @@ class OBSApp : public QApplication { { translatorHooks.pop_front(); } + +signals: + void StyleChanged(); }; int GetConfigPath(char *path, size_t size, const char *name); @@ -177,6 +197,10 @@ static inline int GetProfilePath(char *path, size_t size, const char *file) } extern bool portable_mode; + +extern bool remuxAfterRecord; +extern std::string remuxFilename; + extern bool opt_start_streaming; extern bool opt_start_recording; extern bool opt_start_replaybuffer; diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index ee47fff00..c3c933ab8 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 05483ff53..ae674943a 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 0749d2ed5..b1a906418 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 19b1a9cdc..6a0258197 100644 --- a/UI/platform-osx.mm +++ b/UI/platform-osx.mm @@ -31,7 +31,7 @@ bool GetDataFilePath(const char *data, string &output) { stringstream str; - str << OBS_DATA_PATH "/obs-studio/" << data; + str << OBS_DATA_PATH "/ebs-studio/" << data; output = str.str(); return !access(output.c_str(), R_OK); } @@ -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-windows.cpp b/UI/platform-windows.cpp index 90740187a..5c16ebd2e 100644 --- a/UI/platform-windows.cpp +++ b/UI/platform-windows.cpp @@ -51,10 +51,10 @@ static inline bool check_path(const char* data, const char *path, bool GetDataFilePath(const char *data, string &output) { - if (check_path(data, "data/obs-studio/", output)) + if (check_path(data, "data/ebs-studio/", output)) return true; - return check_path(data, OBS_DATA_PATH "/obs-studio/", output); + return check_path(data, OBS_DATA_PATH "/ebs-studio/", output); } bool InitApplicationBundle() diff --git a/UI/platform-x11.cpp b/UI/platform-x11.cpp index c6b02b9b3..387585697 100644 --- a/UI/platform-x11.cpp +++ b/UI/platform-x11.cpp @@ -41,7 +41,7 @@ static inline bool check_path(const char* data, const char *path, return (access(output.c_str(), R_OK) == 0); } -#define INSTALL_DATA_PATH OBS_INSTALL_PREFIX OBS_DATA_PATH "/obs-studio/" +#define INSTALL_DATA_PATH OBS_INSTALL_PREFIX OBS_DATA_PATH "/ebs-studio/" bool GetDataFilePath(const char *data, string &output) { @@ -51,7 +51,7 @@ bool GetDataFilePath(const char *data, string &output) return true; } - if (check_path(data, OBS_DATA_PATH "/obs-studio/", output)) + if (check_path(data, OBS_DATA_PATH "/ebs-studio/", output)) return true; if (check_path(data, INSTALL_DATA_PATH, output)) return true; diff --git a/UI/platform.hpp b/UI/platform.hpp index 5011e6050..4da23b3bd 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 169b9f702..4a286b6ca 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-display.cpp b/UI/qt-display.cpp index 90bd9bebd..0640ca32e 100644 --- a/UI/qt-display.cpp +++ b/UI/qt-display.cpp @@ -6,6 +6,20 @@ #include #include +static inline long long color_to_int(QColor color) +{ + auto shift = [&](unsigned val, int shift) + { + return ((val & 0xff) << shift); + }; + + return shift(color.red(), 0) | + shift(color.green(), 8) | + shift(color.blue(), 16) | + shift(color.alpha(), 24); +} + + OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags) { @@ -39,6 +53,14 @@ OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) connect(windowHandle(), &QWindow::visibleChanged, windowVisible); connect(windowHandle(), &QWindow::screenChanged, sizeChanged); + + this->setProperty("themeID", "displayBackgroundColor"); +} + +void OBSQTDisplay::SetDisplayBackgroundColor(const QColor &color) +{ + backgroundColor = (uint32_t)color_to_int(color); + obs_display_set_background_color(display, backgroundColor); } void OBSQTDisplay::CreateDisplay() @@ -56,7 +78,7 @@ void OBSQTDisplay::CreateDisplay() QTToGSWindow(winId(), info.window); - display = obs_display_create(&info); + display = obs_display_create(&info, backgroundColor); emit DisplayCreated(this); } diff --git a/UI/qt-display.hpp b/UI/qt-display.hpp index 438100bf9..4c975cee2 100644 --- a/UI/qt-display.hpp +++ b/UI/qt-display.hpp @@ -5,6 +5,8 @@ class OBSQTDisplay : public QWidget { Q_OBJECT + Q_PROPERTY(QColor displayBackgroundColor WRITE SetDisplayBackgroundColor + NOTIFY SetDisplayBackgroundColor) OBSDisplay display; @@ -23,4 +25,9 @@ class OBSQTDisplay : public QWidget { virtual QPaintEngine *paintEngine() const override; inline obs_display_t *GetDisplay() const {return display;} + + uint32_t backgroundColor; + +private slots: + void SetDisplayBackgroundColor(const QColor &color); }; diff --git a/UI/qt-wrappers.hpp b/UI/qt-wrappers.hpp index 7899387bf..5273f9298 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 @@ class SignalBlocker { }; 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 a94ebebdd..d275ad2f0 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 000000000..c86aea9cb --- /dev/null +++ b/UI/source-tree.cpp @@ -0,0 +1,1399 @@ +#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); + } + } +} + +bool SourceTreeItem::IsEditing() +{ + return editor != nullptr; +} + +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; + + QModelIndex index = stm->createIndex(row, 0); + QWidget *widget = indexWidget(index); + SourceTreeItem *itemWidget = reinterpret_cast(widget); + if (itemWidget->IsEditing()) + return; + + itemWidget->EnterEditMode(); + edit(index); +} + +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 000000000..cacd950b8 --- /dev/null +++ b/UI/source-tree.hpp @@ -0,0 +1,178 @@ +#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); + bool IsEditing(); + +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/sparkle-updater.mm b/UI/sparkle-updater.mm index 1baab8d18..75d2f8f3f 100644 --- a/UI/sparkle-updater.mm +++ b/UI/sparkle-updater.mm @@ -105,7 +105,7 @@ static inline bool bundle_matches(NSBundle *bundle) return false; NSRange r = [bundle.executablePath rangeOfString:@"Contents/MacOS/"]; - return [bundle.bundleIdentifier isEqual:@"com.obsproject.obs-studio"] && + return [bundle.bundleIdentifier isEqual:@"com.evercast.ebs-studio"] && r.location != NSNotFound; } diff --git a/UI/visibility-checkbox.cpp b/UI/visibility-checkbox.cpp index 5b73084e0..004a65c4d 100644 --- a/UI/visibility-checkbox.cpp +++ b/UI/visibility-checkbox.cpp @@ -7,10 +7,17 @@ VisibilityCheckBox::VisibilityCheckBox() : QCheckBox() { - checkedImage = - QPixmap::fromImage(QImage(":/res/images/visible_mask.png")); - uncheckedImage = - QPixmap::fromImage(QImage(":/res/images/invisible_mask.png")); + QString checkedFile; + QString uncheckedFile; + if (devicePixelRatio() >= 2) { + checkedFile = ":/res/images/visible_mask@2x.png"; + uncheckedFile = ":/res/images/invisible_mask@2x.png"; + } else { + checkedFile = ":/res/images/visible_mask.png"; + uncheckedFile = ":/res/images/invisible_mask.png"; + } + checkedImage = QPixmap::fromImage(QImage(checkedFile)); + uncheckedImage = QPixmap::fromImage(QImage(uncheckedFile)); setMinimumSize(16, 16); setStyleSheet("outline: none;"); @@ -31,6 +38,5 @@ void VisibilityCheckBox::paintEvent(QPaintEvent *event) palette().color(foregroundRole())); QPainter p(this); - p.drawPixmap(0, 0, image.width(), image.height(), - QPixmap::fromImage(image)); + p.drawPixmap(0, 0, 16, 16, QPixmap::fromImage(image)); } diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 47019cc4a..80fbe1f6e 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 (peakPosition < minimumPosition) { - painter.fillRect( - minimumPosition, y, - nominalLength, height, - backgroundNominalColor); - painter.fillRect( - warningPosition, y, - warningLength, height, - backgroundWarningColor); - painter.fillRect( - errorPosition, y, - errorLength, height, - backgroundErrorColor); + 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); } 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; + } - if (peakHoldPosition - 3 < minimumPosition) { - // Peak-hold below minimum, no drawing. + int end = errorLength + warningLength + nominalLength; + painter.fillRect(minimumPosition, y, end, height, + QBrush(foregroundErrorColor)); + } - } else if (peakHoldPosition < warningPosition) { - painter.fillRect( - peakHoldPosition - 3, y, - 3, height, - foregroundNominalColor); + 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 < errorPosition) { - painter.fillRect( - peakHoldPosition - 3, y, - 3, height, - foregroundWarningColor); + if (magnitudePosition - 3 >= minimumPosition) + painter.fillRect(magnitudePosition - 3, y, 3, height, + magnitudeColor); +} - } else { - painter.fillRect( - peakHoldPosition - 3, y, - 3, height, - foregroundErrorColor); - } +void VolumeMeter::paintVMeter(QPainter &painter, int x, int y, int width, + int height, float magnitude, float peak, float peakHold) +{ + qreal scale = height / minimumLevel; - if (magnitudePosition - 3 < minimumPosition) { - // Magnitude below minimum, no drawing. + 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)); - } else if (magnitudePosition < warningPosition) { - painter.fillRect( - magnitudePosition - 3, y, - 3, height, - magnitudeColor); + int nominalLength = warningPosition - minimumPosition; + int warningLength = errorPosition - warningPosition; + int errorLength = maximumPosition - errorPosition; + locker.unlock(); - } else if (magnitudePosition < errorPosition) { - painter.fillRect( - magnitudePosition - 3, y, - 3, height, - magnitudeColor); + if (clipping) { + peakPosition = maximumPosition; + } + 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 (!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 (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) + 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 390e684dc..fde0d12f4 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 @@ class VolumeMeter : public QWidget 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 @@ class VolumeMeter : public QWidget 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 @@ class VolumeMeter : public QWidget 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 @@ class VolumeMeter : public QWidget 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 @@ class VolumeMeterTimer : public QTimer { void RemoveVolControl(VolumeMeter *meter); protected: - virtual void timerEvent(QTimerEvent *event) override; + void timerEvent(QTimerEvent *event) override; QList volumeMeters; }; @@ -224,6 +235,7 @@ class VolControl : public QWidget { 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 @@ private slots: 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 @@ private slots: void SetName(const QString &newName); void SetMeterDecayRate(qreal q); + void setPeakMeterType(enum obs_peak_meter_type peakMeterType); }; diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 7d0e45c9c..a22ed8375 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -1,4 +1,4 @@ -if(NOT ENABLE_WIN_UPDATER) +if(DISABLE_UPDATE_MODULE) return() endif() @@ -36,7 +36,7 @@ set(updater_SOURCES add_definitions(-DNOMINMAX -DUNICODE -D_UNICODE) if(MSVC) - add_compile_options("$<$:/MT>") + add_compile_options($,/MTd,/MT>) endif() add_executable(updater WIN32 diff --git a/UI/win-update/updater/hash.cpp b/UI/win-update/updater/hash.cpp index 69033b036..73863a5b8 100644 --- a/UI/win-update/updater/hash.cpp +++ b/UI/win-update/updater/hash.cpp @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2017-2018 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #include "updater.hpp" #include diff --git a/UI/win-update/updater/http.cpp b/UI/win-update/updater/http.cpp index 13d0d9d89..04ef58a28 100644 --- a/UI/win-update/updater/http.cpp +++ b/UI/win-update/updater/http.cpp @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2017-2018 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #include "Updater.hpp" #include diff --git a/UI/win-update/updater/patch.cpp b/UI/win-update/updater/patch.cpp index a80116855..0593e04e0 100644 --- a/UI/win-update/updater/patch.cpp +++ b/UI/win-update/updater/patch.cpp @@ -1,9 +1,31 @@ +/* + * Copyright (c) 2017-2018 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #include "updater.hpp" #include #include -#include +#ifdef _MSC_VER +# define restrict __restrict +# include +# undef restrict +#else +# include +#endif using namespace std; @@ -234,7 +256,7 @@ try { vector newData; try { - newData.resize(newsize); + newData.resize((size_t)newsize); } catch (...) { throw int(-1); } diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index 443c7fe0f..93df309c5 100644 --- a/UI/win-update/updater/updater.cpp +++ b/UI/win-update/updater/updater.cpp @@ -1,20 +1,18 @@ -/****************************************************************************** - Copyright (C) 2017 Hugh Bailey - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. -******************************************************************************/ +/* + * Copyright (c) 2017-2018 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ #include "updater.hpp" @@ -59,50 +57,48 @@ void FreeWinHttpHandle(HINTERNET handle) /* ----------------------------------------------------------------------- */ -// http://www.codeproject.com/Articles/320748/Haephrati-Elevating-during-runtime -static bool IsAppRunningAsAdminMode() +static inline bool is_64bit_windows(void); + +static inline bool HasVS2017Redist2() { - BOOL fIsRunAsAdmin = FALSE; - DWORD dwError = ERROR_SUCCESS; - PSID pAdministratorsGroup = nullptr; - - /* Allocate and initialize a SID of the administrators group. */ - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&NtAuthority, - 2, - SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, - 0, - 0, - 0, - 0, - 0, - 0, - &pAdministratorsGroup)) { - dwError = GetLastError(); - goto Cleanup; - } + wchar_t base[MAX_PATH]; + wchar_t path[MAX_PATH]; + WIN32_FIND_DATAW wfd; + HANDLE handle; + int folder = (is32bit && is_64bit_windows()) + ? CSIDL_SYSTEMX86 + : CSIDL_SYSTEM; - /* Determine whether the SID of administrators group is enabled in the - * primary access token of the process. */ - if (!CheckTokenMembership(nullptr, pAdministratorsGroup, - &fIsRunAsAdmin)) { - dwError = GetLastError(); - goto Cleanup; - } + SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT, base); -Cleanup: - /* Centralized cleanup for all allocated resources. */ - if (pAdministratorsGroup) { - FreeSid(pAdministratorsGroup); - pAdministratorsGroup = nullptr; + StringCbCopyW(path, sizeof(path), base); + StringCbCatW(path, sizeof(path), L"\\msvcp140.dll"); + handle = FindFirstFileW(path, &wfd); + if (handle == INVALID_HANDLE_VALUE) { + return false; + } else { + FindClose(handle); } - /* Throw the error if something failed in the function. */ - if (ERROR_SUCCESS != dwError) + StringCbCopyW(path, sizeof(path), base); + StringCbCatW(path, sizeof(path), L"\\vcruntime140.dll"); + handle = FindFirstFileW(path, &wfd); + if (handle == INVALID_HANDLE_VALUE) { return false; + } else { + FindClose(handle); + } + + return true; +} - return !!fIsRunAsAdmin; +static bool HasVS2017Redist() +{ + PVOID old = nullptr; + bool redirect = !!Wow64DisableWow64FsRedirection(&old); + bool success = HasVS2017Redist2(); + if (redirect) Wow64RevertWow64FsRedirection(old); + return success; } static void Status(const wchar_t *fmt, ...) @@ -328,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; } }; @@ -360,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; } @@ -578,10 +576,18 @@ static inline bool FileExists(const wchar_t *path) static bool NonCorePackageInstalled(const char *name) { - if (strcmp(name, "obs-browser") == 0) { - return FileExists(L"obs-plugins\\32bit\\obs-browser.dll"); - } else if (strcmp(name, "realsense") == 0) { - return FileExists(L"obs-plugins\\32bit\\win-ivcam.dll"); + if (is32bit) { + if (strcmp(name, "obs-browser") == 0) { + return FileExists(L"obs-plugins\\32bit\\obs-browser.dll"); + } else if (strcmp(name, "realsense") == 0) { + return FileExists(L"obs-plugins\\32bit\\win-ivcam.dll"); + } + } else { + if (strcmp(name, "obs-browser") == 0) { + return FileExists(L"obs-plugins\\64bit\\obs-browser.dll"); + } else if (strcmp(name, "realsense") == 0) { + return FileExists(L"obs-plugins\\64bit\\win-ivcam.dll"); + } } return false; @@ -608,10 +614,15 @@ static inline bool is_64bit_file(const char *file) strstr(file, "64.exe") != nullptr; } +static inline bool has_str(const char *file, const char *str) +{ + return (file && str) ? (strstr(file, str) != nullptr) : false; +} + #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) @@ -661,6 +672,12 @@ static bool AddPackageUpdateFiles(json_t *root, size_t idx, if (!isWin64 && is_64bit_file(fileUTF8)) continue; + /* ignore update files of opposite arch to reduce download */ + + if (( is32bit && has_str(fileUTF8, "/64bit/")) || + (!is32bit && has_str(fileUTF8, "/32bit/"))) + continue; + /* convert strings to wide */ wchar_t sourceURL[1024]; @@ -741,7 +758,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; @@ -930,6 +947,156 @@ static wchar_t tempPath[MAX_PATH] = {}; #define HASH_NULL \ L"0000000000000000000000000000000000000000" +static bool UpdateVS2017Redists(json_t *root) +{ + /* ------------------------------------------ * + * Initialize session */ + + const DWORD tlsProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2; + + HttpHandle hSession = WinHttpOpen(L"OBS Studio Updater/2.1", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0); + if (!hSession) { + Status(L"Update failed: Couldn't open obsproject.com"); + return false; + } + + WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, + (LPVOID)&tlsProtocols, sizeof(tlsProtocols)); + + HttpHandle hConnect = WinHttpConnect(hSession, L"cdn-fastly.obsproject.com", + INTERNET_DEFAULT_HTTPS_PORT, 0); + if (!hConnect) { + Status(L"Update failed: Couldn't connect to cdn-fastly.obsproject.com"); + return false; + } + + int responseCode; + + DWORD waitResult = WaitForSingleObject(cancelRequested, 0); + if (waitResult == WAIT_OBJECT_0) { + return false; + } + + /* ------------------------------------------ * + * Download redist */ + + Status(L"Downloading %s", L"Visual C++ 2017 Redistributable"); + + const wchar_t *file = (is32bit) + ? L"vc2017redist_x86.exe" + : L"vc2017redist_x64.exe"; + + wstring sourceURL; + sourceURL += L"https://cdn-fastly.obsproject.com/downloads/"; + sourceURL += file; + + wstring destPath; + destPath += tempPath; + destPath += L"\\"; + destPath += file; + + if (!HTTPGetFile(hConnect, + sourceURL.c_str(), + destPath.c_str(), + L"Accept-Encoding: gzip", + &responseCode)) { + + DeleteFile(destPath.c_str()); + Status(L"Update failed: Could not download " + L"%s (error code %d)", + L"Visual C++ 2017 Redistributable", + responseCode); + return false; + } + + /* ------------------------------------------ * + * Get expected hash */ + + json_t *redistJson = json_object_get(root, is32bit + ? "vc2017_redist_x86" + : "vc2017_redist_x64"); + if (!redistJson) { + Status(L"Update failed: Could not parse VC2017 redist json"); + return false; + } + + const char *expectedHashUTF8 = json_string_value(redistJson); + wchar_t expectedHashWide[BLAKE2_HASH_STR_LENGTH]; + BYTE expectedHash[BLAKE2_HASH_LENGTH]; + + if (!UTF8ToWideBuf(expectedHashWide, expectedHashUTF8)) { + DeleteFile(destPath.c_str()); + Status(L"Update failed: Couldn't convert Json for redist hash"); + return false; + } + + StringToHash(expectedHashWide, expectedHash); + + wchar_t downloadHashWide[BLAKE2_HASH_STR_LENGTH]; + BYTE downloadHash[BLAKE2_HASH_LENGTH]; + + /* ------------------------------------------ * + * Get download hash */ + + if (!CalculateFileHash(destPath.c_str(), downloadHash)) { + DeleteFile(destPath.c_str()); + Status(L"Update failed: Couldn't verify integrity of %s", + L"Visual C++ 2017 Redistributable"); + return false; + } + + /* ------------------------------------------ * + * If hashes do not match, integrity failed */ + + HashToString(downloadHash, downloadHashWide); + if (wcscmp(expectedHashWide, downloadHashWide) != 0) { + DeleteFile(destPath.c_str()); + Status(L"Update failed: Couldn't verify integrity of %s", + L"Visual C++ 2017 Redistributable"); + return false; + } + + /* ------------------------------------------ * + * If hashes match, install redist */ + + wchar_t commandline[MAX_PATH + MAX_PATH]; + StringCbPrintf(commandline, sizeof(commandline), + L"%s /install /quiet /norestart", destPath.c_str()); + + PROCESS_INFORMATION pi = {}; + STARTUPINFO si = {}; + si.cb = sizeof(si); + + bool success = !!CreateProcessW(destPath.c_str(), commandline, + nullptr, nullptr, false, CREATE_NO_WINDOW, + nullptr, nullptr, &si, &pi); + if (success) { + Status(L"Installing %s...", L"Visual C++ 2017 Redistributable"); + + CloseHandle(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + } else { + Status(L"Update failed: Could not execute " + L"%s (error code %d)", + L"Visual C++ 2017 Redistributable", + (int)GetLastError()); + } + + DeleteFile(destPath.c_str()); + + waitResult = WaitForSingleObject(cancelRequested, 0); + if (waitResult == WAIT_OBJECT_0) { + return false; + } + + return success; +} + static bool Update(wchar_t *cmdLine) { /* ------------------------------------- * @@ -1035,7 +1202,7 @@ static bool Update(wchar_t *cmdLine) GetLastError()); return false; } - if (!GetTempFileNameW(tempDirName, L"obs-studio", 0, tempPath)) { + if (!GetTempFileNameW(tempDirName, L"ebs-studio", 0, tempPath)) { Status(L"Update failed: Failed to create temp dir name: %ld", GetLastError()); return false; @@ -1091,6 +1258,15 @@ static bool Update(wchar_t *cmdLine) return true; } + /* ------------------------------------- * + * Check for VS2017 redistributables */ + + if (!HasVS2017Redist()) { + if (!UpdateVS2017Redists(root)) { + return false; + } + } + /* ------------------------------------- * * Generate file hash json */ @@ -1219,7 +1395,7 @@ static bool Update(wchar_t *cmdLine) if (!RunDownloadWorkers(2)) return false; - if (completedUpdates != updates.size()) { + if ((size_t)completedUpdates != updates.size()) { Status(L"Update failed to download all files."); return false; } @@ -1431,11 +1607,28 @@ static void RestartAsAdmin(LPWSTR lpCmdLine) } } +static bool HasElevation() +{ + SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY; + PSID sid = nullptr; + BOOL elevated = false; + BOOL success; + + success = AllocateAndInitializeSid(&sia, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &sid); + if (success && sid) { + CheckTokenMembership(nullptr, sid, &elevated); + FreeSid(sid); + } + + return elevated; +} + int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) { INITCOMMONCONTROLSEX icce; - if (!IsAppRunningAsAdminMode()) { + if (!HasElevation()) { HANDLE hLowMutex = CreateMutexW(nullptr, true, L"OBSUpdaterRunningAsNonAdminUser"); diff --git a/UI/win-update/updater/updater.hpp b/UI/win-update/updater/updater.hpp index 5f1abd773..cc5a59c94 100644 --- a/UI/win-update/updater/updater.hpp +++ b/UI/win-update/updater/updater.hpp @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2017-2018 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #pragma once #define WINVER 0x0600 diff --git a/UI/win-update/updater/updater.rc b/UI/win-update/updater/updater.rc index c099f5c0b..a9e9d25b2 100644 --- a/UI/win-update/updater/updater.rc +++ b/UI/win-update/updater/updater.rc @@ -7,7 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS diff --git a/UI/win-update/win-update.cpp b/UI/win-update/win-update.cpp index 23251ae86..418b554c3 100644 --- a/UI/win-update/win-update.cpp +++ b/UI/win-update/win-update.cpp @@ -1,12 +1,14 @@ #include "win-update-helpers.hpp" #include "update-window.hpp" #include "remote-text.hpp" +#include "qt-wrappers.hpp" #include "win-update.hpp" #include "obs-app.hpp" #include #include +#include #include #include @@ -26,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) @@ -373,7 +379,7 @@ try { vector extraHeaders; BPtr updateFilePath = GetConfigPathPtr( - "obs-studio\\updates\\updater.exe"); + "ebs-studio\\updates\\updater.exe"); if (CalculateFileHash(updateFilePath, updateFileHash)) { char hashString[BLAKE2_HASH_STR_LENGTH]; @@ -477,9 +483,35 @@ 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) { - QMessageBox::information(App()->GetMainWindow(), title, text); + OBSMessageBox::information(App()->GetMainWindow(), title, text); } void AutoUpdateThread::info(const QString &title, const QString &text) @@ -490,20 +522,20 @@ void AutoUpdateThread::info(const QString &title, const QString &text) Q_ARG(QString, text)); } -int AutoUpdateThread::queryUpdateSlot(bool manualUpdate, const QString &text) +int AutoUpdateThread::queryUpdateSlot(bool localManualUpdate, const QString &text) { - OBSUpdate updateDlg(App()->GetMainWindow(), manualUpdate, text); + OBSUpdate updateDlg(App()->GetMainWindow(), localManualUpdate, text); return updateDlg.exec(); } -int AutoUpdateThread::queryUpdate(bool manualUpdate, const char *text_utf8) +int AutoUpdateThread::queryUpdate(bool localManualUpdate, const char *text_utf8) { int ret = OBSUpdate::No; QString text = text_utf8; QMetaObject::invokeMethod(this, "queryUpdateSlot", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, ret), - Q_ARG(bool, manualUpdate), + Q_ARG(bool, localManualUpdate), Q_ARG(QString, text)); return ret; } @@ -536,7 +568,7 @@ try { string text; string error; string signature; - CryptProvider provider; + CryptProvider localProvider; BYTE manifestHash[BLAKE2_HASH_LENGTH]; bool updatesAvailable = false; bool success; @@ -550,7 +582,7 @@ try { } finishedTrigger; BPtr manifestPath = GetConfigPathPtr( - "obs-studio\\updates\\manifest.json"); + "ebs-studio\\updates\\manifest.json"); auto ActiveOrGameCaptureLocked = [this] () { @@ -579,7 +611,7 @@ try { /* ----------------------------------- * * create signature provider */ - if (!CryptAcquireContext(&provider, + if (!CryptAcquireContext(&localProvider, nullptr, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, @@ -587,7 +619,7 @@ try { throw strprintf("CryptAcquireContext failed: %lu", GetLastError()); - ::provider = provider; + provider = localProvider; /* ----------------------------------- * * avoid downloading manifest again */ @@ -604,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; @@ -638,7 +653,7 @@ try { if (responseCode == 404) return; - throw strprintf("Failed to fetch manifest file: %s", error); + throw strprintf("Failed to fetch manifest file: %s", error.c_str()); } /* ----------------------------------- * @@ -658,11 +673,11 @@ try { if (responseCode == 200) { if (!QuickWriteFile(manifestPath, text.data(), text.size())) throw strprintf("Could not write file '%s'", - std::string(manifestPath)); + manifestPath.Get()); } else { if (!QuickReadFile(manifestPath, text)) throw strprintf("Could not read file '%s'", - std::string(manifestPath)); + manifestPath.Get()); } /* ----------------------------------- * @@ -735,7 +750,7 @@ try { * execute updater */ BPtr updateFilePath = GetConfigPathPtr( - "obs-studio\\updates\\updater.exe"); + "ebs-studio\\updates\\updater.exe"); BPtr wUpdateFilePath; size_t size = os_utf8_to_wcs_ptr(updateFilePath, 0, &wUpdateFilePath); @@ -764,7 +779,7 @@ try { QString msg = QTStr("Updater.FailedToLaunch"); info(msg, msg); throw strprintf("Can't launch updater '%s': %d", - std::string(updateFilePath), GetLastError()); + updateFilePath.Get(), GetLastError()); } /* force OBS to perform another update check immediately after updating @@ -779,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 47bdd03be..c1bd8fb8b 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-about.cpp b/UI/window-basic-about.cpp new file mode 100644 index 000000000..e07fe1b6a --- /dev/null +++ b/UI/window-basic-about.cpp @@ -0,0 +1,122 @@ +#include "window-basic-about.hpp" +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" +#include +#include +#include +#include + +OBSAbout::OBSAbout(QWidget *parent) + : QDialog(parent), + ui(new Ui::OBSAbout) +{ + ui->setupUi(this); + + setFixedSize(size()); + + QString bitness; + + if(sizeof(void*) == 4) + bitness = " (32 bit)"; + else if(sizeof(void*) == 8) + bitness = " (64 bit)"; + + ui->version->setText( + QString::number(LIBOBS_API_MAJOR_VER) + "." + + QString::number(LIBOBS_API_MINOR_VER) + "." + + QString::number(LIBOBS_API_PATCH_VER) + + bitness); + + ui->contribute->setText(QTStr("About.Contribute")); + ui->donate->setText("" + + QTStr("About.Donate") + ""); + ui->donate->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui->donate->setOpenExternalLinks(true); + + ui->getInvolved->setText("" + + QTStr("About.GetInvolved") + ""); + ui->getInvolved->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui->getInvolved->setOpenExternalLinks(true); + + ui->about->setText("" + QTStr("About") + ""); + ui->authors->setText("" + QTStr("About.Authors") + ""); + ui->license->setText("" + QTStr("About.License") + ""); + + ui->textBrowser->hide(); + + ui->name->setProperty("themeID", "aboutName"); + ui->version->setProperty("themeID", "aboutVersion"); + ui->about->setProperty("themeID", "aboutHLayout"); + ui->authors->setProperty("themeID", "aboutHLayout"); + ui->license->setProperty("themeID", "aboutHLayout"); + ui->info->setProperty("themeID", "aboutInfo"); + + connect(ui->about, SIGNAL(clicked()), this, SLOT(ShowAbout())); + connect(ui->authors, SIGNAL(clicked()), this, SLOT(ShowAuthors())); + connect(ui->license, SIGNAL(clicked()), this, SLOT(ShowLicense())); +} + +void OBSAbout::ShowAbout() +{ + ui->textBrowser->hide(); + ui->info->show(); + ui->contribute->show(); + ui->donate->show(); + ui->getInvolved->show(); +} + +void OBSAbout::ShowAuthors() +{ + std::string path; + QString error = "Error! File could not be read.\n\n \ + Go to: https://github.com/obsproject/obs-studio/blob/master/AUTHORS"; + + if (!GetDataFilePath("authors/AUTHORS", path)) { + ui->textBrowser->setPlainText(error); + return; + } + + ui->textBrowser->setPlainText(QString::fromStdString(path)); + + BPtr text = os_quick_read_utf8_file(path.c_str()); + + if (!text || !*text) { + ui->textBrowser->setPlainText(error); + return; + } + + ui->textBrowser->setPlainText(QT_UTF8(text)); + + ui->info->hide(); + ui->contribute->hide(); + ui->donate->hide(); + ui->getInvolved->hide(); + ui->textBrowser->show(); +} + +void OBSAbout::ShowLicense() +{ + std::string path; + QString error = "Error! File could not be read.\n\n \ + Go to: https://github.com/obsproject/obs-studio/blob/master/COPYING"; + + if (!GetDataFilePath("license/gplv2.txt", path)) { + ui->textBrowser->setPlainText(error); + return; + } + + BPtr text = os_quick_read_utf8_file(path.c_str()); + + if (!text || !*text) { + ui->textBrowser->setPlainText(error); + return; + } + + ui->textBrowser->setPlainText(QT_UTF8(text)); + + ui->info->hide(); + ui->contribute->hide(); + ui->donate->hide(); + ui->getInvolved->hide(); + ui->textBrowser->show(); +} diff --git a/UI/window-basic-about.hpp b/UI/window-basic-about.hpp new file mode 100644 index 000000000..3a21bbe67 --- /dev/null +++ b/UI/window-basic-about.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "ui_OBSAbout.h" + +class OBSAbout : public QDialog { + Q_OBJECT + +public: + explicit OBSAbout(QWidget *parent = 0); + +private: + std::unique_ptr ui; + +private slots: + void ShowAbout(); + void ShowAuthors(); + void ShowLicense(); +}; diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index 2894f6bf2..312401901 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" @@ -36,7 +37,7 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) label = new QLabel(QTStr("Basic.AdvAudio.Mono")); label->setAlignment(Qt::AlignHCenter); mainLayout->addWidget(label, 0, idx++); - label = new QLabel(QTStr("Basic.AdvAudio.Panning")); + label = new QLabel(QTStr("Basic.AdvAudio.Balance")); label->setAlignment(Qt::AlignHCenter); mainLayout->addWidget(label, 0, idx++); label = new QLabel(QTStr("Basic.AdvAudio.SyncOffset")); @@ -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 86990c4b9..5ad489694 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -218,11 +218,11 @@ AutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) ui->bitrateLabel->setVisible(false); ui->bitrate->setVisible(false); - ui->streamType->addItem(obs_service_get_display_name("rtmp_common")); - ui->streamType->addItem(obs_service_get_display_name("rtmp_custom")); +// ui->streamType->addItem(obs_service_get_display_name("rtmp_common")); +// ui->streamType->addItem(obs_service_get_display_name("rtmp_custom")); ui->streamType->addItem(obs_service_get_display_name("webrtc_janus")); - ui->streamType->addItem(obs_service_get_display_name("webrtc_spankchain")); - ui->streamType->addItem(obs_service_get_display_name("webrtc_millicast")); +// ui->streamType->addItem(obs_service_get_display_name("webrtc_spankchain")); +// ui->streamType->addItem(obs_service_get_display_name("webrtc_millicast")); setTitle(QTStr("Basic.AutoConfig.StreamPage")); setSubTitle(QTStr("Basic.AutoConfig.StreamPage.SubTitle")); diff --git a/UI/window-basic-filters.cpp b/UI/window-basic-filters.cpp index d792b1e1d..51d5d867e 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); @@ -71,6 +72,14 @@ OBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_) const char *name = obs_source_get_name(source); setWindowTitle(QTStr("Basic.Filters.Title").arg(QT_UTF8(name))); +#ifndef QT_NO_SHORTCUT + ui->actionRemoveFilter->setShortcut(QApplication::translate("OBSBasicFilters", "Del", nullptr)); +#endif // QT_NO_SHORTCUT + + addAction(ui->actionRemoveFilter); + addAction(ui->actionMoveUp); + addAction(ui->actionMoveDown); + installEventFilter(CreateShortcutFilter()); connect(ui->asyncFilters->itemDelegate(), @@ -97,10 +106,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 +130,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() @@ -499,20 +515,20 @@ void OBSBasicFilters::OBSSourceReordered(void *param, calldata_t *data) UNUSED_PARAMETER(data); } -void OBSBasicFilters::SourceRemoved(void *data, calldata_t *params) +void OBSBasicFilters::SourceRemoved(void *param, calldata_t *data) { - UNUSED_PARAMETER(params); + UNUSED_PARAMETER(data); - QMetaObject::invokeMethod(static_cast(data), + QMetaObject::invokeMethod(static_cast(param), "close"); } -void OBSBasicFilters::SourceRenamed(void *data, calldata_t *params) +void OBSBasicFilters::SourceRenamed(void *param, calldata_t *data) { - const char *name = calldata_string(params, "new_name"); + const char *name = calldata_string(data, "new_name"); QString title = QTStr("Basic.Filters.Title").arg(QT_UTF8(name)); - QMetaObject::invokeMethod(static_cast(data), + QMetaObject::invokeMethod(static_cast(param), "setWindowTitle", Q_ARG(QString, title)); } @@ -569,7 +585,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 +627,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()); } @@ -651,6 +667,30 @@ void OBSBasicFilters::on_effectFilters_currentRowChanged(int row) UpdatePropertiesView(row, false); } +void OBSBasicFilters::on_actionRemoveFilter_triggered() +{ + if (ui->asyncFilters->hasFocus()) + on_removeAsyncFilter_clicked(); + else if (ui->effectFilters->hasFocus()) + on_removeEffectFilter_clicked(); +} + +void OBSBasicFilters::on_actionMoveUp_triggered() +{ + if (ui->asyncFilters->hasFocus()) + on_moveAsyncFilterUp_clicked(); + else if (ui->effectFilters->hasFocus()) + on_moveEffectFilterUp_clicked(); +} + +void OBSBasicFilters::on_actionMoveDown_triggered() +{ + if (ui->asyncFilters->hasFocus()) + on_moveAsyncFilterDown_clicked(); + else if (ui->effectFilters->hasFocus()) + on_moveEffectFilterDown_clicked(); +} + void OBSBasicFilters::CustomContextMenu(const QPoint &pos, bool async) { QListWidget *list = async ? ui->asyncFilters : ui->effectFilters; diff --git a/UI/window-basic-filters.hpp b/UI/window-basic-filters.hpp index cc655a408..5ab28a09d 100644 --- a/UI/window-basic-filters.hpp +++ b/UI/window-basic-filters.hpp @@ -72,6 +72,8 @@ class OBSBasicFilters : public QDialog { bool isAsync; + int noPreviewMargin; + private slots: void AddFilter(OBSSource filter); void RemoveFilter(OBSSource filter); @@ -98,6 +100,10 @@ private slots: void on_effectFilters_customContextMenuRequested(const QPoint &pos); void on_effectFilters_GotFocus(); + void on_actionRemoveFilter_triggered(); + void on_actionMoveUp_triggered(); + void on_actionMoveDown_triggered(); + void AsyncFilterNameEdited(QWidget *editor, QAbstractItemDelegate::EndEditHint endHint); void EffectFilterNameEdited(QWidget *editor, diff --git a/UI/window-basic-main-dropfiles.cpp b/UI/window-basic-main-dropfiles.cpp index 2622ab19d..cd5c48116 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 0c92abfb4..d7d3a2eb0 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -906,6 +906,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); @@ -1043,19 +1044,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_) @@ -1168,6 +1182,7 @@ void AdvancedOutput::UpdateStreamSettings() "ApplyServiceSettings"); OBSData settings = GetDataFromJsonFile("streamEncoder.json"); + ApplyEncoderDefaults(settings, h264Streaming); if (applyServiceSettings) obs_service_apply_encoder_settings(main->GetService(), @@ -1741,6 +1756,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 160e13c7f..60b6a08e4 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -31,7 +31,7 @@ void EnumProfiles(std::function &&cb) os_glob_t *glob; int ret = GetConfigPath(path, sizeof(path), - "obs-studio/basic/profiles/*"); + "ebs-studio/basic/profiles/*"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profiles config path"); return; @@ -122,7 +122,7 @@ static bool GetProfileName(QWidget *parent, std::string &name, return false; } - ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles/"); + ret = GetConfigPath(path, sizeof(path), "ebs-studio/basic/profiles/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profiles config path"); return false; @@ -143,11 +143,11 @@ static bool GetProfileName(QWidget *parent, std::string &name, static bool CopyProfile(const char *fromPartial, const char *to) { os_glob_t *glob; - char path[512]; + char path[514]; char dir[512]; int ret; - ret = GetConfigPath(dir, sizeof(dir), "obs-studio/basic/profiles/"); + ret = GetConfigPath(dir, sizeof(dir), "ebs-studio/basic/profiles/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profiles config path"); return false; @@ -197,7 +197,7 @@ bool OBSBasic::AddProfile(bool create_new, const char *title, const char *text, char baseDir[512]; int ret = GetConfigPath(baseDir, sizeof(baseDir), - "obs-studio/basic/profiles/"); + "ebs-studio/basic/profiles/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profiles config path"); return false; @@ -256,7 +256,7 @@ void OBSBasic::DeleteProfile(const char *profileName, const char *profileDir) char profilePath[512]; char basePath[512]; - int ret = GetConfigPath(basePath, 512, "obs-studio/basic/profiles"); + int ret = GetConfigPath(basePath, 512, "ebs-studio/basic/profiles"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profiles config path"); return; @@ -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() @@ -458,7 +471,7 @@ void OBSBasic::on_actionImportProfile_triggered() QString home = QDir::homePath(); - int ret = GetConfigPath(path, 512, "obs-studio/basic/profiles/"); + int ret = GetConfigPath(path, 512, "ebs-studio/basic/profiles/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profile config path"); return; @@ -507,7 +520,7 @@ void OBSBasic::on_actionExportProfile_triggered() QString::fromUtf8(config_get_string(App()->GlobalConfig(), "Basic", "ProfileDir")); - int ret = GetConfigPath(path, 512, "obs-studio/basic/profiles/"); + int ret = GetConfigPath(path, 512, "ebs-studio/basic/profiles/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get profile config path"); return; diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index 113f84803..19f7da5c5 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -34,7 +34,7 @@ void EnumSceneCollections(std::function &&cb) os_glob_t *glob; int ret = GetConfigPath(path, sizeof(path), - "obs-studio/basic/scenes/*.json"); + "ebs-studio/basic/scenes/*.json"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get config path for scene " "collections"); @@ -134,7 +134,7 @@ static bool GetSceneCollectionName(QWidget *parent, std::string &name, return false; } - ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes/"); + ret = GetConfigPath(path, sizeof(path), "ebs-studio/basic/scenes/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get scene collection config path"); return false; @@ -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); @@ -274,7 +281,7 @@ void OBSBasic::on_actionRenameSceneCollection_triggered() SaveProjectNow(); char path[512]; - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); + int ret = GetConfigPath(path, 512, "ebs-studio/basic/scenes/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get scene collection config path"); return; @@ -336,7 +343,7 @@ void OBSBasic::on_actionRemoveSceneCollection_triggered() return; char path[512]; - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); + int ret = GetConfigPath(path, 512, "ebs-studio/basic/scenes/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get scene collection config path"); return; @@ -374,7 +381,7 @@ void OBSBasic::on_actionImportSceneCollection_triggered() QString qhome = QDir::homePath(); - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); + int ret = GetConfigPath(path, 512, "ebs-studio/basic/scenes/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get scene collection config path"); return; @@ -439,7 +446,7 @@ void OBSBasic::on_actionExportSceneCollection_triggered() QString currentFile = QT_UTF8(config_get_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile")); - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); + int ret = GetConfigPath(path, 512, "ebs-studio/basic/scenes/"); if (ret <= 0) { blog(LOG_WARNING, "Failed to get scene collection config path"); return; diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 726eea134..952f5d4d2 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 da9ddfcb2..b94e5b1cf 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -17,7 +17,7 @@ along with this program. If not, see . ******************************************************************************/ -#include +#include #include #include #include @@ -25,15 +25,16 @@ #include #include #include -#include #include +#include +#include +#include #include #include #include #include #include -#include #include "obs-app.hpp" #include "platform.hpp" @@ -46,7 +47,6 @@ #include "window-basic-main.hpp" #include "window-basic-stats.hpp" #include "window-basic-main-outputs.hpp" -#include "window-basic-properties.hpp" #include "window-log-reply.hpp" #include "window-projector.hpp" #include "window-remux.hpp" @@ -54,12 +54,15 @@ #include "display-helpers.hpp" #include "volume-control.hpp" #include "remote-text.hpp" +#include +#include -#if defined(_WIN32) && defined(ENABLE_WIN_UPDATER) +#ifdef _WIN32 #include "win-update/win-update.hpp" #endif #include "ui_OBSBasic.h" +#include "ui_ColorSelect.h" #include #include @@ -67,8 +70,16 @@ #include #include +#include + +using namespace json11; using namespace std; +#if defined(_WIN32) && defined(BROWSER_AVAILABLE) +#include +static CREATE_BROWSER_WIDGET_PROC create_browser_widget = nullptr; +#endif + namespace { template @@ -103,10 +114,10 @@ static void AddExtraModulePaths() char base_module_dir[512]; #if defined(_WIN32) || defined(__APPLE__) int ret = GetProgramDataPath(base_module_dir, sizeof(base_module_dir), - "obs-studio/plugins/%module%"); + "ebs-studio/plugins/%module%"); #else int ret = GetConfigPath(base_module_dir, sizeof(base_module_dir), - "obs-studio/plugins/%module%"); + "ebs-studio/plugins/%module%"); #endif if (ret <= 0) @@ -116,8 +127,8 @@ static void AddExtraModulePaths() #if defined(__APPLE__) obs_add_module_path((path + "/bin").c_str(), (path + "/data").c_str()); - BPtr config_bin = os_get_config_path_ptr("obs-studio/plugins/%module%/bin"); - BPtr config_data = os_get_config_path_ptr("obs-studio/plugins/%module%/data"); + BPtr config_bin = os_get_config_path_ptr("ebs-studio/plugins/%module%/bin"); + BPtr config_data = os_get_config_path_ptr("ebs-studio/plugins/%module%/data"); obs_add_module_path(config_bin, config_data); #elif ARCH_BITS == 64 @@ -129,7 +140,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,21 +168,25 @@ 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); startingDockLayout = saveState(); - copyActionsDynamicProperties(); + statsDock = new QDockWidget(); + statsDock->setObjectName(QStringLiteral("statsDock")); + statsDock->setFeatures(QDockWidget::AllDockWidgetFeatures); + statsDock->setWindowTitle(QTStr("Basic.Stats")); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + statsDock->setVisible(false); + statsDock->setFloating(true); + statsDock->resize(700, 200); - ui->sources->setItemDelegate(new VisibilityItemDelegate(ui->sources)); + copyActionsDynamicProperties(); char styleSheetPath[512]; int ret = GetProfilePath(styleSheetPath, sizeof(styleSheetPath), @@ -191,7 +226,7 @@ OBSBasic::OBSBasic(QWidget *parent) installEventFilter(CreateShortcutFilter()); stringstream name; - name << "OBS " << App()->GetVersionString(); + name << "EBS " << App()->GetVersionString(); blog(LOG_INFO, "%s", name.str().c_str()); blog(LOG_INFO, "---------------------------------"); @@ -204,31 +239,35 @@ 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); + QAction *renameScene = new QAction(ui->scenesDock); + renameScene->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(renameScene, SIGNAL(triggered()), this, SLOT(EditSceneName())); + ui->scenesDock->addAction(renameScene); + + QAction *renameSource = new QAction(ui->sourcesDock); + renameSource->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(renameSource, SIGNAL(triggered()), this, + SLOT(EditSceneItemName())); + ui->sourcesDock->addAction(renameSource); #ifdef __APPLE__ - ui->actionRemoveSource->setShortcuts(DeleteKeys); - ui->actionRemoveScene->setShortcuts(DeleteKeys); + renameScene->setShortcut({Qt::Key_Return}); + renameSource->setShortcut({Qt::Key_Return}); + + ui->actionRemoveSource->setShortcuts({Qt::Key_Backspace}); + ui->actionRemoveScene->setShortcuts({Qt::Key_Backspace}); ui->action_Settings->setMenuRole(QAction::PreferencesRole); ui->actionE_xit->setMenuRole(QAction::QuitRole); +#else + renameScene->setShortcut({Qt::Key_F2}); + renameSource->setShortcut({Qt::Key_F2}); #endif auto addNudge = [this](const QKeySequence &seq, const char *s) @@ -245,7 +284,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) { @@ -271,6 +310,7 @@ OBSBasic::OBSBasic(QWidget *parent) assignDockToggle(ui->mixerDock, ui->toggleMixer); assignDockToggle(ui->transitionsDock, ui->toggleTransitions); assignDockToggle(ui->controlsDock, ui->toggleControls); + assignDockToggle(statsDock, ui->toggleStats); //hide all docking panes ui->toggleScenes->setChecked(false); @@ -278,6 +318,9 @@ OBSBasic::OBSBasic(QWidget *parent) ui->toggleMixer->setChecked(false); ui->toggleTransitions->setChecked(false); ui->toggleControls->setChecked(false); + ui->toggleStats->setChecked(false); + + QPoint curPos; //restore parent window geometry const char *geometry = config_get_string(App()->GlobalConfig(), @@ -295,7 +338,19 @@ OBSBasic::OBSBasic(QWidget *parent) Qt::AlignCenter, size(), rect)); } + + curPos = pos(); + } else { + QRect desktopRect = QGuiApplication::primaryScreen()->geometry(); + QSize adjSize = desktopRect.size() / 2 - size() / 2; + curPos = QPoint(adjSize.width(), adjSize.height()); } + + QPoint curSize(width(), height()); + QPoint statsDockSize(statsDock->width(), statsDock->height()); + QPoint statsDockPos = curSize / 2 - statsDockSize / 2; + QPoint newPos = curPos + statsDockPos; + statsDock->move(newPos); } static void SaveAudioDevice(const char *name, int channel, obs_data_t *parent, @@ -319,10 +374,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(); @@ -334,9 +386,16 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, SaveAudioDevice(AUX_AUDIO_1, 3, saveData, audioSources); SaveAudioDevice(AUX_AUDIO_2, 4, saveData, audioSources); SaveAudioDevice(AUX_AUDIO_3, 5, saveData, audioSources); + SaveAudioDevice(AUX_AUDIO_4, 6, 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 +407,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 +432,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 +477,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 +525,41 @@ obs_data_array_t *OBSBasic::SaveSceneListOrder() obs_data_array_t *OBSBasic::SaveProjectors() { - obs_data_array_t *saveProjector = obs_data_array_create(); - - 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); - obs_data_release(data); - } - - return saveProjector; -} - -obs_data_array_t *OBSBasic::SavePreviewProjectors() -{ - obs_data_array_t *saveProjector = obs_data_array_create(); + obs_data_array_t *savedProjectors = obs_data_array_create(); - 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(); + auto saveProjector = [savedProjectors](OBSProjector *projector) { + if (!projector) + return; - 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); + 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; -} + }; -obs_data_array_t *OBSBasic::SaveMultiviewProjectors() -{ - obs_data_array_t *saveProjector = obs_data_array_create(); + for (QPointer &proj : projectors) + saveProjector(static_cast(proj.data())); - 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); - } + for (QPointer &proj : windowProjectors) + saveProjector(static_cast(proj.data())); - return saveProjector; + return savedProjectors; } void OBSBasic::Save(const char *file) @@ -508,17 +573,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 +602,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 +678,6 @@ void OBSBasic::CreateDefaultScene(bool firstStart) if (firstStart) CreateFirstRunSources(); - AddScene(obs_scene_get_source(scene)); SetCurrentScene(scene, true); obs_scene_release(scene); @@ -653,47 +719,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 +812,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"); @@ -817,8 +852,16 @@ void OBSBasic::Load(const char *file) LoadAudioDevice(AUX_AUDIO_1, 3, data); LoadAudioDevice(AUX_AUDIO_2, 4, data); LoadAudioDevice(AUX_AUDIO_3, 5, data); + LoadAudioDevice(AUX_AUDIO_4, 6, data); + + if (!sources) { + sources = groups; + groups = nullptr; + } else { + obs_data_array_push_back_array(sources, groups); + } - obs_load_sources(sources, OBSBasic::SourceLoaded, this); + obs_load_sources(sources, nullptr, nullptr); if (transitions) LoadTransitions(transitions); @@ -834,47 +877,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 +905,29 @@ void OBSBasic::Load(const char *file) 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); @@ -955,12 +978,12 @@ void OBSBasic::Load(const char *file) opt_start_streaming = false; } - if (opt_start_recording) { - blog(LOG_INFO, "Starting recording due to command line parameter"); - QMetaObject::invokeMethod(this, "StartRecording", - Qt::QueuedConnection); - opt_start_recording = false; - } +// if (opt_start_recording) { +// blog(LOG_INFO, "Starting recording due to command line parameter"); +// QMetaObject::invokeMethod(this, "StartRecording", +// Qt::QueuedConnection); +// opt_start_recording = false; +// } if (opt_start_replaybuffer) { QMetaObject::invokeMethod(this, "StartReplayBuffer", @@ -972,8 +995,10 @@ void OBSBasic::Load(const char *file) 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" @@ -1015,7 +1040,7 @@ bool OBSBasic::LoadService() obs_data_t *data = obs_data_create_from_json_file_safe(serviceJsonPath, "bak"); - obs_data_set_default_string(data, "type", "rtmp_common"); + obs_data_set_default_string(data, "type", "webrtc_janus"); type = obs_data_get_string(data, "type"); obs_data_t *settings = obs_data_get_obj(data, "settings"); @@ -1039,7 +1064,7 @@ bool OBSBasic::InitService() if (LoadService()) return true; - service = obs_service_create("rtmp_common", "default_service", nullptr, + service = obs_service_create("webrtc_janus", "default_service", nullptr, nullptr); if (!service) return false; @@ -1247,6 +1272,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 +1322,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 +1405,7 @@ void OBSBasic::ResetOutputs() replayBufferButton = new QPushButton( QTStr("Basic.Main.StartReplayBuffer"), this); + replayBufferButton->setCheckable(true); connect(replayBufferButton.data(), &QPushButton::clicked, this, @@ -1402,8 +1431,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." @@ -1425,7 +1452,7 @@ void OBSBasic::OBSInit() if (!sceneCollection) throw "Failed to get scene collection name"; - ret = snprintf(fileName, 512, "obs-studio/basic/scenes/%s.json", + ret = snprintf(fileName, 512, "ebs-studio/basic/scenes/%s.json", sceneCollection); if (ret <= 0) throw "Failed to create scene collection file name"; @@ -1469,8 +1496,6 @@ void OBSBasic::OBSInit() InitOBSCallbacks(); InitHotkeys(); - api = InitializeAPIInterface(this); - AddExtraModulePaths(); blog(LOG_INFO, "---------------------------------"); obs_load_all_modules(); @@ -1479,6 +1504,10 @@ void OBSBasic::OBSInit() blog(LOG_INFO, "---------------------------------"); obs_post_load_modules(); +#if defined(_WIN32) && defined(BROWSER_AVAILABLE) + create_browser_widget = obs_browser_init_panel(); +#endif + CheckForSimpleModeX264Fallback(); blog(LOG_INFO, STARTUP_SEPARATOR); @@ -1520,12 +1549,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 +1578,9 @@ void OBSBasic::OBSInit() } #endif +#ifndef __APPLE__ RefreshSceneCollections(); +#endif RefreshProfiles(); disableSaving--; @@ -1579,6 +1612,10 @@ void OBSBasic::OBSInit() show(); #endif + /* setup stats dock */ + OBSBasicStats *statsDlg = new OBSBasicStats(statsDock, false); + statsDock->setWidget(statsDlg); + const char *dockStateStr = config_get_string(App()->GlobalConfig(), "BasicWindow", "DockState"); if (!dockStateStr) { @@ -1600,9 +1637,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; @@ -1618,24 +1655,25 @@ void OBSBasic::OBSInit() config_save_safe(App()->GlobalConfig(), "tmp", nullptr); } - 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"), - msg); - - if (button == QMessageBox::Yes) { - on_autoConfigure_triggered(); - } else { - msg = QTStr("Basic.FirstStartup.RunWizard.NoClicked"); - OBSMessageBox::information(this, - QTStr("Basic.AutoConfig"), msg); - } - } +// if (!first_run && !has_last_version && !Active()) { +// QString msg; +// msg = QTStr("Basic.FirstStartup.RunWizard"); +// +// QMessageBox::StandardButton button = +// OBSMessageBox::question(this, QTStr("Basic.AutoConfig"), +// msg); +// +// if (button == QMessageBox::Yes) { +// on_autoConfigure_triggered(); +// } else { +// msg = QTStr("Basic.FirstStartup.RunWizard.NoClicked"); +// OBSMessageBox::information(this, +// QTStr("Basic.AutoConfig"), msg); +// } +// } + + ToggleMixerLayout(config_get_bool(App()->GlobalConfig(), "BasicWindow", + "VerticalVolControl")); if (config_get_bool(basicConfig, "General", "OpenStatsOnStartup")) on_stats_triggered(); @@ -1647,80 +1685,271 @@ void OBSBasic::OBSInit() ui->viewMenu->addSeparator(); - QMenu *multiviewProjectorMenu = new QMenu(QTStr("MultiviewProjector")); + multiviewProjectorMenu = new QMenu(QTStr("MultiviewProjector")); + ui->viewMenu->addMenu(multiviewProjectorMenu); AddProjectorMenuMonitors(multiviewProjectorMenu, this, SLOT(OpenMultiviewProjector())); - ui->viewMenu->addMenu(multiviewProjectorMenu); - + connect(ui->viewMenu->menuAction(), &QAction::hovered, this, + &OBSBasic::UpdateMultiviewProjectorMenu); ui->viewMenu->addAction(QTStr("MultiviewWindowed"), this, SLOT(OpenMultiviewWindow())); -} -void OBSBasic::InitHotkeys() -{ - ProfileScope("OBSBasic::InitHotkeys"); +#if !defined(_WIN32) && !defined(__APPLE__) + delete ui->actionShowCrashLogs; + delete ui->actionUploadLastCrashLog; + delete ui->menuCrashLogs; + delete ui->actionCheckForUpdates; + ui->actionShowCrashLogs = nullptr; + ui->actionUploadLastCrashLog = nullptr; + ui->menuCrashLogs = nullptr; + ui->actionCheckForUpdates = nullptr; +#endif - struct obs_hotkeys_translations t = {}; - t.insert = Str("Hotkeys.Insert"); - t.del = Str("Hotkeys.Delete"); - t.home = Str("Hotkeys.Home"); - t.end = Str("Hotkeys.End"); - t.page_up = Str("Hotkeys.PageUp"); - t.page_down = Str("Hotkeys.PageDown"); - t.num_lock = Str("Hotkeys.NumLock"); - t.scroll_lock = Str("Hotkeys.ScrollLock"); - t.caps_lock = Str("Hotkeys.CapsLock"); - t.backspace = Str("Hotkeys.Backspace"); - t.tab = Str("Hotkeys.Tab"); - t.print = Str("Hotkeys.Print"); - t.pause = Str("Hotkeys.Pause"); - t.left = Str("Hotkeys.Left"); - t.right = Str("Hotkeys.Right"); - t.up = Str("Hotkeys.Up"); - t.down = Str("Hotkeys.Down"); -#ifdef _WIN32 - t.meta = Str("Hotkeys.Windows"); +#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 - t.meta = Str("Hotkeys.Super"); + OnFirstLoad(); #endif - t.menu = Str("Hotkeys.Menu"); - t.space = Str("Hotkeys.Space"); - t.numpad_num = Str("Hotkeys.NumpadNum"); - t.numpad_multiply = Str("Hotkeys.NumpadMultiply"); - t.numpad_divide = Str("Hotkeys.NumpadDivide"); - t.numpad_plus = Str("Hotkeys.NumpadAdd"); - t.numpad_minus = Str("Hotkeys.NumpadSubtract"); - t.numpad_decimal = Str("Hotkeys.NumpadDecimal"); - t.apple_keypad_num = Str("Hotkeys.AppleKeypadNum"); - t.apple_keypad_multiply = Str("Hotkeys.AppleKeypadMultiply"); - t.apple_keypad_divide = Str("Hotkeys.AppleKeypadDivide"); - t.apple_keypad_plus = Str("Hotkeys.AppleKeypadAdd"); - t.apple_keypad_minus = Str("Hotkeys.AppleKeypadSubtract"); - t.apple_keypad_decimal = Str("Hotkeys.AppleKeypadDecimal"); - t.apple_keypad_equal = Str("Hotkeys.AppleKeypadEqual"); - t.mouse_num = Str("Hotkeys.MouseButton"); - obs_hotkeys_set_translations(&t); - - obs_hotkeys_set_audio_hotkeys_translations(Str("Mute"), Str("Unmute"), - Str("Push-to-mute"), Str("Push-to-talk")); - - obs_hotkeys_set_sceneitem_hotkeys_translations( - Str("SceneItemShow"), Str("SceneItemHide")); - - obs_hotkey_enable_callback_rerouting(true); - obs_hotkey_set_callback_routing_func(OBSBasic::HotkeyTriggered, this); } -void OBSBasic::ProcessHotkey(obs_hotkey_id id, bool pressed) +void OBSBasic::OnFirstLoad() { - obs_hotkey_trigger_routed_callback(id, pressed); + if (api) + api->on_event(OBS_FRONTEND_EVENT_FINISHED_LOADING); + +#if defined(_WIN32) && defined(BROWSER_AVAILABLE) + /* 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::HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed) +void OBSBasic::DeferredLoad(const QString &file, int requeueCount) { - OBSBasic &basic = *static_cast(data); - QMetaObject::invokeMethod(&basic, "ProcessHotkey", - Q_ARG(obs_hotkey_id, id), Q_ARG(bool, pressed)); + 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 BROWSER_AVAILABLE +#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 +#else + UNUSED_PARAMETER(text); +#endif +} + +void OBSBasic::UpdateMultiviewProjectorMenu() +{ + multiviewProjectorMenu->clear(); + AddProjectorMenuMonitors(multiviewProjectorMenu, this, + SLOT(OpenMultiviewProjector())); +} + +void OBSBasic::InitHotkeys() +{ + ProfileScope("OBSBasic::InitHotkeys"); + + struct obs_hotkeys_translations t = {}; + t.insert = Str("Hotkeys.Insert"); + t.del = Str("Hotkeys.Delete"); + t.home = Str("Hotkeys.Home"); + t.end = Str("Hotkeys.End"); + t.page_up = Str("Hotkeys.PageUp"); + t.page_down = Str("Hotkeys.PageDown"); + t.num_lock = Str("Hotkeys.NumLock"); + t.scroll_lock = Str("Hotkeys.ScrollLock"); + t.caps_lock = Str("Hotkeys.CapsLock"); + t.backspace = Str("Hotkeys.Backspace"); + t.tab = Str("Hotkeys.Tab"); + t.print = Str("Hotkeys.Print"); + t.pause = Str("Hotkeys.Pause"); + t.left = Str("Hotkeys.Left"); + t.right = Str("Hotkeys.Right"); + t.up = Str("Hotkeys.Up"); + t.down = Str("Hotkeys.Down"); +#ifdef _WIN32 + t.meta = Str("Hotkeys.Windows"); +#else + t.meta = Str("Hotkeys.Super"); +#endif + t.menu = Str("Hotkeys.Menu"); + t.space = Str("Hotkeys.Space"); + t.numpad_num = Str("Hotkeys.NumpadNum"); + t.numpad_multiply = Str("Hotkeys.NumpadMultiply"); + t.numpad_divide = Str("Hotkeys.NumpadDivide"); + t.numpad_plus = Str("Hotkeys.NumpadAdd"); + t.numpad_minus = Str("Hotkeys.NumpadSubtract"); + t.numpad_decimal = Str("Hotkeys.NumpadDecimal"); + t.apple_keypad_num = Str("Hotkeys.AppleKeypadNum"); + t.apple_keypad_multiply = Str("Hotkeys.AppleKeypadMultiply"); + t.apple_keypad_divide = Str("Hotkeys.AppleKeypadDivide"); + t.apple_keypad_plus = Str("Hotkeys.AppleKeypadAdd"); + t.apple_keypad_minus = Str("Hotkeys.AppleKeypadSubtract"); + t.apple_keypad_decimal = Str("Hotkeys.AppleKeypadDecimal"); + t.apple_keypad_equal = Str("Hotkeys.AppleKeypadEqual"); + t.mouse_num = Str("Hotkeys.MouseButton"); + obs_hotkeys_set_translations(&t); + + obs_hotkeys_set_audio_hotkeys_translations(Str("Mute"), Str("Unmute"), + Str("Push-to-mute"), Str("Push-to-talk")); + + obs_hotkeys_set_sceneitem_hotkeys_translations( + Str("SceneItemShow"), Str("SceneItemHide")); + + obs_hotkey_enable_callback_rerouting(true); + obs_hotkey_set_callback_routing_func(OBSBasic::HotkeyTriggered, this); +} + +void OBSBasic::ProcessHotkey(obs_hotkey_id id, bool pressed) +{ + obs_hotkey_trigger_routed_callback(id, pressed); +} + +void OBSBasic::HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed) +{ + OBSBasic &basic = *static_cast(data); + QMetaObject::invokeMethod(&basic, "ProcessHotkey", + Q_ARG(obs_hotkey_id, id), Q_ARG(bool, pressed)); } void OBSBasic::CreateHotkeys() @@ -1769,7 +1998,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; \ @@ -1782,9 +2011,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, @@ -1805,18 +2036,20 @@ void OBSBasic::CreateHotkeys() LoadHotkey(forceStreamingStopHotkey, "OBSBasic.ForceStopStreaming"); - recordingHotkeys = obs_hotkey_pair_register_frontend( - "OBSBasic.StartRecording", - Str("Basic.Main.StartRecording"), - "OBSBasic.StopRecording", - Str("Basic.Main.StopRecording"), - MAKE_CALLBACK(!basic.outputHandler->RecordingActive(), - basic.StartRecording, "Starting recording"), - MAKE_CALLBACK(basic.outputHandler->RecordingActive(), - basic.StopRecording, "Stopping recording"), - this, this); - LoadHotkeyPair(recordingHotkeys, - "OBSBasic.StartRecording", "OBSBasic.StopRecording"); +// recordingHotkeys = obs_hotkey_pair_register_frontend( +// "OBSBasic.StartRecording", +// Str("Basic.Main.StartRecording"), +// "OBSBasic.StopRecording", +// Str("Basic.Main.StopRecording"), +// MAKE_CALLBACK(!basic.outputHandler->RecordingActive() && +// !basic.ui->recordButton->isChecked(), +// basic.StartRecording, "Starting recording"), +// MAKE_CALLBACK(basic.outputHandler->RecordingActive() && +// basic.ui->recordButton->isChecked(), +// basic.StopRecording, "Stopping recording"), +// this, this); +// LoadHotkeyPair(recordingHotkeys, +// "OBSBasic.StartRecording", "OBSBasic.StopRecording"); replayBufHotkeys = obs_hotkey_pair_register_frontend( "OBSBasic.StartReplayBuffer", @@ -1865,7 +2098,7 @@ void OBSBasic::CreateHotkeys() void OBSBasic::ClearHotkeys() { obs_hotkey_pair_unregister(streamingHotkeys); - obs_hotkey_pair_unregister(recordingHotkeys); +// obs_hotkey_pair_unregister(recordingHotkeys); obs_hotkey_pair_unregister(replayBufHotkeys); obs_hotkey_unregister(forceStreamingStopHotkey); obs_hotkey_unregister(togglePreviewProgramHotkey); @@ -1877,6 +2110,8 @@ OBSBasic::~OBSBasic() if (updateCheckThread && updateCheckThread->isRunning()) updateCheckThread->wait(); + delete multiviewProjectorMenu; + delete trayMenu; delete programOptions; delete program; @@ -1910,6 +2145,9 @@ OBSBasic::~OBSBasic() if (advAudioWindow) delete advAudioWindow; + if (about) + delete about; + obs_display_remove_draw_callback(ui->preview->GetDisplay(), OBSBasic::RenderMain, this); @@ -1933,6 +2171,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); @@ -2002,7 +2244,7 @@ void OBSBasic::SaveProjectDeferred() if (!sceneCollection) return; - ret = snprintf(fileName, 512, "obs-studio/basic/scenes/%s.json", + ret = snprintf(fileName, 512, "ebs-studio/basic/scenes/%s.json", sceneCollection); if (ret <= 0) return; @@ -2032,7 +2274,7 @@ OBSSceneItem OBSBasic::GetSceneItem(QListWidgetItem *item) OBSSceneItem OBSBasic::GetCurrentSceneItem() { - return GetSceneItem(GetTopSelectedSourceItem()); + return ui->sources->Get(GetTopSelectedSourceItem()); } void OBSBasic::UpdatePreviewScalingMenu() @@ -2055,32 +2297,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) @@ -2144,8 +2360,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", @@ -2207,7 +2421,7 @@ void OBSBasic::RemoveScene(OBSSource source) if (sel != nullptr) { if (sel == ui->scenes->currentItem()) - ClearListItems(ui->sources); + ui->sources->Clear(); delete sel; } @@ -2224,46 +2438,37 @@ void OBSBasic::RemoveScene(OBSSource source) api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); } -void OBSBasic::AddSceneItem(OBSSceneItem item) +static bool select_one(obs_scene_t *scene, obs_sceneitem_t *item, void *param) { - obs_scene_t *scene = obs_sceneitem_get_scene(item); - - if (GetCurrentScene() == scene) - InsertSceneItem(item); + obs_sceneitem_t *selectedItem = + reinterpret_cast(param); + if (obs_sceneitem_is_group(item)) + obs_sceneitem_group_enum_items(item, select_one, param); - SaveProject(); + obs_sceneitem_select(item, (selectedItem == item)); - if (!disableSaving) { - obs_source_t *sceneSource = obs_scene_get_source(scene); - obs_source_t *itemSource = obs_sceneitem_get_source(item); - blog(LOG_INFO, "User added source '%s' (%s) to scene '%s'", - obs_source_get_name(itemSource), - obs_source_get_id(itemSource), - obs_source_get_name(sceneSource)); - } + UNUSED_PARAMETER(scene); + return true; } -void OBSBasic::RemoveSceneItem(OBSSceneItem item) +void OBSBasic::AddSceneItem(OBSSceneItem item) { - for (int i = 0; i < ui->sources->count(); i++) { - QListWidgetItem *listItem = ui->sources->item(i); + obs_scene_t *scene = obs_sceneitem_get_scene(item); - if (GetOBSRef(listItem) == item) { - DeleteListItem(ui->sources, listItem); - break; - } - } + if (GetCurrentScene() == scene) + ui->sources->Add(item); 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'", + blog(LOG_INFO, "User added source '%s' (%s) to 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); } } @@ -2284,7 +2489,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); } } } @@ -2309,13 +2517,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(); @@ -2331,19 +2533,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) @@ -2484,6 +2674,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, @@ -2508,6 +2703,12 @@ void OBSBasic::VolControlContextMenu() /* ------------------- */ + connect(&toggleControlLayoutAction, &QAction::changed, this, + &OBSBasic::ToggleVolControlLayout, + Qt::DirectConnection); + + /* ------------------- */ + hideAction.setProperty("volControl", QVariant::fromValue(vol)); mixerRenameAction.setProperty("volControl", @@ -2520,23 +2721,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, @@ -2549,23 +2767,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, @@ -2573,8 +2851,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) @@ -2590,7 +2874,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) { @@ -2634,7 +2919,7 @@ void OBSBasic::TimedCheckForUpdates() #ifdef UPDATE_SPARKLE init_sparkle_updater(config_get_bool(App()->GlobalConfig(), "General", "UpdateToUndeployed")); -#elif ENABLE_WIN_UPDATER +#elif _WIN32 long long lastUpdate = config_get_int(App()->GlobalConfig(), "General", "LastUpdateCheck"); uint32_t lastVersion = config_get_int(App()->GlobalConfig(), "General", @@ -2658,13 +2943,13 @@ void OBSBasic::CheckForUpdates(bool manualUpdate) { #ifdef UPDATE_SPARKLE trigger_sparkle_update(); -#elif ENABLE_WIN_UPDATER +#elif _WIN32 ui->actionCheckForUpdates->setEnabled(false); if (updateCheckThread && updateCheckThread->isRunning()) return; - updateCheckThread = new AutoUpdateThread(manualUpdate); + updateCheckThread.reset(new AutoUpdateThread(manualUpdate)); updateCheckThread->start(); #endif @@ -2725,7 +3010,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); @@ -2757,62 +3041,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(); } @@ -2838,16 +3072,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); @@ -2873,13 +3097,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))); } @@ -3018,7 +3242,7 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) obs_service_t *OBSBasic::GetService() { if (!service) { - service = obs_service_create("rtmp_common", NULL, NULL, + service = obs_service_create("webrtc_janus", NULL, NULL, nullptr); obs_service_release(service); } @@ -3164,7 +3388,7 @@ int OBSBasic::ResetVideo() /* Try OpenGL if DirectX fails on windows */ if (astrcmpi(ovi.graphics_module, DL_OPENGL) != 0) { - blog(LOG_WARNING, "Failed to initialize obs video (%d) " + blog(LOG_WARNING, "Failed to initialize ebs video (%d) " "with graphics_module='%s', retrying " "with graphics_module='%s'", ret, ovi.graphics_module, @@ -3178,8 +3402,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; } @@ -3301,6 +3527,7 @@ void OBSBasic::CloseDialogs() } if (!stats.isNull()) stats->close(); //call close to save Stats geometry + if (!remux.isNull()) remux->close(); } void OBSBasic::EnumDialogs() @@ -3334,7 +3561,7 @@ void OBSBasic::ClearSceneData() ClearVolumeControls(); ClearListItems(ui->scenes); - ClearListItems(ui->sources); + ui->sources->Clear(); ClearQuickTransitions(); ui->transitions->clear(); @@ -3396,6 +3623,8 @@ void OBSBasic::closeEvent(QCloseEvent *event) blog(LOG_INFO, SHUTDOWN_SEPARATOR); + if (introCheckThread) + introCheckThread->wait(); if (updateCheckThread) updateCheckThread->wait(); if (logUploadThread) @@ -3431,21 +3660,34 @@ void OBSBasic::changeEvent(QEvent *event) void OBSBasic::on_actionShow_Recordings_triggered() { - 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"); - QDesktopServices::openUrl(QUrl::fromLocalFile(path)); +// 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") : +// 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() @@ -3497,6 +3739,9 @@ void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current, SetCurrentScene(source); + if (api) + api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); + UNUSED_PARAMETER(prev); } @@ -3520,10 +3765,10 @@ static void AddProjectorMenuMonitors(QMenu *parent, QObject *target, QString str = QString("%1 %2: %3x%4 @ %5,%6"). arg(QTStr("Display"), QString::number(i), - QString::number((int)screenGeometry.width()), - QString::number((int)screenGeometry.height()), - QString::number((int)screenGeometry.x()), - QString::number((int)screenGeometry.y())); + QString::number(screenGeometry.width()), + QString::number(screenGeometry.height()), + QString::number(screenGeometry.x()), + QString::number(screenGeometry.y())); action = parent->addAction(str, target, slot); action->setProperty("monitor", i); @@ -3547,8 +3792,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"), @@ -3599,7 +3843,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"); @@ -3656,7 +3900,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); } @@ -3689,6 +3932,8 @@ void OBSBasic::ChangeSceneIndex(bool relative, int offset, int invalidIdx) item->setSelected(true); sceneChanging = false; + + OBSProjector::UpdateMultiviewProjectors(); } void OBSBasic::on_actionSceneUp_triggered() @@ -3712,45 +3957,11 @@ void OBSBasic::MoveSceneToBottom() ui->scenes->count() - 1); } -void OBSBasic::on_sources_itemSelectionChanged() +void OBSBasic::EditSceneItemName() { - 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() { @@ -3849,7 +4060,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; @@ -3890,6 +4158,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); @@ -3901,11 +4180,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) == @@ -3914,11 +4193,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); @@ -3948,6 +4227,20 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) popup.addSeparator(); } + QAction *resizeOutput = popup.addAction( + QTStr("ResizeOutputSizeOfSource"), this, + SLOT(ResizeOutputSizeOfSource())); + + int width = obs_source_get_width(source); + int height = obs_source_get_height(source); + + resizeOutput->setEnabled(!(ui->streamButton->isChecked() || + (replayBufferButton && + replayBufferButton->isChecked()))); + + if (width == 0 || height == 0) + resizeOutput->setEnabled(false); + popup.addMenu(AddScaleFilteringMenu(sceneItem)); popup.addSeparator(); @@ -3968,6 +4261,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()); @@ -3975,20 +4270,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) @@ -4014,7 +4299,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); } } @@ -4072,6 +4357,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; @@ -4082,6 +4374,7 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu() popup = nullptr; } else if (foundDeprecated) { + popup->addSeparator(); popup->addMenu(deprecated); } @@ -4107,7 +4400,7 @@ void OBSBasic::AddSourcePopupMenu(const QPoint &pos) return; } - QPointer popup = CreateAddSourcePopupMenu(); + QScopedPointer popup(CreateAddSourcePopupMenu()); if (popup) popup->exec(pos); } @@ -4117,20 +4410,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; @@ -4220,10 +4517,10 @@ void OBSBasic::on_actionMoveToBottom_triggered() obs_sceneitem_set_order(item, OBS_ORDER_MOVE_BOTTOM); } -static BPtr ReadLogFile(const char *log) +static BPtr ReadLogFile(const char *subdir, const char *log) { char logDir[512]; - if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) + if (GetConfigPath(logDir, sizeof(logDir), subdir) <= 0) return nullptr; string path = (char*)logDir; @@ -4237,9 +4534,9 @@ static BPtr ReadLogFile(const char *log) return file; } -void OBSBasic::UploadLog(const char *file) +void OBSBasic::UploadLog(const char *subdir, const char *file) { - BPtr fileString{ReadLogFile(file)}; + BPtr fileString{ReadLogFile(subdir, file)}; if (!fileString) return; @@ -4249,42 +4546,21 @@ void OBSBasic::UploadLog(const char *file) ui->menuLogFiles->setEnabled(false); - auto data_deleter = [](obs_data_t *d) { obs_data_release(d); }; - using data_t = unique_ptr; - - data_t content{obs_data_create(), data_deleter}; - data_t files{obs_data_create(), data_deleter}; - data_t request{obs_data_create(), data_deleter}; - - obs_data_set_string(content.get(), "content", fileString); - - obs_data_set_obj(files.get(), file, content.get()); - stringstream ss; - ss << "OBS " << App()->GetVersionString() - << " log file uploaded at " << CurrentDateTimeString(); - obs_data_set_string(request.get(), "description", ss.str().c_str()); - obs_data_set_bool(request.get(), "public", false); - obs_data_set_obj(request.get(), "files", files.get()); - - const char *json = obs_data_get_json(request.get()); - if (!json) { - blog(LOG_ERROR, "Failed to get JSON data for log upload"); - return; - } + ss << "EBS " << App()->GetVersionString() + << " log file uploaded at " << CurrentDateTimeString() + << "\n\n" << fileString; - QBuffer *postData = new QBuffer(); - postData->setData(json, (int) strlen(json)); if (logUploadThread) { logUploadThread->wait(); - delete logUploadThread; } RemoteTextThread *thread = new RemoteTextThread( - "https://api.github.com/gists", - "application/json", json); - logUploadThread = thread; + "https://obsproject.com/logs/upload", + "text/plain", ss.str().c_str()); + + logUploadThread.reset(thread); connect(thread, &RemoteTextThread::Result, this, &OBSBasic::logUploadFinished); logUploadThread->start(); @@ -4293,7 +4569,7 @@ void OBSBasic::UploadLog(const char *file) void OBSBasic::on_actionShowLogs_triggered() { char logDir[512]; - if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) + if (GetConfigPath(logDir, sizeof(logDir), "ebs-studio/logs") <= 0) return; QUrl url = QUrl::fromLocalFile(QT_UTF8(logDir)); @@ -4302,18 +4578,18 @@ void OBSBasic::on_actionShowLogs_triggered() void OBSBasic::on_actionUploadCurrentLog_triggered() { - UploadLog(App()->GetCurrentLog()); + UploadLog("ebs-studio/logs", App()->GetCurrentLog()); } void OBSBasic::on_actionUploadLastLog_triggered() { - UploadLog(App()->GetLastLog()); + UploadLog("ebs-studio/logs", App()->GetLastLog()); } void OBSBasic::on_actionViewCurrentLog_triggered() { char logDir[512]; - if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) + if (GetConfigPath(logDir, sizeof(logDir), "ebs-studio/logs") <= 0) return; const char* log = App()->GetCurrentLog(); @@ -4326,6 +4602,21 @@ void OBSBasic::on_actionViewCurrentLog_triggered() QDesktopServices::openUrl(url); } +void OBSBasic::on_actionShowCrashLogs_triggered() +{ + char logDir[512]; + if (GetConfigPath(logDir, sizeof(logDir), "ebs-studio/crashes") <= 0) + return; + + QUrl url = QUrl::fromLocalFile(QT_UTF8(logDir)); + QDesktopServices::openUrl(url); +} + +void OBSBasic::on_actionUploadLastCrashLog_triggered() +{ + UploadLog("ebs-studio/crashes", App()->GetLastCrashLog()); +} + void OBSBasic::on_actionCheckForUpdates_triggered() { CheckForUpdates(true); @@ -4343,7 +4634,8 @@ void OBSBasic::logUploadFinished(const QString &text, const QString &error) } obs_data_t *returnData = obs_data_create_from_json(QT_TO_UTF8(text)); - QString logURL = obs_data_get_string(returnData, "html_url"); + string resURL = obs_data_get_string(returnData, "url"); + QString logURL = resURL.c_str(); obs_data_release(returnData); OBSLogReply logDialog(this, logURL); @@ -4399,26 +4691,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(); @@ -4484,10 +4756,10 @@ void OBSBasic::StartStreaming() return; } - bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), - "BasicWindow", "RecordWhenStreaming"); - if (recordWhenStreaming) - StartRecording(); +// bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), +// "BasicWindow", "RecordWhenStreaming"); +// if (recordWhenStreaming) +// StartRecording(); bool replayBufferWhileStreaming = config_get_bool(GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming"); @@ -4520,7 +4792,7 @@ inline void OBSBasic::OnActivate() { if (ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(false); - ui->autoConfigure->setEnabled(false); +// ui->autoConfigure->setEnabled(false); App()->IncrementSleepInhibition(); UpdateProcessPriority(); @@ -4533,12 +4805,12 @@ inline void OBSBasic::OnDeactivate() { if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); - ui->autoConfigure->setEnabled(true); +// ui->autoConfigure->setEnabled(true); App()->DecrementSleepInhibition(); ClearProcessPriority(); if (trayIcon) - trayIcon->setIcon(QIcon(":/res/images/obs.png")); + trayIcon->setIcon(QIcon(":/res/images/ebs.png")); } } @@ -4745,103 +5017,125 @@ void OBSBasic::StreamingStop(int code, QString last_error) } } -void OBSBasic::StartRecording() +void OBSBasic::AutoRemux() { - if (outputHandler->RecordingActive()) - return; - if (disableOutputsRef) - 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"); + std::string s(path); + s += "/"; + s += remuxFilename; + const QString &str = QString::fromStdString(s); + QString file = str.section(".", 0, 0); - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING); + OBSRemux *remux = new OBSRemux(path, this, true); + remux->show(); + remux->AutoRemux(str, file + ".mp4"); +} - SaveProject(); - outputHandler->StartRecording(); +void OBSBasic::StartRecording() +{ +// if (outputHandler->RecordingActive()) +// return; +// if (disableOutputsRef) +// return; +// +// if (api) +// api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING); +// +// SaveProject(); +// +// if (!outputHandler->StartRecording()) +// ui->recordButton->setChecked(false); } void OBSBasic::RecordStopping() { - ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); - - if (sysTrayRecord) - sysTrayRecord->setText(ui->recordButton->text()); - - recordingStopping = true; - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPING); +// ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); +// +// if (sysTrayRecord) +// sysTrayRecord->setText(ui->recordButton->text()); +// +// recordingStopping = true; +// if (api) +// api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPING); } void OBSBasic::StopRecording() { - SaveProject(); - - if (outputHandler->RecordingActive()) - outputHandler->StopRecording(recordingStopping); - - OnDeactivate(); +// SaveProject(); +// +// if (outputHandler->RecordingActive()) +// outputHandler->StopRecording(recordingStopping); +// +// OnDeactivate(); } void OBSBasic::RecordingStart() { - ui->statusbar->RecordingStarted(outputHandler->fileOutput); - ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); - ui->recordButton->setChecked(true); - - if (sysTrayRecord) - sysTrayRecord->setText(ui->recordButton->text()); - - recordingStopping = false; - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED); - - OnActivate(); - - blog(LOG_INFO, RECORDING_START); +// ui->statusbar->RecordingStarted(outputHandler->fileOutput); +// ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); +// ui->recordButton->setChecked(true); +// +// if (sysTrayRecord) +// sysTrayRecord->setText(ui->recordButton->text()); +// +// recordingStopping = false; +// if (api) +// api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED); +// +// OnActivate(); +// +// blog(LOG_INFO, RECORDING_START); } void OBSBasic::RecordingStop(int code) { - ui->statusbar->RecordingStopped(); - ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); - ui->recordButton->setChecked(false); - - if (sysTrayRecord) - sysTrayRecord->setText(ui->recordButton->text()); - - blog(LOG_INFO, RECORDING_STOP); - - if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { - OBSMessageBox::information(this, - QTStr("Output.RecordFail.Title"), - QTStr("Output.RecordFail.Unsupported")); - - } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { - OBSMessageBox::information(this, - QTStr("Output.RecordNoSpace.Title"), - QTStr("Output.RecordNoSpace.Msg")); - - } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { - OBSMessageBox::information(this, - QTStr("Output.RecordError.Title"), - QTStr("Output.RecordError.Msg")); - - } else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) { - SysTrayNotify(QTStr("Output.RecordFail.Unsupported"), - QSystemTrayIcon::Warning); - - } else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) { - SysTrayNotify(QTStr("Output.RecordNoSpace.Msg"), - QSystemTrayIcon::Warning); - - } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { - SysTrayNotify(QTStr("Output.RecordError.Msg"), - QSystemTrayIcon::Warning); - } - - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPED); - - OnDeactivate(); +// ui->statusbar->RecordingStopped(); +// ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); +// ui->recordButton->setChecked(false); +// +// if (sysTrayRecord) +// sysTrayRecord->setText(ui->recordButton->text()); +// +// blog(LOG_INFO, RECORDING_STOP); +// +// if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { +// OBSMessageBox::information(this, +// QTStr("Output.RecordFail.Title"), +// QTStr("Output.RecordFail.Unsupported")); +// +// } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { +// OBSMessageBox::information(this, +// QTStr("Output.RecordNoSpace.Title"), +// QTStr("Output.RecordNoSpace.Msg")); +// +// } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { +// OBSMessageBox::information(this, +// QTStr("Output.RecordError.Title"), +// QTStr("Output.RecordError.Msg")); +// +// } else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) { +// SysTrayNotify(QTStr("Output.RecordFail.Unsupported"), +// QSystemTrayIcon::Warning); +// +// } else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) { +// SysTrayNotify(QTStr("Output.RecordNoSpace.Msg"), +// QSystemTrayIcon::Warning); +// +// } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { +// SysTrayNotify(QTStr("Output.RecordError.Msg"), +// QSystemTrayIcon::Warning); +// } +// +// if (api) +// api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPED); +// +// if (remuxAfterRecord) +// AutoRemux(); +// +// OnDeactivate(); } #define RP_NO_HOTKEY_TITLE QTStr("Output.ReplayBuffer.NoHotkey.Title") @@ -4856,6 +5150,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, @@ -4875,7 +5174,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() @@ -4985,6 +5285,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()) { @@ -4997,12 +5319,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"); @@ -5012,8 +5341,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(); @@ -5022,10 +5353,16 @@ void OBSBasic::on_streamButton_clicked() void OBSBasic::on_recordButton_clicked() { - if (outputHandler->RecordingActive()) - StopRecording(); - else - StartRecording(); +// if (outputHandler->RecordingActive()) { +// StopRecording(); +// } else { +// if (!NoSourcesConfirmation()) { +// ui->recordButton->setChecked(false); +// return; +// } +// +// StartRecording(); +// } } void OBSBasic::on_settingsButton_clicked() @@ -5045,10 +5382,16 @@ 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]; - int ret = GetConfigPath(path, 512, "obs-studio"); + int ret = GetConfigPath(path, 512, "ebs-studio"); if (ret <= 0) return; @@ -5065,13 +5408,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) @@ -5131,12 +5472,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); } @@ -5276,36 +5619,38 @@ void OBSBasic::on_actionPasteTransform_triggered() obs_scene_enum_items(GetCurrentScene(), func, nullptr); } -void OBSBasic::on_actionResetTransform_triggered() +static bool reset_tr(obs_scene_t *scene, obs_sceneitem_t *item, void *param) { - auto func = [] (obs_scene_t *scene, obs_sceneitem_t *item, void *param) - { - if (!obs_sceneitem_selected(item)) - return true; + 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_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_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_crop crop = {}; + obs_sceneitem_set_crop(item, &crop); - obs_sceneitem_defer_update_end(item); + obs_sceneitem_defer_update_end(item); - UNUSED_PARAMETER(scene); - UNUSED_PARAMETER(param); - return true; - }; + UNUSED_PARAMETER(scene); + UNUSED_PARAMETER(param); + return true; +} - obs_scene_enum_items(GetCurrentScene(), func, nullptr); +void OBSBasic::on_actionResetTransform_triggered() +{ + obs_scene_enum_items(GetCurrentScene(), reset_tr, nullptr); } static void GetItemBox(obs_sceneitem_t *item, vec3 &tl, vec3 &br) @@ -5353,6 +5698,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; @@ -5365,10 +5713,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; }; @@ -5395,6 +5744,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; @@ -5405,6 +5757,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); @@ -5432,6 +5786,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; @@ -5469,39 +5826,38 @@ void OBSBasic::on_actionStretchToScreen_triggered() &boundsType); } -void OBSBasic::on_actionCenterToScreen_triggered() +static bool center_to_scene(obs_scene_t *, obs_sceneitem_t *item, void *) { - 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; + vec3 tl, br, itemCenter, screenCenter, offset; + obs_video_info ovi; - obs_get_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; - vec3_set(&screenCenter, float(ovi.base_width), - float(ovi.base_height), 0.0f); - vec3_mulf(&screenCenter, &screenCenter, 0.5f); + obs_get_video_info(&ovi); - GetItemBox(item, tl, br); + vec3_set(&screenCenter, float(ovi.base_width), + float(ovi.base_height), 0.0f); + vec3_mulf(&screenCenter, &screenCenter, 0.5f); - vec3_sub(&itemCenter, &br, &tl); - vec3_mulf(&itemCenter, &itemCenter, 0.5f); - vec3_add(&itemCenter, &itemCenter, &tl); + GetItemBox(item, tl, br); - vec3_sub(&offset, &screenCenter, &itemCenter); - vec3_add(&tl, &tl, &offset); + vec3_sub(&itemCenter, &br, &tl); + vec3_mulf(&itemCenter, &itemCenter, 0.5f); + vec3_add(&itemCenter, &itemCenter, &tl); - SetItemTL(item, tl); + vec3_sub(&offset, &screenCenter, &itemCenter); + vec3_add(&tl, &tl, &offset); - UNUSED_PARAMETER(scene); - UNUSED_PARAMETER(param); - return true; - }; + SetItemTL(item, tl); + return true; +}; - obs_scene_enum_items(GetCurrentScene(), func, nullptr); +void OBSBasic::on_actionCenterToScreen_triggered() +{ + obs_scene_enum_items(GetCurrentScene(), center_to_scene, nullptr); } void OBSBasic::EnablePreviewDisplay(bool enable) @@ -5517,44 +5873,56 @@ void OBSBasic::TogglePreview() EnablePreviewDisplay(previewEnabled); } -void OBSBasic::Nudge(int dist, MoveDir dir) +static bool nudge_callback(obs_scene_t*, obs_sceneitem_t *item, void *param) { - if (ui->preview->Locked()) - return; - - struct MoveInfo { - float dist; - MoveDir dir; - } info = {(float)dist, dir}; + if (obs_sceneitem_locked(item)) + return true; - auto func = [] (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; - MoveInfo *info = reinterpret_cast(param); - struct vec2 dir; - 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); - vec2_set(&dir, 0.0f, 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); - 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; + struct vec2 new_offset; + vec2_set(&new_offset, offset3.x, offset3.y); + obs_sceneitem_group_enum_items(item, nudge_callback, + &new_offset); } - obs_sceneitem_get_pos(item, &pos); - vec2_add(&pos, &pos, &dir); - obs_sceneitem_set_pos(item, &pos); return true; - }; + } + + obs_sceneitem_get_pos(item, &pos); + vec2_add(&pos, &pos, &offset); + obs_sceneitem_set_pos(item, &pos); + return true; +} - obs_scene_enum_items(GetCurrentScene(), func, &info); +void OBSBasic::Nudge(int dist, MoveDir dir) +{ + if (ui->preview->Locked()) + return; + + struct vec2 offset; + vec2_set(&offset, 0.0f, 0.0f); + + 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; + } + + obs_scene_enum_items(GetCurrentScene(), nudge_callback, &offset); } void OBSBasic::NudgeUp() {Nudge(1, MoveDir::Up);} @@ -5562,40 +5930,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; - - if (!window) { - delete projectors[monitor]; - projectors[monitor].clear(); - RemoveSavedProjectors(monitor); - } + return nullptr; - 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; @@ -5605,20 +5950,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() @@ -5628,14 +5979,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() @@ -5645,114 +5996,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()); - - if (!source) { - RemoveSavedProjectors((int)i); - obs_source_release(source); - continue; - } + QString title = nullptr; + if (info->monitor < 0) + title = QString::fromUtf8( + obs_source_get_name(source)); - OpenProjector(source, (int)i, false); - obs_source_release(source); - } - } + projector = OpenProjector(source, info->monitor, title, + info->type); - for (size_t i = 0; i < studioProgramProjectorArray.size(); i++) { - if (studioProgramProjectorArray.at(i) == 1) { - OpenProjector(nullptr, (int)i, false, nullptr, - ProjectorType::StudioProgram); - } + obs_source_release(source); + break; } - - for (size_t i = 0; i < previewProjectorArray.size(); i++) { - if (previewProjectorArray.at(i) == 1) { - OpenProjector(nullptr, (int)i, false, nullptr, - ProjectorType::Preview); - } + 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; } + } + + if (projector && !info->geometry.empty()) { + QByteArray byteArray = QByteArray::fromBase64( + QByteArray(info->geometry.c_str())); + projector->restoreGeometry(byteArray); - 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) @@ -5772,7 +6121,7 @@ void OBSBasic::UpdateTitleBar() const char *sceneCollection = config_get_string(App()->GlobalConfig(), "Basic", "SceneCollection"); - name << "OBS "; + name << "EBS "; if (previewProgramMode) name << "Studio "; @@ -5800,7 +6149,7 @@ int OBSBasic::GetProfilePath(char *path, size_t size, const char *file) const if (!file) file = ""; - ret = GetConfigPath(profiles_path, 512, "obs-studio/basic/profiles"); + ret = GetConfigPath(profiles_path, 512, "ebs-studio/basic/profiles"); if (ret <= 0) return ret; @@ -5846,6 +6195,8 @@ void OBSBasic::on_resetUI_triggered() ui->mixerDock->setVisible(true); ui->transitionsDock->setVisible(true); ui->controlsDock->setVisible(true); + statsDock->setVisible(false); + statsDock->setFloating(true); resizeDocks(docks, {cy, cy, cy, cy, cy}, Qt::Vertical); resizeDocks(docks, sizes, Qt::Horizontal); @@ -5863,6 +6214,7 @@ void OBSBasic::on_lockUI_toggled(bool lock) ui->mixerDock->setFeatures(features); ui->transitionsDock->setFeatures(features); ui->controlsDock->setFeatures(features); + statsDock->setFeatures(features); } void OBSBasic::on_toggleListboxToolbars_toggled(bool visible) @@ -5964,6 +6316,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")); @@ -5974,6 +6330,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) { @@ -6008,59 +6372,66 @@ void OBSBasic::ToggleShowHide() void OBSBasic::SystemTrayInit() { - trayIcon = new QSystemTrayIcon(QIcon(":/res/images/obs.png"), - this); - trayIcon->setToolTip("OBS Studio"); + trayIcon.reset(new QSystemTrayIcon(QIcon(":/res/images/ebs.png"), + this)); + trayIcon->setToolTip("EBS Studio"); showHide = new QAction(QTStr("Basic.SystemTray.Show"), - trayIcon); + trayIcon.data()); sysTrayStream = new QAction(QTStr("Basic.Main.StartStreaming"), - trayIcon); - sysTrayRecord = new QAction(QTStr("Basic.Main.StartRecording"), - trayIcon); + trayIcon.data()); +// sysTrayRecord = new QAction(QTStr("Basic.Main.StartRecording"), +// trayIcon.data()); sysTrayReplayBuffer = new QAction(QTStr("Basic.Main.StartReplayBuffer"), - trayIcon); + trayIcon.data()); exit = new QAction(QTStr("Exit"), - trayIcon); + trayIcon.data()); + + trayMenu = new QMenu; + previewProjector = new QMenu(QTStr("PreviewProjector")); + studioProgramProjector = new QMenu(QTStr("StudioProgramProjector")); + AddProjectorMenuMonitors(previewProjector, this, + SLOT(OpenPreviewProjector())); + AddProjectorMenuMonitors(studioProgramProjector, this, + SLOT(OpenStudioProgramProjector())); + trayMenu->addAction(showHide); + trayMenu->addMenu(previewProjector); + trayMenu->addMenu(studioProgramProjector); + trayMenu->addAction(sysTrayStream); +// trayMenu->addAction(sysTrayRecord); + trayMenu->addAction(sysTrayReplayBuffer); + trayMenu->addAction(exit); + trayIcon->setContextMenu(trayMenu); 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()), this, SLOT(ToggleShowHide())); connect(sysTrayStream, SIGNAL(triggered()), this, SLOT(on_streamButton_clicked())); - connect(sysTrayRecord, SIGNAL(triggered()), - this, SLOT(on_recordButton_clicked())); +// connect(sysTrayRecord, SIGNAL(triggered()), +// this, SLOT(on_recordButton_clicked())); connect(sysTrayReplayBuffer.data(), &QAction::triggered, this, &OBSBasic::ReplayBufferClicked); connect(exit, SIGNAL(triggered()), this, SLOT(close())); +} - QMenu *previewProjector = new QMenu(QTStr("PreviewProjector")); +void OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason) +{ + // Refresh projector list + previewProjector->clear(); + studioProgramProjector->clear(); AddProjectorMenuMonitors(previewProjector, this, SLOT(OpenPreviewProjector())); - QMenu *studioProgramProjector = new QMenu( - QTStr("StudioProgramProjector")); AddProjectorMenuMonitors(studioProgramProjector, this, SLOT(OpenStudioProgramProjector())); - trayMenu = new QMenu; - trayMenu->addAction(showHide); - trayMenu->addMenu(previewProjector); - trayMenu->addMenu(studioProgramProjector); - trayMenu->addAction(sysTrayStream); - trayMenu->addAction(sysTrayRecord); - trayMenu->addAction(sysTrayReplayBuffer); - trayMenu->addAction(exit); - trayIcon->setContextMenu(trayMenu); -} - -void OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason) -{ if (reason == QSystemTrayIcon::Trigger) ToggleShowHide(); } @@ -6071,7 +6442,7 @@ void OBSBasic::SysTrayNotify(const QString &text, if (QSystemTrayIcon::supportsMessages()) { QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::MessageIcon(n); - trayIcon->showMessage("OBS Studio", text, icon, 10000); + trayIcon->showMessage("EBS Studio", text, icon, 10000); } } @@ -6097,6 +6468,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) { @@ -6121,13 +6495,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); @@ -6144,6 +6517,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(); } @@ -6182,6 +6560,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); @@ -6203,3 +6755,54 @@ void OBSBasic::on_stats_triggered() statsDlg->show(); stats = statsDlg; } + +void OBSBasic::on_actionShowAbout_triggered() +{ + if (about) + about->close(); + + about = new OBSAbout(this); + about->show(); + + about->setAttribute(Qt::WA_DeleteOnClose, true); +} + +void OBSBasic::ResizeOutputSizeOfSource() +{ + if (ui->streamButton->isChecked() || + (replayBufferButton && replayBufferButton->isChecked())) + return; + + QMessageBox resize_output(this); + resize_output.setText(QTStr("ResizeOutputSizeOfSource.Text") + + "\n\n" + QTStr("ResizeOutputSizeOfSource.Continue")); + QAbstractButton *Yes = resize_output.addButton(QTStr("Yes"), + QMessageBox::YesRole); + resize_output.addButton(QTStr("No"), QMessageBox::NoRole); + resize_output.setIcon(QMessageBox::Warning); + resize_output.setWindowTitle(QTStr("ResizeOutputSizeOfSource")); + resize_output.exec(); + + if (resize_output.clickedButton() != Yes) + return; + + OBSSource source = obs_sceneitem_get_source(GetCurrentSceneItem()); + + int width = obs_source_get_width(source); + int height = obs_source_get_height(source); + + config_set_uint(basicConfig, "Video", "BaseCX", width); + config_set_uint(basicConfig, "Video", "BaseCY", height); + config_set_uint(basicConfig, "Video", "OutputCX", width); + config_set_uint(basicConfig, "Video", "OutputCY", height); + + ResetVideo(); + on_actionFitToScreen_triggered(); +} + +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 30a1e983c..27377622a 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -29,6 +29,8 @@ #include "window-basic-transform.hpp" #include "window-basic-adv-audio.hpp" #include "window-basic-filters.hpp" +#include "window-projector.hpp" +#include "window-basic-about.hpp" #include @@ -41,16 +43,17 @@ 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") #define AUX_AUDIO_1 Str("AuxAudioDevice1") #define AUX_AUDIO_2 Str("AuxAudioDevice2") #define AUX_AUDIO_3 Str("AuxAudioDevice3") +#define AUX_AUDIO_4 Str("AuxAudioDevice4") #define SIMPLE_ENCODER_X264 "x264" #define SIMPLE_ENCODER_X264_LOWCPU "x264_lowcpu" @@ -67,26 +70,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 +130,6 @@ class OBSBasic : public OBSMainWindow { 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,14 +140,17 @@ class OBSBasic : public OBSMainWindow { const char *copyFiltersString; bool copyVisible = true; - QPointer updateCheckThread; - QPointer logUploadThread; + QScopedPointer updateCheckThread; + QScopedPointer introCheckThread; + QScopedPointer logUploadThread; QPointer interaction; QPointer properties; QPointer transformWindow; QPointer advAudioWindow; QPointer filters; + QPointer statsDock; + QPointer about; QPointer cpuUsageTimer; os_cpu_usage_info_t *cpuUsageInfo = nullptr; @@ -169,22 +177,29 @@ class OBSBasic : public OBSMainWindow { 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; QPointer showHide; QPointer exit; QPointer trayMenu; + QPointer previewProjector; + QPointer studioProgramProjector; + QPointer multiviewProjectorMenu; + + void UpdateMultiviewProjectorMenu(); void DrawBackdrop(float cx, float cy); @@ -194,9 +209,10 @@ class OBSBasic : public OBSMainWindow { void CreateDefaultScene(bool firstStart); void UpdateVolumeControlsDecayRate(); + void UpdateVolumeControlsPeakMeterType(); void ClearVolumeControls(); - void UploadLog(const char *file); + void UploadLog(const char *subdir, const char *file); void Save(const char *file); void Load(const char *file); @@ -214,6 +230,8 @@ class OBSBasic : public OBSMainWindow { void InitPrimitives(); + void OnFirstLoad(); + OBSSceneItem GetSceneItem(QListWidgetItem *item); OBSSceneItem GetCurrentSceneItem(); @@ -230,9 +248,6 @@ class OBSBasic : public OBSMainWindow { 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); @@ -241,23 +256,20 @@ class OBSBasic : public OBSMainWindow { 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(); @@ -273,7 +285,7 @@ class OBSBasic : public OBSMainWindow { void SaveProjectNow(); - QListWidgetItem *GetTopSelectedSourceItem(); + int GetTopSelectedSourceItem(); obs_hotkey_pair_id streamingHotkeys, recordingHotkeys, replayBufHotkeys; @@ -364,19 +376,14 @@ class OBSBasic : public OBSMainWindow { obs_data_array_t *SaveProjectors(); void LoadSavedProjectors(obs_data_array_t *savedProjectors); - obs_data_array_t *SavePreviewProjectors(); - void LoadSavedPreviewProjectors( - obs_data_array_t *savedPreviewProjectors); - - obs_data_array_t *SaveStudioProgramProjectors(); - void LoadSavedStudioProgramProjectors( - obs_data_array_t *savedStudioProgramProjectors); + void ReceivedIntroJson(const QString &text); - 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(); @@ -414,9 +421,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); @@ -459,7 +469,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(); @@ -468,14 +479,19 @@ private slots: void on_actionCopyFilters_triggered(); void on_actionPasteFilters_triggered(); + void ColorChange(); + + SourceTreeItem *GetItemWidgetFromSceneItem(obs_sceneitem_t *sceneItem); + + void on_actionShowAbout_triggered(); + 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); @@ -491,6 +507,8 @@ private slots: static void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed); + void AutoRemux(); + public: OBSSource GetProgramSource(); OBSScene GetCurrentScene(); @@ -534,6 +552,11 @@ private slots: cy = previewCY; } + inline bool SavingDisabled() const + { + return disableSaving; + } + inline double GetCPUUsage() const { return os_cpu_usage_info_query(cpuUsageInfo); @@ -552,11 +575,10 @@ private slots: } } - 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); @@ -565,7 +587,10 @@ private slots: 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; @@ -586,6 +611,9 @@ private slots: void on_actionViewCurrentLog_triggered(); void on_actionCheckForUpdates_triggered(); + void on_actionShowCrashLogs_triggered(); + void on_actionUploadLastCrashLog_triggered(); + void on_actionEditTransform_triggered(); void on_actionCopyTransform_triggered(); void on_actionPasteTransform_triggered(); @@ -605,9 +633,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(); @@ -634,6 +660,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); @@ -689,8 +716,6 @@ private slots: void SceneNameEdited(QWidget *editor, QAbstractItemDelegate::EndEditHint endHint); - void SceneItemNameEdited(QWidget *editor, - QAbstractItemDelegate::EndEditHint endHint); void OpenSceneFilters(); void OpenFilters(); @@ -715,6 +740,12 @@ private slots: void OpenMultiviewWindow(); void OpenSceneWindow(); + void DeferredLoad(const QString &file, int requeueCount); + + void StackedMixerAreaContextMenuRequested(); + + void ResizeOutputSizeOfSource(); + public slots: void on_actionResetTransform_triggered(); @@ -732,3 +763,12 @@ public slots: 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 8483060c1..bc4b2b943 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)) + 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 4042281f1..c177dd9a4 100644 --- a/UI/window-basic-preview.hpp +++ b/UI/window-basic-preview.hpp @@ -35,11 +35,13 @@ class OBSBasicPreview : public OBSQTDisplay { 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 d07336b26..a03f18770 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 7615cb6aa..c7c92db6a 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,11 +404,13 @@ 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); HookWidget(ui->auxAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->auxAudioDevice3, COMBO_CHANGED, AUDIO_CHANGED); + HookWidget(ui->auxAudioDevice4, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->baseResolution, CBEDIT_CHANGED, VIDEO_RES); HookWidget(ui->outputResolution, CBEDIT_CHANGED, VIDEO_RES); HookWidget(ui->downscaleFilter, COMBO_CHANGED, VIDEO_CHANGED); @@ -426,6 +431,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,14 +447,20 @@ 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); + HookWidget(ui->autoRemux, CHECK_CHANGED, ADV_CHANGED); -#if !defined(_WIN32) && !defined(__APPLE__) && !HAVE_PULSEAUDIO +#if !defined(_WIN32) && !defined(__APPLE__) delete ui->enableAutoUpdates; - delete ui->advAudioGroupBox; ui->enableAutoUpdates = nullptr; ui->advAudioGroupBox = nullptr; #endif +#if !defined(_WIN32) && !defined(__APPLE__) && !HAVE_PULSEAUDIO + delete ui->advAudioGroupBox; + ui->advAudioGroupBox = nullptr; +#endif + #ifdef _WIN32 uint32_t winVer = GetWindowsVersion(); if (winVer > 0 && winVer < 0x602) { @@ -492,6 +504,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 @@ -504,6 +518,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 @@ -699,14 +715,19 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) UpdateAutomaticReplayBufferCheckboxes(); + App()->EnableInFocusHotkeys(false); - ui->warning_label->setStyleSheet("QLabel { color : red; }"); - QObject::connect(ui->streamType, SIGNAL(currentIndexChanged(int)), this, SLOT(displayWarning())); +// ui->warning_label->setStyleSheet("QLabel { color : red; }"); +// QObject::connect(ui->streamType, SIGNAL(currentIndexChanged(int)), this, SLOT(displayWarning())); } 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, @@ -763,6 +784,7 @@ void OBSBasicSettings::LoadServiceTypes() QString qName = QT_UTF8(name); QString qType = QT_UTF8(type); + if(qType=="webrtc_janus") ui->streamType->addItem(qName, qType); } @@ -971,7 +993,7 @@ void OBSBasicSettings::LoadThemeList() string themeDir; char userThemeDir[512]; int ret = GetConfigPath(userThemeDir, sizeof(userThemeDir), - "obs-studio/themes/"); + "ebs-studio/themes/"); GetDataFilePath("themes/", themeDir); /* Check user dir first. */ @@ -1093,30 +1115,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; } @@ -1982,6 +2011,7 @@ void OBSBasicSettings::LoadAudioDevices() LoadListValues(ui->auxAudioDevice1, inputs, 3); LoadListValues(ui->auxAudioDevice2, inputs, 4); LoadListValues(ui->auxAudioDevice3, inputs, 5); + LoadListValues(ui->auxAudioDevice4, inputs, 6); obs_properties_destroy(input_props); } @@ -2125,6 +2155,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; @@ -2160,6 +2192,8 @@ void OBSBasicSettings::LoadAudioSettings() else ui->meterDecayRate->setCurrentIndex(0); + ui->peakMeterType->setCurrentIndex(peakMeterTypeIdx); + LoadAudioDevices(); LoadAudioSources(); @@ -2208,6 +2242,8 @@ void OBSBasicSettings::LoadAdvancedSettings() "RecRBTime"); int rbSize = config_get_int(main->Config(), "AdvOut", "RecRBSize"); + bool autoRemux = config_get_bool(main->Config(), "Video", + "AutoRemux"); loading = true; @@ -2234,6 +2270,7 @@ void OBSBasicSettings::LoadAdvancedSettings() ui->streamDelaySec->setValue(delaySec); ui->streamDelayPreserve->setChecked(preserveDelay); ui->streamDelayEnable->setChecked(enableDelay); + ui->autoRemux->setChecked(autoRemux); SetComboByName(ui->colorFormat, videoColorFormat); @@ -2274,8 +2311,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; } @@ -2389,6 +2434,47 @@ void OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey) widget->setLayout(layout); ui->hotkeyPage->setWidget(widget); + auto filterLayout = new QGridLayout(); + auto filterWidget = new QWidget(); + filterWidget->setLayout(filterLayout); + + auto filterLabel = new QLabel(QTStr("Basic.Settings.Hotkeys.Filter")); + auto filter = new QLineEdit(); + + auto setRowVisible = [=](int row, bool visible, QLayoutItem *label) { + label->widget()->setVisible(visible); + + auto field = layout->itemAt(row, QFormLayout::FieldRole); + if (field) + field->widget()->setVisible(visible); + }; + + auto searchFunction = [=](const QString &text) { + for (int i = 0; i < layout->rowCount(); i++) { + auto label = layout->itemAt(i, QFormLayout::LabelRole); + if (label) { + OBSHotkeyLabel *item = + qobject_cast( + label->widget()); + if(item) { + if (item->text().toLower() + .contains(text.toLower())) + setRowVisible(i, true, label); + else + setRowVisible(i, false, label); + } + } + } + }; + + connect(filter, &QLineEdit::textChanged, + this, searchFunction); + + filterLayout->addWidget(filterLabel, 0, 0); + filterLayout->addWidget(filter, 0, 1); + + layout->addRow(filterWidget); + using namespace std; using encoders_elem_t = tuple, QPointer>; @@ -2697,13 +2783,37 @@ void OBSBasicSettings::SaveGeneralSettings() main->ResetUI(); } + 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_string(GetGlobalConfig(), "BasicWindow", + config_set_int(GetGlobalConfig(), "BasicWindow", "MultiviewLayout", - QT_TO_UTF8(GetComboData(ui->multiviewLayout))); + ui->multiviewLayout->currentData().toInt()); + multiviewChanged = true; + } + if (multiviewChanged) OBSProjector::UpdateMultiviewProjectors(); - } } void OBSBasicSettings::SaveStream1Settings() @@ -2783,8 +2893,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(); @@ -2826,6 +2944,7 @@ void OBSBasicSettings::SaveAdvancedSettings() SaveSpinBox(ui->reconnectRetryDelay, "Output", "RetryDelay"); SaveSpinBox(ui->reconnectMaxRetries, "Output", "MaxRetries"); SaveComboData(ui->bindToIP, "Output", "BindIP"); + SaveCheckBox(ui->autoRemux, "Video", "AutoRemux"); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO QString newDevice = ui->monitoringDevice->currentData().toString(); @@ -3114,6 +3233,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) @@ -3151,6 +3278,8 @@ void OBSBasicSettings::SaveAudioSettings() "Basic.AuxDevice2", 4); UpdateAudioDevice(true, ui->auxAudioDevice3, "Basic.AuxDevice3", 5); + UpdateAudioDevice(true, ui->auxAudioDevice4, + "Basic.AuxDevice4", 6); main->SaveProject(); } @@ -3795,6 +3924,13 @@ void OBSBasicSettings::AdvOutRecCheckWarnings() if (!warningMsg.isEmpty()) warningMsg += "\n\n"; warningMsg += QTStr("OutputWarnings.MP4Recording"); + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux") + + " " + + QTStr("Basic.Settings.Advanced.AutoRemux.MP4")); + } else { + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux")); } delete advOutRecWarning; @@ -4124,6 +4260,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(); @@ -4149,9 +4288,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); @@ -4228,7 +4364,7 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged() if (qual == "Lossless") { if (!warning.isEmpty()) - warning += "\n\n"; + warning += "\n\n"; warning += SIMPLE_OUTPUT_WARNING("Lossless"); warning += "\n\n"; warning += SIMPLE_OUTPUT_WARNING("Encoder"); @@ -4245,18 +4381,19 @@ 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) { if (!warning.isEmpty()) warning += "\n\n"; warning += QTStr("OutputWarnings.MP4Recording"); + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux") + + " " + + QTStr("Basic.Settings.Advanced.AutoRemux.MP4")); + } else { + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux")); } if (warning.isEmpty()) @@ -4356,8 +4493,8 @@ void OBSBasicSettings::on_disableOSXVSync_clicked() void OBSBasicSettings::displayWarning() { if (ui->streamType->currentIndex() == 0 || ui->streamType->currentIndex() == 1) { - ui->warning_label->setVisible(false); +// ui->warning_label->setVisible(false); } else { - ui->warning_label->setVisible(true); +// ui->warning_label->setVisible(true); } } diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index 72b355806..da81782c4 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 7bab4768c..90496798c 100644 --- a/UI/window-basic-source-select.hpp +++ b/UI/window-basic-source-select.hpp @@ -32,6 +32,7 @@ class OBSBasicSourceSelect : public QDialog { 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 2a66c2d41..d6360b5e2 100644 --- a/UI/window-basic-stats.cpp +++ b/UI/window-basic-stats.cpp @@ -28,7 +28,7 @@ static void setThemeID(QWidget *widget, const QString &themeID) } } -OBSBasicStats::OBSBasicStats(QWidget *parent) +OBSBasicStats::OBSBasicStats(QWidget *parent, bool closeable) : QWidget (parent), cpu_info (os_cpu_usage_info_start()), timer (this) @@ -75,13 +75,15 @@ OBSBasicStats::OBSBasicStats(QWidget *parent) newStat("SkippedFrames", skippedFrames, 2); /* --------------------------------------------- */ - - QPushButton *closeButton = new QPushButton(QTStr("Close")); + QPushButton *closeButton = nullptr; + if(closeable) + closeButton = new QPushButton(QTStr("Close")); QPushButton *resetButton = new QPushButton(QTStr("Reset")); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); buttonLayout->addWidget(resetButton); - buttonLayout->addWidget(closeButton); + if(closeable) + buttonLayout->addWidget(closeButton); /* --------------------------------------------- */ @@ -125,17 +127,17 @@ OBSBasicStats::OBSBasicStats(QWidget *parent) setLayout(mainLayout); /* --------------------------------------------- */ - - connect(closeButton, &QPushButton::clicked, [this] () {close();}); + if(closeable) + connect(closeButton, &QPushButton::clicked, + [this] () {close();}); connect(resetButton, &QPushButton::clicked, [this] () {Reset();}); installEventFilter(CreateShortcutFilter()); resize(800, 280); - setWindowFlags(Qt::Window | - Qt::WindowMinimizeButtonHint | - Qt::WindowCloseButtonHint); + setWindowTitle(QTStr("Basic.Stats")); + setWindowIcon(QIcon(":/res/images/ebs.png")); setWindowModality(Qt::NonModal); setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/UI/window-basic-stats.hpp b/UI/window-basic-stats.hpp index a719b7da7..7f3e26967 100644 --- a/UI/window-basic-stats.hpp +++ b/UI/window-basic-stats.hpp @@ -55,7 +55,7 @@ class OBSBasicStats : public QWidget { virtual void closeEvent(QCloseEvent *event) override; public: - OBSBasicStats(QWidget *parent = nullptr); + OBSBasicStats(QWidget *parent = nullptr, bool closable = true); ~OBSBasicStats(); static void InitializeValues(); diff --git a/UI/window-basic-transform.cpp b/UI/window-basic-transform.cpp index 7b0b4b048..1f529ddaa 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-license-agreement.cpp b/UI/window-license-agreement.cpp deleted file mode 100644 index a7a5f3b33..000000000 --- a/UI/window-license-agreement.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include -#include -#include "window-license-agreement.hpp" -#include "qt-wrappers.hpp" - -using namespace std; - -OBSLicenseAgreement::OBSLicenseAgreement(QWidget *parent) - : QDialog (parent), - ui (new Ui::OBSLicenseAgreement) -{ - ui->setupUi(this); - - string path; - if (!GetDataFilePath("license/gplv2.txt", path)) - throw "Could not find license file"; - - BPtr licenseText = os_quick_read_utf8_file(path.c_str()); - if (!licenseText || !*licenseText || strlen(licenseText) < 1000) - throw "Invalid license file data"; - - ui->license->setPlainText(QT_UTF8(licenseText)); -} diff --git a/UI/window-license-agreement.hpp b/UI/window-license-agreement.hpp deleted file mode 100644 index aa3a6f45b..000000000 --- a/UI/window-license-agreement.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include "ui_OBSLicenseAgreement.h" - -class OBSLicenseAgreement : public QDialog { - Q_OBJECT - -private: - std::unique_ptr ui; - -public: - OBSLicenseAgreement(QWidget *parent); -}; diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index 0ad9755ad..812093cf0 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -3,32 +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/ebs.png")); + + UpdateProjectorTitle(projectorTitle); + windowedProjectors.push_back(this); + + resize(480, 270); + } 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, config_get_bool(GetGlobalConfig(), + "BasicWindow", "ProjectorAlwaysOnTop")); + setAttribute(Qt::WA_DeleteOnClose, true); //disable application quit when last window closed @@ -49,14 +73,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 +168,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 +180,9 @@ OBSProjector::~OBSProjector() if (type == ProjectorType::Multiview) multiviewProjectors.removeAll(this); + if (isWindow) + windowedProjectors.removeAll(this); + App()->DecrementSleepInhibition(); } @@ -132,92 +226,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 +251,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 +291,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; - - gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID); - gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color"); + float scale; - 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; + case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: + window->sourceX = (i % 6) * window->scenesCX; + window->sourceY = window->pvwprgCY + + (i / 6) * window->scenesCY; + break; + case MultiviewLayout::VERTICAL_LEFT_8_SCENES: + window->sourceX = window->pvwprgCX; + window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) - sourceX = halfCX + quarterCX; + window->sourceX += window->scenesCX; break; - case VERTICAL_RIGHT: - sourceX = 0; - sourceY = (i / 2 ) * quarterCY; + case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: + window->sourceX = 0; + window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) - sourceX = quarterCX; + window->sourceX = window->scenesCX; break; - case HORIZONTAL_BOTTOM: + 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; } } }; - /* ----------------------------- */ - /* draw sources */ + 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(); + }; - 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); + // Define the whole usable region for the multiview + startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, window->fw, + 0.0f, window->fh); - for (size_t i = 0; i < 8; i++) { - OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]); - obs_source *label = window->multiviewLabels[i + 2]; + // Change the background color to highlight all sources + drawBox(window->fw, window->fh, outerColor); - if (!src) - continue; - if (!label) - continue; + /* ----------------------------- */ + /* draw sources */ + for (size_t i = 0; i < maxSrcs; i++) { + // Handle all the offsets calcBaseSource(i); - qiX = sourceX + 4.0f; - qiY = sourceY + 4.0f; - - /* ----------- */ + 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; + } - if (src == previewSrc || src == programSrc) { - uint32_t colorVal = src == programSrc - ? 0xFFFF0000 - : 0xFF00FF00; + OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]); - gs_matrix_push(); - gs_matrix_translate3f(sourceX, sourceY, 0.0f); - drawBox(quarterCX, quarterCY, colorVal); - gs_matrix_pop(); + // 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; - gs_matrix_push(); - gs_matrix_translate3f(qiX, qiY, 0.0f); - drawBox(qiCX, qiCY, 0xFF000000); - gs_matrix_pop(); - } + // 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(qiX, qiY, 0.0f); - gs_matrix_scale3f(qiScaleX, qiScaleY, 1.0f); - - setRegion(qiX, qiY, qiCX, qiCY); + 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); - resetRegion(); - - gs_effect_set_color(color, 0xFFFFFFFF); - renderVB(solid, window->outerBox, targetCX, targetCY); - + endRegion(); gs_matrix_pop(); /* ----------- */ - offset = labelOffset(label, quarterCX); - cx = obs_source_get_width(label); - cy = obs_source_get_height(label); + // Render the label + if (!drawLabel) + continue; - gs_matrix_push(); - gs_matrix_translate3f(sourceX + offset, - (quarterCY * 0.8f) + sourceY, 0.0f); + obs_source *label = window->multiviewLabels[i + 2]; + if (!label) + continue; - drawBox(cx, cy + int(quarterCX * 0.015f), 0xD91F1F1F); - obs_source_video_render(label); + window->offset = labelOffset(label, window->scenesCX); + gs_matrix_push(); + 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); - gs_matrix_push(); - gs_matrix_translate3f(sourceX, sourceY, 0.0f); - gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f); - - setRegion(sourceX, sourceY, hiCX, hiCY); + // Paint the background + paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, + window->ppiCY, backgroundColor); - if (studioMode) { + // Scale and Draw the preview + gs_matrix_push(); + 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(); - - 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); - + endRegion(); 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); - - renderVB(solid, window->outerBox, targetCX, targetCY); - - 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(); + // 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(); + } - /* ----------------------------- */ + // 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_viewport_pop(); - gs_projection_pop(); + endRegion(); } void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) @@ -588,6 +637,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 +645,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 +660,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 +691,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 +743,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 +762,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 +781,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 +808,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 +819,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 +841,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 +860,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)); + + multiviewLayout = static_cast(config_get_int( + GetGlobalConfig(), "BasicWindow", "MultiviewLayout")); + + drawLabel = config_get_bool(GetGlobalConfig(), + "BasicWindow", "MultiviewDrawNames"); + + drawSafeArea = config_get_bool(GetGlobalConfig(), "BasicWindow", + "MultiviewDrawAreas"); + + mouseSwitching = config_get_bool(GetGlobalConfig(), "BasicWindow", + "MultiviewMouseSwitch"); - multiviewLabels[0] = CreateLabel(Str("StudioMode.Preview"), h / 2); - multiviewLabels[1] = CreateLabel(Str("StudioMode.Program"), h / 2); + transitionOnDoubleClick = config_get_bool(GetGlobalConfig(), + "BasicWindow", "TransitionOnDoubleClick"); - for (size_t i = 0; i < scenes.sources.num && curIdx < 8; i++) { - obs_source_t *src = scenes.sources.array[i]; + 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 +937,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); + std::string name = std::to_string(numSrcs) + " - " + + obs_source_get_name(src); + multiviewLabels.emplace_back(CreateLabel(name.c_str(), h / 3)); + } - multiviewLabels[curIdx + 2] = CreateLabel(name.c_str(), h / 3); + obs_frontend_source_list_free(&scenes); +} - curIdx++; +void OBSProjector::UpdateProjectorTitle(QString name) +{ + projectorTitle = name; + + 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; } - obs_frontend_source_list_free(&scenes); + setWindowTitle(title); +} - const char *multiviewLayoutText = config_get_string(GetGlobalConfig(), - "BasicWindow", "MultiviewLayout"); +OBSSource OBSProjector::GetSource() +{ + return source; +} - 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; +ProjectorType OBSProjector::GetProjectorType() +{ + return type; +} + +int OBSProjector::GetMonitor() +{ + return savedMonitor; } void OBSProjector::UpdateMultiviewProjectors() @@ -874,3 +999,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 606ab616b..1ec629d65 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 @@ class OBSProjector : public OBSQTDisplay { 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 multiviewLabels; + gs_vertbuffer_t *actionSafeMargin = nullptr; + gs_vertbuffer_t *graphicsSafeMargin = nullptr; + gs_vertbuffer_t *fourByThreeSafeMargin = nullptr; + gs_vertbuffer_t *leftLine = nullptr; + gs_vertbuffer_t *topLine = nullptr; + gs_vertbuffer_t *rightLine = nullptr; + gs_effect_t *solid = nullptr; + gs_eparam_t *color = nullptr; + // Multiview position helpers + float thickness = 4; + float offset, thicknessx2 = thickness * 2, pvwprgCX, + pvwprgCY, sourceX, sourceY, labelX, labelY, scenesCX, scenesCY, + ppiCX, ppiCY, siX, siY, siCX, siCY, ppiScaleX, ppiScaleY, + siScaleX, siScaleY, fw, fh, ratio; + + float lineLength = 0.1f; + // Rec. ITU-R BT.1848-1 / EBU R 95 + float actionSafePercentage = 0.035f; // 3.5% + float graphicsSafePercentage = 0.05f; // 5.0% + float fourByThreeSafePercentage = 0.1625f; // 16.25% bool ready = false; + // argb colors + static const uint32_t outerColor = 0xFFD0D0D0; + static const uint32_t labelColor = 0xD91F1F1F; + static const uint32_t backgroundColor = 0xFF000000; + static const uint32_t previewColor = 0xFF00D000; + static const uint32_t programColor = 0xFFD00000; + void UpdateMultiview(); + void UpdateProjectorTitle(QString name); private slots: void EscapeTriggered(); public: - OBSProjector(QWidget *parent, obs_source_t *source, bool window); + OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, + QString title, ProjectorType type_); ~OBSProjector(); - void Init(int monitor, bool window, QString title, - ProjectorType type = ProjectorType::Source); - + OBSSource GetSource(); + ProjectorType GetProjectorType(); + int GetMonitor(); static void UpdateMultiviewProjectors(); + static void RenameProjector(QString oldName, QString newName); }; diff --git a/UI/window-remux.cpp b/UI/window-remux.cpp index d7308ca5d..1b10c21e0 100644 --- a/UI/window-remux.cpp +++ b/UI/window-remux.cpp @@ -20,9 +20,18 @@ #include "obs-app.hpp" #include +#include #include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include "qt-wrappers.hpp" @@ -31,42 +40,665 @@ using namespace std; -OBSRemux::OBSRemux(const char *path, QWidget *parent) - : QDialog (parent), - worker (new RemuxWorker), - ui (new Ui::OBSRemux), - recPath (path) +enum RemuxEntryColumn { + State, + InputPath, + OutputPath, + + Count +}; + +enum RemuxEntryRole { + EntryStateRole = Qt::UserRole, + NewPathsToProcessRole +}; + +/********************************************************** + Delegate - Presents cells in the grid. +**********************************************************/ + +class RemuxEntryPathItemDelegate : public QStyledItemDelegate { +public: + + RemuxEntryPathItemDelegate(bool isOutput, const QString &defaultPath) + : QStyledItemDelegate(), + isOutput(isOutput), + defaultPath(defaultPath) + { + } + + virtual QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem & /* option */, + const QModelIndex &index) const override + { + RemuxEntryState state = index.model() + ->index(index.row(), RemuxEntryColumn::State) + .data(RemuxEntryRole::EntryStateRole) + .value(); + if (state == RemuxEntryState::Pending || + state == RemuxEntryState::InProgress) { + // Never allow modification of rows that are + // in progress. + return Q_NULLPTR; + } else if (isOutput && state != RemuxEntryState::Ready) { + // Do not allow modification of output rows + // that aren't associated with a valid input. + return Q_NULLPTR; + } else if (!isOutput && state == RemuxEntryState::Complete) { + // Don't allow modification of rows that are + // already complete. + return Q_NULLPTR; + } else { + QSizePolicy buttonSizePolicy( + QSizePolicy::Policy::Minimum, + QSizePolicy::Policy::Expanding, + QSizePolicy::ControlType::PushButton); + + QWidget *container = new QWidget(parent); + + auto browseCallback = [this, container]() + { + const_cast(this) + ->handleBrowse(container); + }; + + auto clearCallback = [this, container]() + { + const_cast(this) + ->handleClear(container); + }; + + QHBoxLayout *layout = new QHBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + + QLineEdit *text = new QLineEdit(); + text->setObjectName(QStringLiteral("text")); + text->setSizePolicy(QSizePolicy( + QSizePolicy::Policy::Expanding, + QSizePolicy::Policy::Expanding, + QSizePolicy::ControlType::LineEdit)); + layout->addWidget(text); + + QToolButton *browseButton = new QToolButton(); + browseButton->setText("..."); + browseButton->setSizePolicy(buttonSizePolicy); + layout->addWidget(browseButton); + + container->connect(browseButton, &QToolButton::clicked, + browseCallback); + + // The "clear" button is not shown in output cells + // or the insertion point's input cell. + if (!isOutput && state != RemuxEntryState::Empty) { + QToolButton *clearButton = new QToolButton(); + clearButton->setText("X"); + clearButton->setSizePolicy(buttonSizePolicy); + layout->addWidget(clearButton); + + container->connect(clearButton, + &QToolButton::clicked, + clearCallback); + } + + container->setLayout(layout); + container->setFocusProxy(text); + + return container; + } + } + + virtual void setEditorData(QWidget *editor, const QModelIndex &index) + const override + { + QLineEdit *text = editor->findChild(); + text->setText(index.data().toString()); + + editor->setProperty(PATH_LIST_PROP, QVariant()); + } + + virtual void setModelData(QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const override + { + // We use the PATH_LIST_PROP property to pass a list of + // path strings from the editor widget into the model's + // NewPathsToProcessRole. This is only used when paths + // are selected through the "browse" or "delete" buttons + // in the editor. If the user enters new text in the + // text box, we simply pass that text on to the model + // as normal text data in the default role. + QVariant pathListProp = editor->property(PATH_LIST_PROP); + if (pathListProp.isValid()) { + QStringList list = editor->property(PATH_LIST_PROP) + .toStringList(); + if (isOutput) { + if (list.size() > 0) + model->setData(index, list); + } else + model->setData(index, list, + RemuxEntryRole::NewPathsToProcessRole); + } else { + QLineEdit *lineEdit = editor->findChild(); + model->setData(index, lineEdit->text()); + } + } + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override + { + RemuxEntryState state = index.model() + ->index(index.row(), RemuxEntryColumn::State) + .data(RemuxEntryRole::EntryStateRole) + .value(); + + QStyleOptionViewItem localOption = option; + initStyleOption(&localOption, index); + + if (isOutput) { + if (state != Ready) { + QColor background = localOption.palette + .color(QPalette::ColorGroup::Disabled, + QPalette::ColorRole::Background); + + localOption.backgroundBrush = QBrush(background); + } + } + + QApplication::style()->drawControl(QStyle::CE_ItemViewItem, + &localOption, painter); + } + +private: + bool isOutput; + QString defaultPath; + + const char *PATH_LIST_PROP = "pathList"; + + void handleBrowse(QWidget *container) + { + QString OutputPattern = + "(*.mp4 *.flv *.mov *.mkv *.ts *.m3u8)"; + QString InputPattern = + "(*.flv *.mov *.mkv *.ts *.m3u8)"; + + QLineEdit *text = container->findChild(); + + QString currentPath = text->text(); + if (currentPath.isEmpty()) + currentPath = defaultPath; + + bool isSet = false; + if (isOutput) { + QString newPath = QFileDialog::getSaveFileName( + container, QTStr("Remux.SelectTarget"), + currentPath, OutputPattern); + + if (!newPath.isEmpty()) { + container->setProperty(PATH_LIST_PROP, + QStringList() << newPath); + isSet = true; + } + } else { + QStringList paths = QFileDialog::getOpenFileNames( + container, + QTStr("Remux.SelectRecording"), + currentPath, + QTStr("Remux.OBSRecording") + + QString(" ") + InputPattern); + + if (!paths.empty()) { + container->setProperty(PATH_LIST_PROP, paths); + isSet = true; + } + } + + if (isSet) + emit commitData(container); + } + + void handleClear(QWidget *container) + { + // An empty string list will indicate that the entry is being + // blanked and should be deleted. + container->setProperty(PATH_LIST_PROP, QStringList()); + + emit commitData(container); + } +}; + +/********************************************************** + Model - Manages the queue's data +**********************************************************/ + +int RemuxQueueModel::rowCount(const QModelIndex &) const +{ + return queue.length() + (isProcessing ? 0 : 1); +} + +int RemuxQueueModel::columnCount(const QModelIndex &) const +{ + return RemuxEntryColumn::Count; +} + +QVariant RemuxQueueModel::data(const QModelIndex &index, int role) const +{ + QVariant result = QVariant(); + + if (index.row() >= queue.length()) { + return QVariant(); + } else if (role == Qt::DisplayRole) { + switch (index.column()) { + case RemuxEntryColumn::InputPath: + result = queue[index.row()].sourcePath; + break; + case RemuxEntryColumn::OutputPath: + result = queue[index.row()].targetPath; + break; + } + } else if (role == Qt::DecorationRole && + index.column() == RemuxEntryColumn::State) { + result = getIcon(queue[index.row()].state); + } else if (role == RemuxEntryRole::EntryStateRole) { + result = queue[index.row()].state; + } + + return result; +} + +QVariant RemuxQueueModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + QVariant result = QVariant(); + + if (role == Qt::DisplayRole && + orientation == Qt::Orientation::Horizontal) { + switch (section) { + case RemuxEntryColumn::State: + result = QString(); + break; + case RemuxEntryColumn::InputPath: + result = QTStr("Remux.SourceFile"); + break; + case RemuxEntryColumn::OutputPath: + result = QTStr("Remux.TargetFile"); + break; + } + } + + return result; +} + +Qt::ItemFlags RemuxQueueModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + if (index.column() == RemuxEntryColumn::InputPath) { + flags |= Qt::ItemIsEditable; + } else if (index.column() == RemuxEntryColumn::OutputPath && + index.row() != queue.length()) { + flags |= Qt::ItemIsEditable; + } + + return flags; +} + +bool RemuxQueueModel::setData(const QModelIndex &index, const QVariant &value, + int role) +{ + bool success = false; + + if (role == RemuxEntryRole::NewPathsToProcessRole) { + QStringList pathList = value.toStringList(); + + if (pathList.size() == 0) { + if (index.row() < queue.size()) { + beginRemoveRows(QModelIndex(), index.row(), + index.row()); + queue.removeAt(index.row()); + endRemoveRows(); + } + } else { + if (pathList.size() > 1 && index.row() < queue.length()) { + queue[index.row()].sourcePath = pathList[0]; + checkInputPath(index.row()); + + pathList.removeAt(0); + + success = true; + } + + if (pathList.size() > 0) { + int row = index.row(); + int lastRow = row + pathList.size() - 1; + beginInsertRows(QModelIndex(), row, lastRow); + + for (QString path : pathList) { + RemuxQueueEntry entry; + entry.sourcePath = path; + + queue.insert(row, entry); + row++; + } + endInsertRows(); + + for (row = index.row(); row <= lastRow; row++) { + checkInputPath(row); + } + + success = true; + } + } + } else if (index.row() == queue.length()) { + QString path = value.toString(); + + if (!path.isEmpty()) { + RemuxQueueEntry entry; + entry.sourcePath = path; + + beginInsertRows(QModelIndex(), queue.length() + 1, + queue.length() + 1); + queue.append(entry); + endInsertRows(); + + checkInputPath(index.row()); + success = true; + } + } else { + QString path = value.toString(); + + if (path.isEmpty()) { + if (index.column() == RemuxEntryColumn::InputPath) { + beginRemoveRows(QModelIndex(), index.row(), + index.row()); + queue.removeAt(index.row()); + endRemoveRows(); + } + } else { + switch (index.column()) { + case RemuxEntryColumn::InputPath: + queue[index.row()].sourcePath = value.toString(); + checkInputPath(index.row()); + success = true; + break; + case RemuxEntryColumn::OutputPath: + queue[index.row()].targetPath = value.toString(); + emit dataChanged(index, index); + success = true; + break; + } + } + } + + return success; +} + +QVariant RemuxQueueModel::getIcon(RemuxEntryState state) +{ + QVariant icon; + QStyle *style = QApplication::style(); + + switch (state) { + case RemuxEntryState::Complete: + icon = style->standardIcon( + QStyle::SP_DialogApplyButton); + break; + + case RemuxEntryState::InProgress: + icon = style->standardIcon( + QStyle::SP_ArrowRight); + break; + + case RemuxEntryState::Error: + icon = style->standardIcon( + QStyle::SP_DialogCancelButton); + break; + + case RemuxEntryState::InvalidPath: + icon = style->standardIcon( + QStyle::SP_MessageBoxWarning); + break; + } + + return icon; +} + +void RemuxQueueModel::checkInputPath(int row) +{ + RemuxQueueEntry &entry = queue[row]; + + if (entry.sourcePath.isEmpty()) { + entry.state = RemuxEntryState::Empty; + } else { + QFileInfo fileInfo(entry.sourcePath); + if (fileInfo.exists()) + entry.state = RemuxEntryState::Ready; + else + entry.state = RemuxEntryState::InvalidPath; + + if (entry.state == RemuxEntryState::Ready) + entry.targetPath = fileInfo.path() + QDir::separator() + + fileInfo.baseName() + ".mp4"; + } + + if (entry.state == RemuxEntryState::Ready && isProcessing) + entry.state = RemuxEntryState::Pending; + + emit dataChanged(index(row, 0), index(row, RemuxEntryColumn::Count)); +} + +QFileInfoList RemuxQueueModel::checkForOverwrites() const { + QFileInfoList list; + + for (const RemuxQueueEntry &entry : queue) { + if (entry.state == RemuxEntryState::Ready) { + QFileInfo fileInfo(entry.targetPath); + if (fileInfo.exists()) { + list.append(fileInfo); + } + } + } + + return list; +} + +bool RemuxQueueModel::checkForErrors() const +{ + bool hasErrors = false; + + for (const RemuxQueueEntry &entry : queue) { + if (entry.state == RemuxEntryState::Error) { + hasErrors = true; + break; + } + } + + return hasErrors; +} + +void RemuxQueueModel::clearAll() +{ + beginRemoveRows(QModelIndex(), 0, queue.size() - 1); + queue.clear(); + endRemoveRows(); +} + +void RemuxQueueModel::clearFinished() +{ + int index = 0; + + for (index = 0; index < queue.size(); index++) { + const RemuxQueueEntry &entry = queue[index]; + if (entry.state == RemuxEntryState::Complete) { + beginRemoveRows(QModelIndex(), index, index); + queue.removeAt(index); + endRemoveRows(); + index--; + } + } +} + +bool RemuxQueueModel::canClearFinished() const +{ + bool canClearFinished = false; + for (const RemuxQueueEntry &entry : queue) + if (entry.state == RemuxEntryState::Complete) { + canClearFinished = true; + break; + } + + return canClearFinished; +} + +void RemuxQueueModel::beginProcessing() +{ + for (RemuxQueueEntry &entry : queue) + if (entry.state == RemuxEntryState::Ready) + entry.state = RemuxEntryState::Pending; + + // Signal that the insertion point no longer exists. + beginRemoveRows(QModelIndex(), queue.length(), queue.length()); + endRemoveRows(); + + isProcessing = true; + + emit dataChanged(index(0, RemuxEntryColumn::State), + index(queue.length(), RemuxEntryColumn::State)); +} + +void RemuxQueueModel::endProcessing() +{ + for (RemuxQueueEntry &entry : queue) { + if (entry.state == RemuxEntryState::Pending) { + entry.state = RemuxEntryState::Ready; + } + } + + // Signal that the insertion point exists again. + + if (!autoRemux) { + beginInsertRows(QModelIndex(), queue.length(), queue.length()); + endInsertRows(); + } + + isProcessing = false; + + emit dataChanged(index(0, RemuxEntryColumn::State), + index(queue.length(), RemuxEntryColumn::State)); +} + +bool RemuxQueueModel::beginNextEntry(QString &inputPath, QString &outputPath) +{ + bool anyStarted = false; + + for (int row = 0; row < queue.length(); row++) { + RemuxQueueEntry &entry = queue[row]; + if (entry.state == RemuxEntryState::Pending) { + entry.state = RemuxEntryState::InProgress; + + inputPath = entry.sourcePath; + outputPath = entry.targetPath; + + QModelIndex index = this->index(row, + RemuxEntryColumn::State); + emit dataChanged(index, index); + + anyStarted = true; + break; + } + } + + return anyStarted; +} + +void RemuxQueueModel::finishEntry(bool success) +{ + for (int row = 0; row < queue.length(); row++) { + RemuxQueueEntry &entry = queue[row]; + if (entry.state == RemuxEntryState::InProgress) { + if (success) + entry.state = RemuxEntryState::Complete; + else + entry.state = RemuxEntryState::Error; + + QModelIndex index = this->index(row, + RemuxEntryColumn::State); + emit dataChanged(index, index); + + break; + } + } +} + +/********************************************************** + The actual remux window implementation +**********************************************************/ + +OBSRemux::OBSRemux(const char *path, QWidget *parent, bool autoRemux_) + : QDialog (parent), + queueModel(new RemuxQueueModel), + worker (new RemuxWorker()), + ui (new Ui::OBSRemux), + recPath (path), + autoRemux (autoRemux_) +{ + setAcceptDrops(true); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + ui->setupUi(this); ui->progressBar->setVisible(false); ui->buttonBox->button(QDialogButtonBox::Ok)-> setEnabled(false); - ui->targetFile->setEnabled(false); - ui->browseTarget->setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(false); + + if (autoRemux) { + resize(280, 40); + ui->tableView->hide(); + ui->buttonBox->hide(); + ui->label->hide(); + } ui->progressBar->setMinimum(0); ui->progressBar->setMaximum(1000); ui->progressBar->setValue(0); - installEventFilter(CreateShortcutFilter()); - - connect(ui->browseSource, &QPushButton::clicked, - [&]() { BrowseInput(); }); - connect(ui->browseTarget, &QPushButton::clicked, - [&]() { BrowseOutput(); }); + ui->tableView->setModel(queueModel); + ui->tableView->setItemDelegateForColumn(RemuxEntryColumn::InputPath, + new RemuxEntryPathItemDelegate(false, recPath)); + ui->tableView->setItemDelegateForColumn(RemuxEntryColumn::OutputPath, + new RemuxEntryPathItemDelegate(true, recPath)); + ui->tableView->horizontalHeader()->setSectionResizeMode( + QHeaderView::ResizeMode::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode( + RemuxEntryColumn::State, + QHeaderView::ResizeMode::Fixed); + ui->tableView->setEditTriggers( + QAbstractItemView::EditTrigger::CurrentChanged); - connect(ui->sourceFile, &QLineEdit::textChanged, - this, &OBSRemux::inputChanged); + installEventFilter(CreateShortcutFilter()); ui->buttonBox->button(QDialogButtonBox::Ok)-> setText(QTStr("Remux.Remux")); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setText(QTStr("Remux.ClearFinished")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setText(QTStr("Remux.ClearAll")); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setDisabled(true); connect(ui->buttonBox->button(QDialogButtonBox::Ok), - SIGNAL(clicked()), this, SLOT(Remux())); - + SIGNAL(clicked()), this, SLOT(beginRemux())); + connect(ui->buttonBox->button(QDialogButtonBox::Reset), + SIGNAL(clicked()), this, SLOT(clearFinished())); + connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), + SIGNAL(clicked()), this, SLOT(clearAll())); connect(ui->buttonBox->button(QDialogButtonBox::Close), - SIGNAL(clicked()), this, SLOT(close())); + SIGNAL(clicked()), this, SLOT(close())); worker->moveToThread(&remuxer); remuxer.start(); @@ -79,107 +711,212 @@ OBSRemux::OBSRemux(const char *path, QWidget *parent) connect(worker_, &RemuxWorker::remuxFinished, this, &OBSRemux::remuxFinished); connect(this, &OBSRemux::remux, worker_, &RemuxWorker::remux); + + // Guessing the GCC bug mentioned above would also affect + // QPointer? Unsure. + RemuxQueueModel *queueModel_ = queueModel; + connect(queueModel_, + SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, + SLOT(rowCountChanged(const QModelIndex &, int, int))); + connect(queueModel_, + SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, + SLOT(rowCountChanged(const QModelIndex &, int, int))); + + QModelIndex index = queueModel->createIndex(0, 1); + QMetaObject::invokeMethod(ui->tableView, + "setCurrentIndex", Qt::QueuedConnection, + Q_ARG(const QModelIndex &, index)); } -bool OBSRemux::Stop() +bool OBSRemux::stopRemux() { - if (!worker->job) + if (!worker->isWorking) return true; + // By locking the worker thread's mutex, we ensure that its + // update poll will be blocked as long as we're in here with + // the popup open. + QMutexLocker lock(&worker->updateMutex); + + bool exit = false; + if (QMessageBox::critical(nullptr, - QTStr("Remux.ExitUnfinishedTitle"), - QTStr("Remux.ExitUnfinished"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No) == + QTStr("Remux.ExitUnfinishedTitle"), + QTStr("Remux.ExitUnfinished"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) == QMessageBox::Yes) { - os_event_signal(worker->stop); - return true; + exit = true; + } + + if (exit) { + // Inform the worker it should no longer be + // working. It will interrupt accordingly in + // its next update callback. + worker->isWorking = false; } - return false; + return exit; } OBSRemux::~OBSRemux() { - Stop(); + stopRemux(); remuxer.quit(); remuxer.wait(); } -#define RECORDING_PATTERN "(*.flv *.mp4 *.mov *.mkv *.ts *.m3u8)" +void OBSRemux::rowCountChanged(const QModelIndex &, int, int) +{ + // See if there are still any rows ready to remux. Change + // the state of the "go" button accordingly. + // There must be more than one row, since there will always be + // at least one row for the empty insertion point. + if (queueModel->rowCount() > 1) { + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setEnabled(queueModel->canClearFinished()); + } else { + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setEnabled(false); + } +} -void OBSRemux::BrowseInput() +void OBSRemux::dropEvent(QDropEvent *ev) { - QString path = ui->sourceFile->text(); - if (path.isEmpty()) - path = recPath; + QStringList urlList; + + for (QUrl url : ev->mimeData()->urls()) { + QFileInfo fileInfo(url.toLocalFile()); + + if (fileInfo.isDir()) { + QStringList directoryFilter; + directoryFilter << + "*.flv" << + "*.mp4" << + "*.mov" << + "*.mkv" << + "*.ts" << + "*.m3u8"; + + QDirIterator dirIter(fileInfo.absoluteFilePath(), + directoryFilter, QDir::Files, + QDirIterator::Subdirectories); + + while (dirIter.hasNext()) { + urlList.append(dirIter.next()); + } + } else { + urlList.append(fileInfo.canonicalFilePath()); + } + } - path = QFileDialog::getOpenFileName(this, - QTStr("Remux.SelectRecording"), path, - QTStr("Remux.OBSRecording") + QString(" ") + - RECORDING_PATTERN); + if (urlList.empty()) { + QMessageBox::information(nullptr, + QTStr("Remux.NoFilesAddedTitle"), + QTStr("Remux.NoFilesAdded"), QMessageBox::Ok); + } else if (!autoRemux) { + QModelIndex insertIndex = queueModel->index( + queueModel->rowCount() - 1, + RemuxEntryColumn::InputPath); + queueModel->setData(insertIndex, urlList, + RemuxEntryRole::NewPathsToProcessRole); + } +} - inputChanged(path); +void OBSRemux::dragEnterEvent(QDragEnterEvent *ev) +{ + if (ev->mimeData()->hasUrls() && !worker->isWorking) + ev->accept(); } -void OBSRemux::inputChanged(const QString &path) +void OBSRemux::beginRemux() { - if (!QFileInfo::exists(path)) { - ui->buttonBox->button(QDialogButtonBox::Ok)-> - setEnabled(false); + if (worker->isWorking) { + stopRemux(); return; } - ui->sourceFile->setText(path); - ui->buttonBox->button(QDialogButtonBox::Ok)-> - setEnabled(true); + bool proceedWithRemux = true; + QFileInfoList overwriteFiles = queueModel->checkForOverwrites(); - QFileInfo fi(path); - QString mp4 = fi.path() + "/" + fi.baseName() + ".mp4"; - ui->targetFile->setText(mp4); + if (!overwriteFiles.empty()) { + QString message = QTStr("Remux.FileExists"); + message += "\n\n"; - ui->targetFile->setEnabled(true); - ui->browseTarget->setEnabled(true); -} + for (QFileInfo fileInfo : overwriteFiles) + message += fileInfo.canonicalFilePath() + "\n"; -void OBSRemux::BrowseOutput() -{ - QString path(ui->targetFile->text()); - path = QFileDialog::getSaveFileName(this, QTStr("Remux.SelectTarget"), - path, RECORDING_PATTERN); + if (OBSMessageBox::question(this, + QTStr("Remux.FileExistsTitle"), message) + != QMessageBox::Yes) + proceedWithRemux = false; + } - if (path.isEmpty()) + if (!proceedWithRemux) return; - ui->targetFile->setText(path); + // Set all jobs to "pending" first. + queueModel->beginProcessing(); + + ui->progressBar->setVisible(true); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setText(QTStr("Remux.Stop")); + setAcceptDrops(false); + + remuxNextEntry(); } -void OBSRemux::Remux() +void OBSRemux::AutoRemux(QString inFile, QString outFile) { - if (QFileInfo::exists(ui->targetFile->text())) - if (OBSMessageBox::question(this, QTStr("Remux.FileExistsTitle"), - QTStr("Remux.FileExists")) != - QMessageBox::Yes) - return; - - media_remux_job_t mr_job = nullptr; - if (!media_remux_job_create(&mr_job, QT_TO_UTF8(ui->sourceFile->text()), - QT_TO_UTF8(ui->targetFile->text()))) - return; + if (inFile != "" && outFile != "" && autoRemux) { + emit remux(inFile, outFile); + autoRemuxFile = inFile; + } +} - worker->job = job_t(mr_job, media_remux_job_destroy); +void OBSRemux::remuxNextEntry() +{ worker->lastProgress = 0.f; - ui->progressBar->setVisible(true); - ui->buttonBox->button(QDialogButtonBox::Ok)-> - setEnabled(false); - - emit remux(); + QString inputPath, outputPath; + if (queueModel->beginNextEntry(inputPath, outputPath)) { + emit remux(inputPath, outputPath); + } else { + queueModel->autoRemux = autoRemux; + queueModel->endProcessing(); + + if (!autoRemux) { + OBSMessageBox::information(this, + QTStr("Remux.FinishedTitle"), + queueModel->checkForErrors() + ? QTStr("Remux.FinishedError") + : QTStr("Remux.Finished")); + } + + ui->progressBar->setVisible(autoRemux); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setText(QTStr("Remux.Remux")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setEnabled(queueModel->canClearFinished()); + setAcceptDrops(true); + } } void OBSRemux::closeEvent(QCloseEvent *event) { - if (!Stop()) + if (!stopRemux()) event->ignore(); else QDialog::closeEvent(event); @@ -187,7 +924,7 @@ void OBSRemux::closeEvent(QCloseEvent *event) void OBSRemux::reject() { - if (!Stop()) + if (!stopRemux()) return; QDialog::reject(); @@ -200,26 +937,34 @@ void OBSRemux::updateProgress(float percent) void OBSRemux::remuxFinished(bool success) { - OBSMessageBox::information(this, QTStr("Remux.FinishedTitle"), - success ? - QTStr("Remux.Finished") : QTStr("Remux.FinishedError")); - - worker->job.reset(); - ui->progressBar->setVisible(false); ui->buttonBox->button(QDialogButtonBox::Ok)-> setEnabled(true); + + queueModel->finishEntry(success); + + if (autoRemux && autoRemuxFile != "") { + QFile::remove(autoRemuxFile); + QTimer::singleShot(3000, this, SLOT(close())); + } + + remuxNextEntry(); } -RemuxWorker::RemuxWorker() +void OBSRemux::clearFinished() { - os_event_init(&stop, OS_EVENT_TYPE_MANUAL); + queueModel->clearFinished(); } -RemuxWorker::~RemuxWorker() +void OBSRemux::clearAll() { - os_event_destroy(stop); + queueModel->clearAll(); } +/********************************************************** + Worker thread - Executes the libobs remux operation as a + background process. +**********************************************************/ + void RemuxWorker::UpdateProgress(float percent) { if (abs(lastProgress - percent) < 0.1f) @@ -229,16 +974,38 @@ void RemuxWorker::UpdateProgress(float percent) lastProgress = percent; } -void RemuxWorker::remux() +void RemuxWorker::remux(const QString &source, const QString &target) { + isWorking = true; + auto callback = [](void *data, float percent) { - auto rw = static_cast(data); + RemuxWorker *rw = static_cast(data); + + QMutexLocker lock(&rw->updateMutex); + rw->UpdateProgress(percent); - return !!os_event_try(rw->stop); + + return rw->isWorking; }; - bool success = media_remux_job_process(job.get(), callback, this); + bool stopped = false; + bool success = false; + + media_remux_job_t mr_job = nullptr; + if (media_remux_job_create(&mr_job, + QT_TO_UTF8(source), + QT_TO_UTF8(target))) { + + success = media_remux_job_process(mr_job, callback, + this); + + media_remux_job_destroy(mr_job); + + stopped = !isWorking; + } + + isWorking = false; - emit remuxFinished(os_event_try(stop) && success); + emit remuxFinished(!stopped && success); } diff --git a/UI/window-remux.hpp b/UI/window-remux.hpp index d68982871..ce5b168c3 100644 --- a/UI/window-remux.hpp +++ b/UI/window-remux.hpp @@ -17,6 +17,8 @@ #pragma once +#include +#include #include #include #include @@ -25,11 +27,25 @@ #include #include +class RemuxQueueModel; class RemuxWorker; +enum RemuxEntryState +{ + Empty, + Ready, + Pending, + InProgress, + Complete, + InvalidPath, + Error +}; +Q_DECLARE_METATYPE(RemuxEntryState); + class OBSRemux : public QDialog { Q_OBJECT + QPointer queueModel; QThread remuxer; QPointer worker; @@ -37,46 +53,106 @@ class OBSRemux : public QDialog { const char *recPath; - void BrowseInput(); - void BrowseOutput(); - - bool Stop(); - virtual void closeEvent(QCloseEvent *event) override; virtual void reject() override; + bool autoRemux; + QString autoRemuxFile; + public: - explicit OBSRemux(const char *recPath, QWidget *parent = nullptr); + explicit OBSRemux(const char *recPath, QWidget *parent = nullptr, + bool autoRemux = false); virtual ~OBSRemux() override; using job_t = std::shared_ptr; + void AutoRemux(QString inFile, QString outFile); + +protected: + void dropEvent(QDropEvent *ev); + void dragEnterEvent(QDragEnterEvent *ev); + + void remuxNextEntry(); + private slots: - void inputChanged(const QString &str); + void rowCountChanged(const QModelIndex &parent, int first, int last); public slots: void updateProgress(float percent); void remuxFinished(bool success); - void Remux(); + void beginRemux(); + bool stopRemux(); + void clearFinished(); + void clearAll(); signals: - void remux(); + void remux(const QString &source, const QString &target); +}; + +class RemuxQueueModel : public QAbstractTableModel { + Q_OBJECT + + friend class OBSRemux; + +public: + RemuxQueueModel(QObject *parent = 0) + : QAbstractTableModel(parent) + , isProcessing(false) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role); + + QFileInfoList checkForOverwrites() const; + bool checkForErrors() const; + void beginProcessing(); + void endProcessing(); + bool beginNextEntry(QString &inputPath, QString &outputPath); + void finishEntry(bool success); + bool canClearFinished() const; + void clearFinished(); + void clearAll(); + + bool autoRemux = false; + +private: + struct RemuxQueueEntry + { + RemuxEntryState state; + + QString sourcePath; + QString targetPath; + }; + + QList queue; + bool isProcessing; + + static QVariant getIcon(RemuxEntryState state); + + void checkInputPath(int row); }; class RemuxWorker : public QObject { Q_OBJECT - OBSRemux::job_t job; - os_event_t *stop; + QMutex updateMutex; + + bool isWorking; float lastProgress; void UpdateProgress(float percent); - explicit RemuxWorker(); - virtual ~RemuxWorker(); + explicit RemuxWorker() + : isWorking(false) { } + virtual ~RemuxWorker() {}; private slots: - void remux(); + void remux(const QString &source, const QString &target); signals: void updateProgress(float percent); diff --git a/appveyor.yml b/appveyor.yml index 6d13f103e..4caf7cde6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,35 +1,26 @@ +image: + - Ubuntu + - Visual Studio 2017 + environment: CURL_VERSION: 7.56.1 + CEF_VERSION: 3.3440.1805.gbe070f9 + APPVEYOR_YML_DISABLE_PS_LINUX: true install: - git submodule update --init --recursive - - if exist dependencies2015.zip (curl -kLO https://obsproject.com/downloads/dependencies2015.zip -f --retry 5 -z dependencies2015.zip) else (curl -kLO https://obsproject.com/downloads/dependencies2015.zip -f --retry 5 -C -) - - if exist vlc.zip (curl -kLO https://obsproject.com/downloads/vlc.zip -f --retry 5 -z vlc.zip) else (curl -kLO https://obsproject.com/downloads/vlc.zip -f --retry 5 -C -) - - 7z x dependencies2015.zip -odependencies2015 - - 7z x vlc.zip -ovlc - - set DepsPath32=%CD%\dependencies2015\win32 - - set DepsPath64=%CD%\dependencies2015\win64 - - set VLCPath=%CD%\vlc - - set QTDIR32=C:\Qt\5.7\msvc2015 - - set QTDIR64=C:\Qt\5.7\msvc2015_64 - - set build_config=RelWithDebInfo - - move "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\um\d3d11on12*" "C:\Program Files (x86)\Windows Kits\8.1\Include\um" - - move "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\um\d3d12*" "C:\Program Files (x86)\Windows Kits\8.1\Include\um" - - move "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\shared\dxgi*" "C:\Program Files (x86)\Windows Kits\8.1\Include\shared" - - mkdir build - - mkdir build32 - - mkdir build64 - - cd ./build32 - - cmake -G "Visual Studio 14 2015" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true .. - - cd ../build64 - - cmake -G "Visual Studio 14 2015 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true .. + - cmd: C:\projects\obs-studio\CI\install-script-win.cmd + - sh: ./CI/install-dependencies-linux-ubuntu16.sh + - sh: ./CI/before-script-linux.sh + - sh: ./CI/install-script-linux.sh build_script: - - call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - - call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - cmd: call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - cmd: call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - sh: cd ./build && make -j4 && cd - before_deploy: - - C:\projects\obs-studio\CI\before-deploy-win.cmd + - cmd: C:\projects\obs-studio\CI\before-deploy-win.cmd deploy_script: - ps: Push-AppveyorArtifact "build.zip" -FileName "$(git describe).zip" @@ -37,5 +28,15 @@ deploy_script: test: off cache: - - dependencies2015.zip + - dependencies2017.zip - vlc.zip + - 'cef_binary_%CEF_VERSION%_windows32.zip' + - 'cef_binary_%CEF_VERSION%_windows64.zip' + +notifications: + - provider: Webhook + url: + secure: k1kpaz4CB5Rg5a3MTb4XKnd76fJ+9ozz5RACVnNjdgmAjA1OSssZ6LZ3g0NGfzc/ + headers: + Authorization: + secure: A0PBwpHtsYzBOuye1EeS0fl562T0NZEInwZp0ZVER1wLQSeE6gzWGrRo2a0E7hii diff --git a/cmake/Modules/CopyMSVCBins.cmake b/cmake/Modules/CopyMSVCBins.cmake index 9e458003f..f105eb8ea 100644 --- a/cmake/Modules/CopyMSVCBins.cmake +++ b/cmake/Modules/CopyMSVCBins.cmake @@ -147,27 +147,27 @@ if (NOT ZLIB_BIN_FILES) ) endif() -if (CMAKE_CONFIGURATION_TYPES MATCHES "Debug") - file(GLOB QT_DEBUG_BIN_FILES - "${Qt5Core_DIR}/../../../bin/Qt5Cored.dll" - "${Qt5Core_DIR}/../../../bin/Qt5Guid.dll" - "${Qt5Core_DIR}/../../../bin/Qt5Widgetsd.dll" - "${Qt5Core_DIR}/../../../bin/libGLESv2d.dll" - "${Qt5Core_DIR}/../../../bin/libEGLd.dll") - file(GLOB QT_DEBUG_PLAT_BIN_FILES - "${Qt5Core_DIR}/../../../plugins/platforms/qwindowsd.dll") -endif() - -if (CMAKE_CONFIGURATION_TYPES MATCHES "Release") - file(GLOB QT_BIN_FILES - "${Qt5Core_DIR}/../../../bin/Qt5Core.dll" - "${Qt5Core_DIR}/../../../bin/Qt5Gui.dll" - "${Qt5Core_DIR}/../../../bin/Qt5Widgets.dll" - "${Qt5Core_DIR}/../../../bin/libGLESv2.dll" - "${Qt5Core_DIR}/../../../bin/libEGL.dll") - file(GLOB QT_PLAT_BIN_FILES - "${Qt5Core_DIR}/../../../plugins/platforms/qwindows.dll") -endif() +file(GLOB QT_DEBUG_BIN_FILES + "${Qt5Core_DIR}/../../../bin/Qt5Cored.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Guid.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Widgetsd.dll" + "${Qt5Core_DIR}/../../../bin/libGLESv2d.dll" + "${Qt5Core_DIR}/../../../bin/libEGLd.dll") +file(GLOB QT_DEBUG_PLAT_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/platforms/qwindowsd.dll") +file(GLOB QT_DEBUG_STYLES_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/styles/qwindowsvistastyled.dll") + +file(GLOB QT_BIN_FILES + "${Qt5Core_DIR}/../../../bin/Qt5Core.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Gui.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Widgets.dll" + "${Qt5Core_DIR}/../../../bin/libGLESv2.dll" + "${Qt5Core_DIR}/../../../bin/libEGL.dll") +file(GLOB QT_PLAT_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/platforms/qwindows.dll") +file(GLOB QT_STYLES_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/styles/qwindowsvistastyle.dll") file(GLOB QT_ICU_BIN_FILES "${Qt5Core_DIR}/../../../bin/icu*.dll") @@ -190,16 +190,21 @@ set(ALL_DBG_BIN_FILES ${QT_DEBUG_BIN_FILES}) set(ALL_PLATFORM_BIN_FILES) - set(ALL_PLATFORM_REL_BIN_FILES ${QT_PLAT_BIN_FILES}) - set(ALL_PLATFORM_DBG_BIN_FILES ${QT_DEBUG_PLAT_BIN_FILES}) +set(ALL_STYLES_BIN_FILES) +set(ALL_STYLES_REL_BIN_FILES + ${QT_STYLES_BIN_FILES}) +set(ALL_STYLES_DBG_BIN_FILES + ${QT_DEBUG_STYLES_BIN_FILES}) + foreach(list ALL_BASE_BIN_FILES ALL_REL_BIN_FILES ALL_DBG_BIN_FILES - ALL_PLATFORM_BIN_FILES ALL_PLATFORM_REL_BIN_FILES ALL_PLATFORM_DBG_BIN_FILES) + ALL_PLATFORM_BIN_FILES ALL_PLATFORM_REL_BIN_FILES ALL_PLATFORM_DBG_BIN_FILES + ALL_STYLES_BIN_FILES ALL_STYLES_REL_BIN_FILES ALL_STYLES_DBG_BIN_FILES) if(${list}) list(REMOVE_DUPLICATES ${list}) endif() @@ -215,8 +220,10 @@ message(STATUS "ssl files: ${SSL_BIN_FILES}") message(STATUS "zlib files: ${ZLIB_BIN_FILES}") message(STATUS "QT Debug files: ${QT_DEBUG_BIN_FILES}") message(STATUS "QT Debug Platform files: ${QT_DEBUG_PLAT_BIN_FILES}") +message(STATUS "QT Debug Styles files: ${QT_DEBUG_STYLES_BIN_FILES}") message(STATUS "QT Release files: ${QT_BIN_FILES}") message(STATUS "QT Release Platform files: ${QT_PLAT_BIN_FILES}") +message(STATUS "QT Release Styles files: ${QT_STYLES_BIN_FILES}") message(STATUS "QT ICU files: ${QT_ICU_BIN_FILES}") foreach(BinFile ${ALL_BASE_BIN_FILES}) @@ -249,4 +256,19 @@ foreach(BinFile ${ALL_PLATFORM_DBG_BIN_FILES}) file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/platforms/") endforeach() +foreach(BinFile ${ALL_STYLES_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/styles") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/styles/") +endforeach() + +foreach(BinFile ${ALL_STYLES_REL_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/styles") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/styles/") +endforeach() + +foreach(BinFile ${ALL_STYLES_DBG_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/styles") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/styles/") +endforeach() + set(COPIED_DEPENDENCIES TRUE CACHE BOOL "Dependencies have been copied, set to false to copy again" FORCE) diff --git a/cmake/Modules/FindLuajit.cmake b/cmake/Modules/FindLuajit.cmake index 73e5e000d..ddd094803 100644 --- a/cmake/Modules/FindLuajit.cmake +++ b/cmake/Modules/FindLuajit.cmake @@ -44,6 +44,10 @@ FIND_PATH(LUAJIT_INCLUDE_DIR include/luajit-2.0 luajit2.0 include/luajit2.0 + luajit-2.1 + include/luajit-2.1 + luajit2.1 + include/luajit2.1 ) find_library(LUAJIT_LIB diff --git a/cmake/Modules/FindMbedTLS.cmake b/cmake/Modules/FindMbedTLS.cmake new file mode 100644 index 000000000..954df18e8 --- /dev/null +++ b/cmake/Modules/FindMbedTLS.cmake @@ -0,0 +1,143 @@ +# Once done these will be defined: +# +# LIBMBEDTLS_FOUND +# LIBMBEDTLS_INCLUDE_DIRS +# LIBMBEDTLS_LIBRARIES +# +# For use in OBS: +# +# MBEDTLS_INCLUDE_DIR + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_check_modules(_MBEDTLS QUIET mbedtls) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) +else() + set(_lib_suffix 32) +endif() + +# If we're on MacOS or Linux, please try to statically-link mbedtls. +if(STATIC_MBEDTLS AND (APPLE OR UNIX)) + set(_MBEDTLS_LIBRARIES libmbedtls.a) + set(_MBEDCRYPTO_LIBRARIES libmbedcrypto.a) + set(_MBEDX509_LIBRARIES libmbedx509.a) +endif() + +find_path(MBEDTLS_INCLUDE_DIR + NAMES mbedtls/ssl.h + HINTS + ENV mbedtlsPath${_lib_suffix} + ENV mbedtlsPath + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${mbedtlsPath${_lib_suffix}} + ${mbedtlsPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_MBEDTLS_INCLUDE_DIRS} + PATHS + /usr/include /usr/local/include /opt/local/include /sw/include + PATH_SUFFIXES + include) + +find_library(MBEDTLS_LIB + NAMES ${_MBEDTLS_LIBRARIES} mbedtls libmbedtls + HINTS + ENV mbedtlsPath${_lib_suffix} + ENV mbedtlsPath + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${mbedtlsPath${_lib_suffix}} + ${mbedtlsPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_MBEDTLS_LIBRARY_DIRS} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +find_library(MBEDCRYPTO_LIB + NAMES ${_MBEDCRYPTO_LIBRARIES} mbedcrypto libmbedcrypto + HINTS + ENV mbedcryptoPath${_lib_suffix} + ENV mbedcryptoPath + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${mbedcryptoPath${_lib_suffix}} + ${mbedcryptoPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_MBEDCRYPTO_LIBRARY_DIRS} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +find_library(MBEDX509_LIB + NAMES ${_MBEDX509_LIBRARIES} mbedx509 libmbedx509 + HINTS + ENV mbedx509Path${_lib_suffix} + ENV mbedx509Path + ENV DepsPath${_lib_suffix} + ENV DepsPath + ${mbedx509Path${_lib_suffix}} + ${mbedx509Path} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_MBEDX509_LIBRARY_DIRS} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin) + +# Sometimes mbedtls is split between three libs, and sometimes it isn't. +# If it isn't, let's check if the symbols we need are all in MBEDTLS_LIB. +if(MBEDTLS_LIB AND NOT MBEDCRYPTO_LIB AND NOT MBEDX509_LIB) + set(CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIB}) + set(CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR}) + check_symbol_exists(mbedtls_x509_crt_init "mbedtls/x509_crt.h" MBEDTLS_INCLUDES_X509) + check_symbol_exists(mbedtls_sha256_init "mbedtls/sha256.h" MBEDTLS_INCLUDES_CRYPTO) + unset(CMAKE_REQUIRED_INCLUDES) + unset(CMAKE_REQUIRED_LIBRARIES) +endif() + +# If we find all three libraries, then go ahead. +if(MBEDTLS_LIB AND MBEDCRYPTO_LIB AND MBEDX509_LIB) + set(LIBMBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(LIBMBEDTLS_LIBRARIES ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB} ${MBEDX509_LIB}) + set(MBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIRS}) + set(MBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARIES}) + +# Otherwise, if we find MBEDTLS_LIB, and it has both CRYPTO and x509 +# within the single lib (i.e. a windows build environment), then also +# feel free to go ahead. +elseif(MBEDTLS_LIB AND MBEDTLS_INCLUDES_CRYPTO AND MBEDTLS_INCLUDES_X509) + set(LIBMBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(LIBMBEDTLS_LIBRARIES ${MBEDTLS_LIB}) + set(MBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIRS}) + set(MBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARIES}) +endif() + +# Now we've accounted for the 3-vs-1 library case: +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libmbedtls DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) +mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) diff --git a/cmake/Modules/FindXCB.cmake b/cmake/Modules/FindXCB.cmake index efeea58af..7f948bc7c 100644 --- a/cmake/Modules/FindXCB.cmake +++ b/cmake/Modules/FindXCB.cmake @@ -21,6 +21,7 @@ # XCB_GLX_FOUND XCB_GLX_INCLUDE_DIR XCB_GLX_LIBRARY # XCB_SHM_FOUND XCB_SHM_INCLUDE_DIR XCB_SHM_LIBRARY # XCB_XV_FOUND XCB_XV_INCLUDE_DIR XCB_XV_LIBRARY +# XCB_XINPUT_FOUND XCB_XINPUT_INCLUDE_DIR XCB_XINPUT_LIBRARY # XCB_SYNC_FOUND XCB_SYNC_INCLUDE_DIR XCB_SYNC_LIBRARY # XCB_XTEST_FOUND XCB_XTEST_INCLUDE_DIR XCB_XTEST_LIBRARY # XCB_ICCCM_FOUND XCB_ICCCM_INCLUDE_DIR XCB_ICCCM_LIBRARY @@ -54,6 +55,7 @@ set(knownComponents XCB XFIXES XTEST XV + XINPUT XINERAMA) unset(unknownComponents) @@ -112,6 +114,8 @@ foreach(comp ${comps}) list(APPEND pkgConfigModules "xcb-xtest") elseif("${comp}" STREQUAL "XV") list(APPEND pkgConfigModules "xcb-xv") + elseif("${comp}" STREQUAL "XINPUT") + list(APPEND pkgConfigModules "xcb-xinput") elseif("${comp}" STREQUAL "XINERAMA") list(APPEND pkgConfigModules "xcb-xinerama") endif() @@ -190,6 +194,9 @@ macro(_XCB_HANDLE_COMPONENT _comp) elseif("${_comp}" STREQUAL "XV") set(_header "xcb/xv.h") set(_lib "xcb-xv") + elseif("${_comp}" STREQUAL "XINPUT") + set(_header "xcb/xinput.h") + set(_lib "xcb-xinput") elseif("${_comp}" STREQUAL "XINERAMA") set(_header "xcb/xinerama.h") set(_lib "xcb-xinerama") diff --git a/cmake/Modules/ObsCpack.cmake b/cmake/Modules/ObsCpack.cmake index ae3466470..3e29e82e2 100644 --- a/cmake/Modules/ObsCpack.cmake +++ b/cmake/Modules/ObsCpack.cmake @@ -48,13 +48,13 @@ else() set(_output_suffix "") endif() - set(CPACK_PACKAGE_EXECUTABLES "obs${_output_suffix}" "OBS Studio") - set(CPACK_CREATE_DESKTOP_LINKS "obs${_output_suffix}") + set(CPACK_PACKAGE_EXECUTABLES "ebs${_output_suffix}" "EBS Studio") + set(CPACK_CREATE_DESKTOP_LINKS "ebs${_output_suffix}") endif() set(CPACK_BUNDLE_NAME "OBS") set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/cmake/osxbundle/Info.plist") -set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/cmake/osxbundle/obs.icns") +set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/cmake/osxbundle/ebs.icns") set(CPACK_BUNDLE_STARTUP_COMMAND "${CMAKE_SOURCE_DIR}/cmake/osxbundle/obslaunch.sh") set(CPACK_WIX_TEMPLATE "${CMAKE_SOURCE_DIR}/cmake/Modules/WIX.template.in") @@ -63,7 +63,7 @@ if(INSTALLER_RUN) set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OBSStudio") set(CPACK_WIX_UPGRADE_GUID "1f59ff79-2a3c-43c1-b2b2-033a5e6342eb") set(CPACK_WIX_PRODUCT_GUID "0c7bec2a-4f07-41b2-9dff-d64b09c9c384") - set(CPACK_PACKAGE_FILE_NAME "obs-studio-${OBS_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "ebs-studio-${OBS_VERSION}") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) if(WIN32) set(CPACK_PACKAGE_NAME "OBS Studio (64bit)") @@ -71,7 +71,7 @@ elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OBSStudio64") set(CPACK_WIX_UPGRADE_GUID "44c72510-2e8e-489c-8bc0-2011a9631b0b") set(CPACK_WIX_PRODUCT_GUID "ca5bf4fe-7b38-4003-9455-de249d03caac") - set(CPACK_PACKAGE_FILE_NAME "obs-studio-x64-${OBS_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "ebs-studio-x64-${OBS_VERSION}") else() if(WIN32) set(CPACK_PACKAGE_NAME "OBS Studio (32bit)") @@ -79,7 +79,7 @@ else() set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OBSStudio32") set(CPACK_WIX_UPGRADE_GUID "a26acea4-6190-4470-9fb9-f6d32f3ba030") set(CPACK_WIX_PRODUCT_GUID "8e24982d-b0ab-4f66-9c90-f726f3b64682") - set(CPACK_PACKAGE_FILE_NAME "obs-studio-x86-${OBS_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "ebs-studio-x86-${OBS_VERSION}") endif() set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") diff --git a/cmake/Modules/ObsHelpers.cmake b/cmake/Modules/ObsHelpers.cmake index c0073d786..627aadf5b 100644 --- a/cmake/Modules/ObsHelpers.cmake +++ b/cmake/Modules/ObsHelpers.cmake @@ -504,6 +504,30 @@ function(install_obs_data target datadir datadest) endif() endfunction() +function(install_obs_data_file target datafile datadest) + install(FILES ${datafile} + DESTINATION "${OBS_DATA_DESTINATION}/${datadest}") + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory + "${OBS_OUTPUT_DIR}/$/data/${datadest}" + VERBATIM) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/${datafile}" "${OBS_OUTPUT_DIR}/$/data/${datadest}" + VERBATIM) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND DEFINED ENV{obsInstallerTempDir}) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$ENV{obsInstallerTempDir}/${OBS_DATA_DESTINATION}/${datadest}" + VERBATIM) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/${datafile}" "$ENV{obsInstallerTempDir}/${OBS_DATA_DESTINATION}/${datadest}" + VERBATIM) + endif() +endfunction() + function(install_obs_datatarget target datadest) install(TARGETS ${target} LIBRARY DESTINATION "${OBS_DATA_DESTINATION}/${datadest}" diff --git a/cmake/osxbundle/Info.plist b/cmake/osxbundle/Info.plist index 443e45693..0a1bfc291 100644 --- a/cmake/osxbundle/Info.plist +++ b/cmake/osxbundle/Info.plist @@ -3,15 +3,15 @@ CFBundleIconFile - OBS.icns + EBS.icns CFBundleName - OBS + EBS CFBundleGetInfoString - OBS - Free and Open Source Streaming/Recording Software + EBS - Studio CFBundleExecutable - OBS + EBS CFBundleIdentifier - com.obsproject.obs-studio + com.evercast.ebs-studio CFBundlePackageType APPL CFBundleSignature @@ -22,5 +22,9 @@ LSAppNapIsDisabled + NSCameraUsageDescription + OBS needs to access the camera to enable camera sources to work. + NSMicrophoneUsageDescription + OBS needs to access the microphone to enable audio input. diff --git a/cmake/osxbundle/ebs.icns b/cmake/osxbundle/ebs.icns new file mode 100644 index 000000000..07240ae2f Binary files /dev/null and b/cmake/osxbundle/ebs.icns differ diff --git a/cmake/osxbundle/obs.icns b/cmake/osxbundle/obs.icns deleted file mode 100644 index 6f878d6a3..000000000 Binary files a/cmake/osxbundle/obs.icns and /dev/null differ diff --git a/cmake/osxbundle/obslaunch.sh b/cmake/osxbundle/obslaunch.sh index 18a80db83..2ea3c585d 100755 --- a/cmake/osxbundle/obslaunch.sh +++ b/cmake/osxbundle/obslaunch.sh @@ -1,5 +1,5 @@ #!/bin/sh cd "$(dirname "$0")" cd ../Resources/bin -exec ./obs "$@" +exec ./EBS "$@" diff --git a/deps/blake2/CMakeLists.txt b/deps/blake2/CMakeLists.txt index 43092d744..f73ead78c 100644 --- a/deps/blake2/CMakeLists.txt +++ b/deps/blake2/CMakeLists.txt @@ -13,7 +13,7 @@ include_directories( if(WIN32) if(MSVC) - add_compile_options("$<$:/MT>") + add_compile_options($,/MTd,/MT> /Zl) endif() add_definitions( -Dinline=_inline diff --git a/deps/ipc-util/CMakeLists.txt b/deps/ipc-util/CMakeLists.txt index 8b2e672a3..ac6103957 100644 --- a/deps/ipc-util/CMakeLists.txt +++ b/deps/ipc-util/CMakeLists.txt @@ -23,7 +23,7 @@ else() endif() if(MSVC) - add_compile_options("$<$:/MT>") + add_compile_options($,/MTd,/MT> /Zl) endif() add_library(ipc-util STATIC diff --git a/plugins/obs-outputs/ftl-sdk/LICENSE b/deps/json11/LICENSE.txt similarity index 85% rename from plugins/obs-outputs/ftl-sdk/LICENSE rename to deps/json11/LICENSE.txt index 347e2dd76..691742e9e 100644 --- a/plugins/obs-outputs/ftl-sdk/LICENSE +++ b/deps/json11/LICENSE.txt @@ -1,6 +1,4 @@ -The MIT License (MIT) - -Copyright (c) 2016 Mixer Interactive, Inc. +Copyright (c) 2013 Dropbox, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/deps/json11/json11.cpp b/deps/json11/json11.cpp new file mode 100644 index 000000000..9647846b6 --- /dev/null +++ b/deps/json11/json11.cpp @@ -0,0 +1,788 @@ +/* Copyright (c) 2013 Dropbox, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "json11.hpp" +#include +#include +#include +#include +#include + +namespace json11 { + +static const int max_depth = 200; + +using std::string; +using std::vector; +using std::map; +using std::make_shared; +using std::initializer_list; +using std::move; + +/* Helper for representing null - just a do-nothing struct, plus comparison + * operators so the helpers in JsonValue work. We can't use nullptr_t because + * it may not be orderable. + */ +struct NullStruct { + bool operator==(NullStruct) const { return true; } + bool operator<(NullStruct) const { return false; } +}; + +/* * * * * * * * * * * * * * * * * * * * + * Serialization + */ + +static void dump(NullStruct, string &out) { + out += "null"; +} + +static void dump(double value, string &out) { + if (std::isfinite(value)) { + char buf[32]; + snprintf(buf, sizeof buf, "%.17g", value); + out += buf; + } else { + out += "null"; + } +} + +static void dump(int value, string &out) { + char buf[32]; + snprintf(buf, sizeof buf, "%d", value); + out += buf; +} + +static void dump(bool value, string &out) { + out += value ? "true" : "false"; +} + +static void dump(const string &value, string &out) { + out += '"'; + for (size_t i = 0; i < value.length(); i++) { + const char ch = value[i]; + if (ch == '\\') { + out += "\\\\"; + } else if (ch == '"') { + out += "\\\""; + } else if (ch == '\b') { + out += "\\b"; + } else if (ch == '\f') { + out += "\\f"; + } else if (ch == '\n') { + out += "\\n"; + } else if (ch == '\r') { + out += "\\r"; + } else if (ch == '\t') { + out += "\\t"; + } else if (static_cast(ch) <= 0x1f) { + char buf[8]; + snprintf(buf, sizeof buf, "\\u%04x", ch); + out += buf; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa8) { + out += "\\u2028"; + i += 2; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa9) { + out += "\\u2029"; + i += 2; + } else { + out += ch; + } + } + out += '"'; +} + +static void dump(const Json::array &values, string &out) { + bool first = true; + out += "["; + for (const auto &value : values) { + if (!first) + out += ", "; + value.dump(out); + first = false; + } + out += "]"; +} + +static void dump(const Json::object &values, string &out) { + bool first = true; + out += "{"; + for (const auto &kv : values) { + if (!first) + out += ", "; + dump(kv.first, out); + out += ": "; + kv.second.dump(out); + first = false; + } + out += "}"; +} + +void Json::dump(string &out) const { + m_ptr->dump(out); +} + +/* * * * * * * * * * * * * * * * * * * * + * Value wrappers + */ + +template +class Value : public JsonValue { +protected: + + // Constructors + explicit Value(const T &value) : m_value(value) {} + explicit Value(T &&value) : m_value(move(value)) {} + + // Get type tag + Json::Type type() const override { + return tag; + } + + // Comparisons + bool equals(const JsonValue * other) const override { + return m_value == static_cast *>(other)->m_value; + } + bool less(const JsonValue * other) const override { + return m_value < static_cast *>(other)->m_value; + } + + const T m_value; + void dump(string &out) const override { json11::dump(m_value, out); } +}; + +class JsonDouble final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return static_cast(m_value); } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } +public: + explicit JsonDouble(double value) : Value(value) {} +}; + +class JsonInt final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return m_value; } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } +public: + explicit JsonInt(int value) : Value(value) {} +}; + +class JsonBoolean final : public Value { + bool bool_value() const override { return m_value; } +public: + explicit JsonBoolean(bool value) : Value(value) {} +}; + +class JsonString final : public Value { + const string &string_value() const override { return m_value; } +public: + explicit JsonString(const string &value) : Value(value) {} + explicit JsonString(string &&value) : Value(move(value)) {} +}; + +class JsonArray final : public Value { + const Json::array &array_items() const override { return m_value; } + const Json & operator[](size_t i) const override; +public: + explicit JsonArray(const Json::array &value) : Value(value) {} + explicit JsonArray(Json::array &&value) : Value(move(value)) {} +}; + +class JsonObject final : public Value { + const Json::object &object_items() const override { return m_value; } + const Json & operator[](const string &key) const override; +public: + explicit JsonObject(const Json::object &value) : Value(value) {} + explicit JsonObject(Json::object &&value) : Value(move(value)) {} +}; + +class JsonNull final : public Value { +public: + JsonNull() : Value({}) {} +}; + +/* * * * * * * * * * * * * * * * * * * * + * Static globals - static-init-safe + */ +struct Statics { + const std::shared_ptr null = make_shared(); + const std::shared_ptr t = make_shared(true); + const std::shared_ptr f = make_shared(false); + const string empty_string; + const vector empty_vector; + const map empty_map; + Statics() {} +}; + +static const Statics & statics() { + static const Statics s {}; + return s; +} + +static const Json & static_null() { + // This has to be separate, not in Statics, because Json() accesses statics().null. + static const Json json_null; + return json_null; +} + +/* * * * * * * * * * * * * * * * * * * * + * Constructors + */ + +Json::Json() noexcept : m_ptr(statics().null) {} +Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} +Json::Json(double value) : m_ptr(make_shared(value)) {} +Json::Json(int value) : m_ptr(make_shared(value)) {} +Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} +Json::Json(const string &value) : m_ptr(make_shared(value)) {} +Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} +Json::Json(const char * value) : m_ptr(make_shared(value)) {} +Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} +Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} + +/* * * * * * * * * * * * * * * * * * * * + * Accessors + */ + +Json::Type Json::type() const { return m_ptr->type(); } +double Json::number_value() const { return m_ptr->number_value(); } +int Json::int_value() const { return m_ptr->int_value(); } +bool Json::bool_value() const { return m_ptr->bool_value(); } +const string & Json::string_value() const { return m_ptr->string_value(); } +const vector & Json::array_items() const { return m_ptr->array_items(); } +const map & Json::object_items() const { return m_ptr->object_items(); } +const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } +const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } + +double JsonValue::number_value() const { return 0; } +int JsonValue::int_value() const { return 0; } +bool JsonValue::bool_value() const { return false; } +const string & JsonValue::string_value() const { return statics().empty_string; } +const vector & JsonValue::array_items() const { return statics().empty_vector; } +const map & JsonValue::object_items() const { return statics().empty_map; } +const Json & JsonValue::operator[] (size_t) const { return static_null(); } +const Json & JsonValue::operator[] (const string &) const { return static_null(); } + +const Json & JsonObject::operator[] (const string &key) const { + auto iter = m_value.find(key); + return (iter == m_value.end()) ? static_null() : iter->second; +} +const Json & JsonArray::operator[] (size_t i) const { + if (i >= m_value.size()) return static_null(); + else return m_value[i]; +} + +/* * * * * * * * * * * * * * * * * * * * + * Comparison + */ + +bool Json::operator== (const Json &other) const { + if (m_ptr == other.m_ptr) + return true; + if (m_ptr->type() != other.m_ptr->type()) + return false; + + return m_ptr->equals(other.m_ptr.get()); +} + +bool Json::operator< (const Json &other) const { + if (m_ptr == other.m_ptr) + return false; + if (m_ptr->type() != other.m_ptr->type()) + return m_ptr->type() < other.m_ptr->type(); + + return m_ptr->less(other.m_ptr.get()); +} + +/* * * * * * * * * * * * * * * * * * * * + * Parsing + */ + +/* esc(c) + * + * Format char c suitable for printing in an error message. + */ +static inline string esc(char c) { + char buf[12]; + if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { + snprintf(buf, sizeof buf, "'%c' (%d)", c, c); + } else { + snprintf(buf, sizeof buf, "(%d)", c); + } + return string(buf); +} + +static inline bool in_range(long x, long lower, long upper) { + return (x >= lower && x <= upper); +} + +namespace { +/* JsonParser + * + * Object that tracks all state of an in-progress parse. + */ +struct JsonParser final { + + /* State + */ + const string &str; + size_t i; + string &err; + bool failed; + const JsonParse strategy; + + /* fail(msg, err_ret = Json()) + * + * Mark this parse as failed. + */ + Json fail(string &&msg) { + return fail(move(msg), Json()); + } + + template + T fail(string &&msg, const T err_ret) { + if (!failed) + err = std::move(msg); + failed = true; + return err_ret; + } + + /* consume_whitespace() + * + * Advance until the current character is non-whitespace. + */ + void consume_whitespace() { + while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') + i++; + } + + /* consume_comment() + * + * Advance comments (c-style inline and multiline). + */ + bool consume_comment() { + bool comment_found = false; + if (str[i] == '/') { + i++; + if (i == str.size()) + return fail("unexpected end of input after start of comment", false); + if (str[i] == '/') { // inline comment + i++; + // advance until next line, or end of input + while (i < str.size() && str[i] != '\n') { + i++; + } + comment_found = true; + } + else if (str[i] == '*') { // multiline comment + i++; + if (i > str.size()-2) + return fail("unexpected end of input inside multi-line comment", false); + // advance until closing tokens + while (!(str[i] == '*' && str[i+1] == '/')) { + i++; + if (i > str.size()-2) + return fail( + "unexpected end of input inside multi-line comment", false); + } + i += 2; + comment_found = true; + } + else + return fail("malformed comment", false); + } + return comment_found; + } + + /* consume_garbage() + * + * Advance until the current character is non-whitespace and non-comment. + */ + void consume_garbage() { + consume_whitespace(); + if(strategy == JsonParse::COMMENTS) { + bool comment_found = false; + do { + comment_found = consume_comment(); + if (failed) return; + consume_whitespace(); + } + while(comment_found); + } + } + + /* get_next_token() + * + * Return the next non-whitespace character. If the end of the input is reached, + * flag an error and return 0. + */ + char get_next_token() { + consume_garbage(); + if (failed) return (char)0; + if (i == str.size()) + return fail("unexpected end of input", (char)0); + + return str[i++]; + } + + /* encode_utf8(pt, out) + * + * Encode pt as UTF-8 and add it to out. + */ + void encode_utf8(long pt, string & out) { + if (pt < 0) + return; + + if (pt < 0x80) { + out += static_cast(pt); + } else if (pt < 0x800) { + out += static_cast((pt >> 6) | 0xC0); + out += static_cast((pt & 0x3F) | 0x80); + } else if (pt < 0x10000) { + out += static_cast((pt >> 12) | 0xE0); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } else { + out += static_cast((pt >> 18) | 0xF0); + out += static_cast(((pt >> 12) & 0x3F) | 0x80); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } + } + + /* parse_string() + * + * Parse a string, starting at the current position. + */ + string parse_string() { + string out; + long last_escaped_codepoint = -1; + while (true) { + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + char ch = str[i++]; + + if (ch == '"') { + encode_utf8(last_escaped_codepoint, out); + return out; + } + + if (in_range(ch, 0, 0x1f)) + return fail("unescaped " + esc(ch) + " in string", ""); + + // The usual case: non-escaped characters + if (ch != '\\') { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + out += ch; + continue; + } + + // Handle escapes + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + ch = str[i++]; + + if (ch == 'u') { + // Extract 4-byte escape sequence + string esc = str.substr(i, 4); + // Explicitly check length of the substring. The following loop + // relies on std::string returning the terminating NUL when + // accessing str[length]. Checking here reduces brittleness. + if (esc.length() < 4) { + return fail("bad \\u escape: " + esc, ""); + } + for (size_t j = 0; j < 4; j++) { + if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') + && !in_range(esc[j], '0', '9')) + return fail("bad \\u escape: " + esc, ""); + } + + long codepoint = strtol(esc.data(), nullptr, 16); + + // JSON specifies that characters outside the BMP shall be encoded as a pair + // of 4-hex-digit \u escapes encoding their surrogate pair components. Check + // whether we're in the middle of such a beast: the previous codepoint was an + // escaped lead (high) surrogate, and this is a trail (low) surrogate. + if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) + && in_range(codepoint, 0xDC00, 0xDFFF)) { + // Reassemble the two surrogate pairs into one astral-plane character, per + // the UTF-16 algorithm. + encode_utf8((((last_escaped_codepoint - 0xD800) << 10) + | (codepoint - 0xDC00)) + 0x10000, out); + last_escaped_codepoint = -1; + } else { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = codepoint; + } + + i += 4; + continue; + } + + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + + if (ch == 'b') { + out += '\b'; + } else if (ch == 'f') { + out += '\f'; + } else if (ch == 'n') { + out += '\n'; + } else if (ch == 'r') { + out += '\r'; + } else if (ch == 't') { + out += '\t'; + } else if (ch == '"' || ch == '\\' || ch == '/') { + out += ch; + } else { + return fail("invalid escape character " + esc(ch), ""); + } + } + } + + /* parse_number() + * + * Parse a double. + */ + Json parse_number() { + size_t start_pos = i; + + if (str[i] == '-') + i++; + + // Integer part + if (str[i] == '0') { + i++; + if (in_range(str[i], '0', '9')) + return fail("leading 0s not permitted in numbers"); + } else if (in_range(str[i], '1', '9')) { + i++; + while (in_range(str[i], '0', '9')) + i++; + } else { + return fail("invalid " + esc(str[i]) + " in number"); + } + + if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' + && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { + return std::atoi(str.c_str() + start_pos); + } + + // Decimal part + if (str[i] == '.') { + i++; + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in fractional part"); + + while (in_range(str[i], '0', '9')) + i++; + } + + // Exponent part + if (str[i] == 'e' || str[i] == 'E') { + i++; + + if (str[i] == '+' || str[i] == '-') + i++; + + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in exponent"); + + while (in_range(str[i], '0', '9')) + i++; + } + + return std::strtod(str.c_str() + start_pos, nullptr); + } + + /* expect(str, res) + * + * Expect that 'str' starts at the character that was just read. If it does, advance + * the input and return res. If not, flag an error. + */ + Json expect(const string &expected, Json res) { + assert(i != 0); + i--; + if (str.compare(i, expected.length(), expected) == 0) { + i += expected.length(); + return res; + } else { + return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); + } + } + + /* parse_json() + * + * Parse a JSON object. + */ + Json parse_json(int depth) { + if (depth > max_depth) { + return fail("exceeded maximum nesting depth"); + } + + char ch = get_next_token(); + if (failed) + return Json(); + + if (ch == '-' || (ch >= '0' && ch <= '9')) { + i--; + return parse_number(); + } + + if (ch == 't') + return expect("true", true); + + if (ch == 'f') + return expect("false", false); + + if (ch == 'n') + return expect("null", Json()); + + if (ch == '"') + return parse_string(); + + if (ch == '{') { + map data; + ch = get_next_token(); + if (ch == '}') + return data; + + while (1) { + if (ch != '"') + return fail("expected '\"' in object, got " + esc(ch)); + + string key = parse_string(); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch != ':') + return fail("expected ':' in object, got " + esc(ch)); + + data[std::move(key)] = parse_json(depth + 1); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == '}') + break; + if (ch != ',') + return fail("expected ',' in object, got " + esc(ch)); + + ch = get_next_token(); + } + return data; + } + + if (ch == '[') { + vector data; + ch = get_next_token(); + if (ch == ']') + return data; + + while (1) { + i--; + data.push_back(parse_json(depth + 1)); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == ']') + break; + if (ch != ',') + return fail("expected ',' in list, got " + esc(ch)); + + ch = get_next_token(); + (void)ch; + } + return data; + } + + return fail("expected value, got " + esc(ch)); + } +}; +}//namespace { + +Json Json::parse(const string &in, string &err, JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + Json result = parser.parse_json(0); + + // Check for any trailing garbage + parser.consume_garbage(); + if (parser.failed) + return Json(); + if (parser.i != in.size()) + return parser.fail("unexpected trailing " + esc(in[parser.i])); + + return result; +} + +// Documented in json11.hpp +vector Json::parse_multi(const string &in, + std::string::size_type &parser_stop_pos, + string &err, + JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + parser_stop_pos = 0; + vector json_vec; + while (parser.i != in.size() && !parser.failed) { + json_vec.push_back(parser.parse_json(0)); + if (parser.failed) + break; + + // Check for another object + parser.consume_garbage(); + if (parser.failed) + break; + parser_stop_pos = parser.i; + } + return json_vec; +} + +/* * * * * * * * * * * * * * * * * * * * + * Shape-checking + */ + +bool Json::has_shape(const shape & types, string & err) const { + if (!is_object()) { + err = "expected JSON object, got " + dump(); + return false; + } + + for (auto & item : types) { + if ((*this)[item.first].type() != item.second) { + err = "bad type for " + item.first + " in " + dump(); + return false; + } + } + + return true; +} + +} // namespace json11 diff --git a/deps/json11/json11.hpp b/deps/json11/json11.hpp new file mode 100644 index 000000000..0c47d0509 --- /dev/null +++ b/deps/json11/json11.hpp @@ -0,0 +1,232 @@ +/* json11 + * + * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. + * + * The core object provided by the library is json11::Json. A Json object represents any JSON + * value: null, bool, number (int or double), string (std::string), array (std::vector), or + * object (std::map). + * + * Json objects act like values: they can be assigned, copied, moved, compared for equality or + * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and + * Json::parse (static) to parse a std::string as a Json object. + * + * Internally, the various types of Json object are represented by the JsonValue class + * hierarchy. + * + * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, + * so some JSON implementations distinguish between integers and floating-point numbers, while + * some don't. In json11, we choose the latter. Because some JSON implementations (namely + * Javascript itself) treat all numbers as the same type, distinguishing the two leads + * to JSON that will be *silently* changed by a round-trip through those implementations. + * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also + * provides integer helpers. + * + * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the + * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 + * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch + * will be exact for +/- 275 years.) + */ + +/* Copyright (c) 2013 Dropbox, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #if _MSC_VER <= 1800 // VS 2013 + #ifndef noexcept + #define noexcept throw() + #endif + + #ifndef snprintf + #define snprintf _snprintf_s + #endif + #endif +#endif + +namespace json11 { + +enum JsonParse { + STANDARD, COMMENTS +}; + +class JsonValue; + +class Json final { +public: + // Types + enum Type { + NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT + }; + + // Array and object typedefs + typedef std::vector array; + typedef std::map object; + + // Constructors for the various types of JSON value. + Json() noexcept; // NUL + Json(std::nullptr_t) noexcept; // NUL + Json(double value); // NUMBER + Json(int value); // NUMBER + Json(bool value); // BOOL + Json(const std::string &value); // STRING + Json(std::string &&value); // STRING + Json(const char * value); // STRING + Json(const array &values); // ARRAY + Json(array &&values); // ARRAY + Json(const object &values); // OBJECT + Json(object &&values); // OBJECT + + // Implicit constructor: anything with a to_json() function. + template + Json(const T & t) : Json(t.to_json()) {} + + // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) + template ().begin()->first)>::value + && std::is_constructible().begin()->second)>::value, + int>::type = 0> + Json(const M & m) : Json(object(m.begin(), m.end())) {} + + // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) + template ().begin())>::value, + int>::type = 0> + Json(const V & v) : Json(array(v.begin(), v.end())) {} + + // This prevents Json(some_pointer) from accidentally producing a bool. Use + // Json(bool(some_pointer)) if that behavior is desired. + Json(void *) = delete; + + // Accessors + Type type() const; + + bool is_null() const { return type() == NUL; } + bool is_number() const { return type() == NUMBER; } + bool is_bool() const { return type() == BOOL; } + bool is_string() const { return type() == STRING; } + bool is_array() const { return type() == ARRAY; } + bool is_object() const { return type() == OBJECT; } + + // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not + // distinguish between integer and non-integer numbers - number_value() and int_value() + // can both be applied to a NUMBER-typed object. + double number_value() const; + int int_value() const; + + // Return the enclosed value if this is a boolean, false otherwise. + bool bool_value() const; + // Return the enclosed string if this is a string, "" otherwise. + const std::string &string_value() const; + // Return the enclosed std::vector if this is an array, or an empty vector otherwise. + const array &array_items() const; + // Return the enclosed std::map if this is an object, or an empty map otherwise. + const object &object_items() const; + + // Return a reference to arr[i] if this is an array, Json() otherwise. + const Json & operator[](size_t i) const; + // Return a reference to obj[key] if this is an object, Json() otherwise. + const Json & operator[](const std::string &key) const; + + // Serialize. + void dump(std::string &out) const; + std::string dump() const { + std::string out; + dump(out); + return out; + } + + // Parse. If parse fails, return Json() and assign an error message to err. + static Json parse(const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + static Json parse(const char * in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + if (in) { + return parse(std::string(in), err, strategy); + } else { + err = "null input"; + return nullptr; + } + } + // Parse multiple objects, concatenated or separated by whitespace + static std::vector parse_multi( + const std::string & in, + std::string::size_type & parser_stop_pos, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + + static inline std::vector parse_multi( + const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + std::string::size_type parser_stop_pos; + return parse_multi(in, parser_stop_pos, err, strategy); + } + + bool operator== (const Json &rhs) const; + bool operator< (const Json &rhs) const; + bool operator!= (const Json &rhs) const { return !(*this == rhs); } + bool operator<= (const Json &rhs) const { return !(rhs < *this); } + bool operator> (const Json &rhs) const { return (rhs < *this); } + bool operator>= (const Json &rhs) const { return !(*this < rhs); } + + /* has_shape(types, err) + * + * Return true if this is a JSON object and, for each item in types, has a field of + * the given type. If not, return false and set err to a descriptive message. + */ + typedef std::initializer_list> shape; + bool has_shape(const shape & types, std::string & err) const; + +private: + std::shared_ptr m_ptr; +}; + +// Internal class hierarchy - JsonValue objects are not exposed to users of this API. +class JsonValue { +protected: + friend class Json; + friend class JsonInt; + friend class JsonDouble; + virtual Json::Type type() const = 0; + virtual bool equals(const JsonValue * other) const = 0; + virtual bool less(const JsonValue * other) const = 0; + virtual void dump(std::string &out) const = 0; + virtual double number_value() const; + virtual int int_value() const; + virtual bool bool_value() const; + virtual const std::string &string_value() const; + virtual const Json::array &array_items() const; + virtual const Json &operator[](size_t i) const; + virtual const Json::object &object_items() const; + virtual const Json &operator[](const std::string &key) const; + virtual ~JsonValue() {} +}; + +} // namespace json11 diff --git a/deps/libcaption/CMakeLists.txt b/deps/libcaption/CMakeLists.txt index 638d53294..8d2190e91 100644 --- a/deps/libcaption/CMakeLists.txt +++ b/deps/libcaption/CMakeLists.txt @@ -12,19 +12,19 @@ set(CAPTION_SOURCES src/utf8.c src/srt.c src/scc.c - src/avc.c - src/xds.c + src/mpeg.c src/cea708.c + src/xds.c src/caption.c src/eia608_charmap.c + src/eia608_from_utf8.c src/eia608.c ) set(CAPTION_HEADERS caption/utf8.h - caption/sei.h caption/scc.h - caption/avc.c + caption/mpeg.h caption/cea708.h caption/eia608.h caption/caption.h diff --git a/deps/libcaption/LICENSE.txt b/deps/libcaption/LICENSE.txt index 43df7fba9..a770bbb56 100644 --- a/deps/libcaption/LICENSE.txt +++ b/deps/libcaption/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License -Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. +Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/deps/libcaption/README.md b/deps/libcaption/README.md index 160a95694..352ea271e 100644 --- a/deps/libcaption/README.md +++ b/deps/libcaption/README.md @@ -1,10 +1,10 @@ # version -v0.6 +v0.8 Matthew Szatmary m3u8@twitch.tv / matt@szatmary.org # libcaption -libcaption is a small library written in C to aid in the creating and parsing of closed caption data for use in the twitch player, open sourced to use within community developed broadcast tools. To maintain consistency across platforms libcaption aims to implement a subset of EIA608, CEA708 as supported by the Apple iOS platform. +libcaption is a library written in C to aid in the creating and parsing of closed caption data, open sourced under the MIT license to use within community developed broadcast tools. To maintain consistency across platforms libcaption aims to implement a subset of EIA608, CEA708 as supported by the Apple iOS platform. 608 support is currently limited to encoding and decoding the necessary control and preamble codes as well as support for the Basic North American, Special North American and Extended Western European character sets. @@ -23,10 +23,10 @@ H.264 utility functions are limited to wrapping the 708 payload into a SEI NALU. |BNA|P|Q|R|S|T|U|V|W|X|Y|Z|[|é|]|í|ó| |BNA|ú|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o| |BNA|p|q|r|s|t|u|v|w|x|y|z|ç|÷|Ñ|ñ|█| -|SNA|®|°|½|¿|™|¢|£|♪|à| |è|â|ê|î|ô|û| +|SNA|®|°|½|¿|™|¢|£|♪|à| |è|â|ê|î|ô|û| |WES|Á|É|Ó|Ú|Ü|ü|‘|¡|*|'|—|©|℠|•|“|”| |WEF|À|Â|Ç|È|Ê|Ë|ë|Î|Ï|ï|Ô|Ù|ù|Û|«|»| -|WEP|Ã|ã|Í|Ì|ì|Ò|ò|Õ|õ|{|}|\\|^|_|\||~| +|WEP|Ã|ã|Í|Ì|ì|Ò|ò|Õ|õ|{|}|\|^|_|||~| |WEG|Ä|ä|Ö|ö|ß|¥|¤|¦|Å|å|Ø|ø|┌|┐|└|┘| * BNA = Basic North American character set @@ -36,64 +36,16 @@ H.264 utility functions are limited to wrapping the 708 payload into a SEI NALU. * WEP = Extended Western European character set : Portuguese * WEG = Extended Western European character set : German/Danish - ------- -eia608_screen_t is the default internal representation. `screens` can be -converted to and from SEI messages 608/708 buffers and SRT files - -## JSON format -A JSON rendered format is provided for convenience of integration. The JSON -format is as follows: - -``` -{ - "format": "eia608", - "mode": "pop-on", - "roll-up": 0, - "data": [ - { "row":0, "col":0, "char":"A", "style":"white" }, - { "row":0, "col":1, "char":"B", "style":"white" }, - // ... - ] -} -``` - -### `format` -The only current valid value is `"eia608"`. This field exists for -future expansion. Any values other than `"eia608"`, and the object should be -ignored for now. - -### `mode` -Defines the method by which screen contents are adjusted in response to -new data, such as content in excess of the normal grid size. - -Possible modes are: - -`"clear"`: Generated when a screen is initialized, but not entered a "loading" -state. `"clear"` mode occurs frequently when the display is to be erased. - -`"pop-on"`: Normally used for pre recorded content where. A pop-on screen should -completely replace all previous screen contents. - -`"paint-on"`: Normally used for live content. In this mode, new characters -appear one or two at a time. The `data` array does include the complete state of -the screen. In practice, it may not be different that `"pop-on"`. - -An internal "loading" mode also exists. - -### `roll-up` -Normally used for live content such as news broadcasts. Text is moved up the -screen as new rows appear. The number of rows to be displayed is also encoded in -the JSON key `"roll-up"` where value is an integer of `0`, `1`, `2`, `3`, or `4`. -Like `"paint-on"`, the `data` array does include the complete state of the screen. - -### `data` -Contains a object for every character to be displayed. The screen is a grid of -15 rows and 32 columns. The position of the character is available in the `row` -and `col` fields. The character itself is in the `char` field. The full -character set is available in the document, but any valid UTF-8 should be -supported for future. The style field will contain one of the following values: - -`"white"`, `"green"`, `"blue"`, `"cyan"`, `"red"`, `"yellow"`, `"magenta"`, `"italics"` - -Characters with the `"italics"` style should be displayed in white. +## Limitations +Current B-frame support for caption creation is minimal. libcaption ensures no re-ordering of captions is required +on playback. + +## Build Directions +# Mac Os/Linux +Install build dependencies (git, cmake, a compiler such as xcode, gcc or clang and optionally re2c and ffmpeg) +* run `cmake . && make` +* or to compile without re2c `cmake -DENABLE_RE2C=OFF . && make` +* finally `sudo make install` to install +# Windows +I have never tested libcaption in windows. It is written in pure C with no dependencies, +so there is no reason it would not work. diff --git a/deps/libcaption/caption/caption.h b/deps/libcaption/caption/caption.h index 665ec28f4..ee4d8a78c 100644 --- a/deps/libcaption/caption/caption.h +++ b/deps/libcaption/caption/caption.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,9 +23,13 @@ /**********************************************************************************************/ #ifndef LIBCAPTION_H #define LIBCAPTION_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "eia608.h" #include "utf8.h" #include "xds.h" -#include "eia608.h" // ssize_t is POSIX and does not exist on Windows #if defined(_MSC_VER) @@ -37,16 +41,15 @@ typedef signed int ssize_t; #endif typedef enum { - LIBCAPTION_OK = 1, LIBCAPTION_ERROR = 0, + LIBCAPTION_OK = 1, LIBCAPTION_READY = 2 } libcaption_stauts_t; - -/*! \brief - \param -*/ -static inline libcaption_stauts_t libcaption_status_update (libcaption_stauts_t old_stat, libcaption_stauts_t new_stat) { return (LIBCAPTION_ERROR == old_stat || LIBCAPTION_ERROR == new_stat) ? LIBCAPTION_ERROR : (LIBCAPTION_READY == old_stat) ? LIBCAPTION_READY : new_stat; } +static inline libcaption_stauts_t libcaption_status_update(libcaption_stauts_t old_stat, libcaption_stauts_t new_stat) +{ + return (LIBCAPTION_ERROR == old_stat || LIBCAPTION_ERROR == new_stat) ? LIBCAPTION_ERROR : (LIBCAPTION_READY == old_stat) ? LIBCAPTION_READY : new_stat; +} #define SCREEN_ROWS 15 #define SCREEN_COLS 32 @@ -54,47 +57,54 @@ static inline libcaption_stauts_t libcaption_status_update (libcaption_stauts_t typedef struct { unsigned int uln : 1; //< underline unsigned int sty : 3; //< style - utf8_char_t data[5]; //< 4 byte utf8 values plus null term -} caption_frame_cell_t; + utf8_char_t data[5]; //< 4 byte utf8 values plus null term +} caption_frame_cell_t; typedef struct { caption_frame_cell_t cell[SCREEN_ROWS][SCREEN_COLS]; } caption_frame_buffer_t; - -typedef struct { +typedef struct { unsigned int uln : 1; //< underline unsigned int sty : 3; //< style - unsigned int mod : 3; //< current mode unsigned int rup : 2; //< roll-up line count minus 1 - uint16_t row, col, cc_data; + int8_t row, col; + uint16_t cc_data; } caption_frame_state_t; // timestamp and duration are in seconds typedef struct { double timestamp; - double duration; xds_t xds; caption_frame_state_t state; caption_frame_buffer_t front; caption_frame_buffer_t back; + caption_frame_buffer_t* write; + libcaption_stauts_t status; } caption_frame_t; -// typedef enum { -// eia608_paint_on = 0, -// eia608_pop_on = 1, -// eia608_rollup_2 = 2, -// eia608_rollup_3 = 3, -// eia608_rollup_4 = 4, -// } eia608_display_mode_t; -// eia608_display_mode_t caption_frame_mode (caption_frame_t* frame); - - /*! \brief Initializes an allocated caption_frame_t instance \param frame Pointer to prealocated caption_frame_t object */ -void caption_frame_init (caption_frame_t* frame); +void caption_frame_init(caption_frame_t* frame); +/*! \brief + \param +*/ +static inline int caption_frame_popon(caption_frame_t* frame) { return (frame->write == &frame->back) ? 1 : 0; } +/*! \brief + \param +*/ +static inline int caption_frame_painton(caption_frame_t* frame) { return (frame->write == &frame->front) ? 1 : 0; } +/*! \brief + \param +*/ +const static int _caption_frame_rollup[] = { 0, 2, 3, 4 }; +static inline int caption_frame_rollup(caption_frame_t* frame) { return _caption_frame_rollup[frame->state.rup]; } +/*! \brief + \param +*/ +static inline double caption_frame_timestamp(caption_frame_t* frame) { return frame->timestamp; } /*! \brief Writes a single charcter to a caption_frame_t object \param frame A pointer to an allocted and initialized caption_frame_t object \param row Row position to write charcter, must be between 0 and SCREEN_ROWS-1 @@ -103,39 +113,32 @@ void caption_frame_init (caption_frame_t* frame); \param underline Set underline attribute, 0 = off any other value = on \param c pointer to a single valid utf8 charcter. Bytes are automatically determined, and a NULL terminator is not required */ -int caption_frame_write_char (caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const utf8_char_t* c); +int caption_frame_write_char(caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const utf8_char_t* c); /*! \brief \param */ -const utf8_char_t* caption_frame_read_char (caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline); +const utf8_char_t* caption_frame_read_char(caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline); /*! \brief \param */ - +libcaption_stauts_t caption_frame_decode(caption_frame_t* frame, uint16_t cc_data, double timestamp); /*! \brief \param */ -libcaption_stauts_t caption_frame_decode (caption_frame_t* frame, uint16_t cc_data, double timestamp); - -/*! \brief - \param -*/ -int caption_frame_from_text (caption_frame_t* frame, const utf8_char_t* data); +int caption_frame_from_text(caption_frame_t* frame, const utf8_char_t* data); /*! \brief \param */ -#define CAPTION_FRAME_TEXT_BYTES (((2+SCREEN_ROWS)*SCREEN_COLS*4)+1) -void caption_frame_to_text (caption_frame_t* frame, utf8_char_t* data); +#define CAPTION_FRAME_TEXT_BYTES (4 * ((SCREEN_COLS + 2) * SCREEN_ROWS) + 1) +size_t caption_frame_to_text(caption_frame_t* frame, utf8_char_t* data); /*! \brief \param */ -#define CAPTION_FRAME_DUMP_BUF_SIZE 4096 -size_t caption_frame_dump_buffer (caption_frame_t* frame, utf8_char_t* buf); -void caption_frame_dump (caption_frame_t* frame); -/*! \brief - \param -*/ -#define CAPTION_FRAME_JSON_BUF_SIZE 32768 -size_t caption_frame_json (caption_frame_t* frame, utf8_char_t* buf); +#define CAPTION_FRAME_DUMP_BUF_SIZE 8192 +size_t caption_frame_dump_buffer(caption_frame_t* frame, utf8_char_t* buf); +void caption_frame_dump(caption_frame_t* frame); +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/cea708.h b/deps/libcaption/caption/cea708.h index c2f5bf0f3..c4b35e5c5 100644 --- a/deps/libcaption/caption/cea708.h +++ b/deps/libcaption/caption/cea708.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,6 +23,9 @@ /**********************************************************************************************/ #ifndef LIBCAPTION_CEA708_H #define LIBCAPTION_CEA708_H +#ifdef __cplusplus +extern "C" { +#endif #include "caption.h" #define CEA608_MAX_SIZE (255) @@ -54,15 +57,15 @@ typedef struct { /*! \brief \param */ -cc_data_t cea708_encode_cc_data (int cc_valid, cea708_cc_type_t type, uint16_t cc_data); +cc_data_t cea708_encode_cc_data(int cc_valid, cea708_cc_type_t type, uint16_t cc_data); /*! \brief \param */ -int cea708_cc_count (user_data_t* data); +int cea708_cc_count(user_data_t* data); /*! \brief \param */ -uint16_t cea708_cc_data (user_data_t* data, int index, int* valid, cea708_cc_type_t* type); +uint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_cc_type_t* type); //////////////////////////////////////////////////////////////////////////////// typedef enum { @@ -78,33 +81,44 @@ typedef struct { itu_t_t35_country_code_t country; itu_t_t35_provider_code_t provider; uint32_t user_identifier; - uint8_t atsc1_data_user_data_type_code; + uint8_t user_data_type_code; uint8_t directv_user_data_length; user_data_t user_data; + double timestamp; } cea708_t; +const static uint32_t GA94 = (('G' << 24) | ('A' << 16) | ('9' << 8) | '4'); +const static uint32_t DTG1 = (('D' << 24) | ('T' << 16) | ('G' << 8) | '1'); + +/*! \brief + \param +*/ +int cea708_init(cea708_t* cea708, double timestamp); // will confgure using HLS compatiable defaults /*! \brief \param */ -int cea708_init (cea708_t* cea708); // will confgure using HLS compatiable defaults +libcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708); /*! \brief \param */ -int cea708_parse (uint8_t* data, size_t size, cea708_t* cea708); +libcaption_stauts_t cea708_parse_h262(const uint8_t* data, size_t size, cea708_t* cea708); /*! \brief \param */ -libcaption_stauts_t cea708_to_caption_frame (caption_frame_t* frame, cea708_t* cea708, double pts); +libcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708); /*! \brief \param */ -int cea708_add_cc_data (cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data); +int cea708_add_cc_data(cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data); /*! \brief \param */ -int cea708_render (cea708_t* cea708, uint8_t* data, size_t size); +int cea708_render(cea708_t* cea708, uint8_t* data, size_t size); /*! \brief \param */ -void cea708_dump (cea708_t* cea708); +void cea708_dump(cea708_t* cea708); +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/dvtcc.h b/deps/libcaption/caption/dvtcc.h new file mode 100644 index 000000000..05742777f --- /dev/null +++ b/deps/libcaption/caption/dvtcc.h @@ -0,0 +1,21 @@ +#ifndef LIBCAPTION_CEA708_H +#define LIBCAPTION_CEA708_H +#ifdef __cplusplus +extern "C" { +#endif +//////////////////////////////////////////////////////////////////////////////// +struct dtvcc_packet_t { + unsigned int sequence_number; + unsigned int packet_size; + unsigned int serice_number; +}; + +#defing DVTCC_SERVICE_NUMBER_UNKNOWN + +// static inline size_t dvtvcc_packet_size_bytes(const struct dtvcc_packet_t *dvtcc) { return dvtcc->packet_size*2-1;} + +//////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +} +#endif +#endif diff --git a/deps/libcaption/caption/eia608.h b/deps/libcaption/caption/eia608.h index d0caf3270..668a6fd94 100644 --- a/deps/libcaption/caption/eia608.h +++ b/deps/libcaption/caption/eia608.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,18 +23,20 @@ /**********************************************************************************************/ #ifndef LIBCAPTION_EIA608_H #define LIBCAPTION_EIA608_H +#ifdef __cplusplus +extern "C" { +#endif -#include "utf8.h" #include "eia608_charmap.h" +#include "utf8.h" //////////////////////////////////////////////////////////////////////////////// // Parity -#define EIA608_BX(B,X) (((B)>>X)&0x01) -#define EIA608_BP(B) ((B)&0x7F) | ((EIA608_BX((B),0)^EIA608_BX(B,1)^EIA608_BX((B),2)^EIA608_BX((B),3)^EIA608_BX((B),4)^EIA608_BX((B),5)^EIA608_BX((B),6)^(0x01))<<7) -#define EIA608_B2(B) EIA608_BP((B)+0), EIA608_BP((B)+1), EIA608_BP((B)+2), EIA608_BP((B)+3), EIA608_BP((B)+4), EIA608_BP((B)+5), EIA608_BP((B)+6), EIA608_BP((B)+7) -#define EIA608_B1(B) EIA608_B2((B)+0), EIA608_B2((B)+8), EIA608_B2((B)+16), EIA608_B2((B)+24), EIA608_B2((B)+32), EIA608_B2((B)+40), EIA608_B2((B)+48), EIA608_B2((B)+56) +#define EIA608_BX(B, X) (((B) << (X)) & 0x80) +#define EIA608_BP(B) ((B)&0x7F) | (0x80 ^ EIA608_BX((B), 1) ^ EIA608_BX((B), 2) ^ EIA608_BX((B), 3) ^ EIA608_BX((B), 4) ^ EIA608_BX((B), 5) ^ EIA608_BX((B), 6) ^ EIA608_BX((B), 7)) +#define EIA608_B2(B) EIA608_BP((B) + 0), EIA608_BP((B) + 1), EIA608_BP((B) + 2), EIA608_BP((B) + 3), EIA608_BP((B) + 4), EIA608_BP((B) + 5), EIA608_BP((B) + 6), EIA608_BP((B) + 7) +#define EIA608_B1(B) EIA608_B2((B) + 0), EIA608_B2((B) + 8), EIA608_B2((B) + 16), EIA608_B2((B) + 24), EIA608_B2((B) + 32), EIA608_B2((B) + 40), EIA608_B2((B) + 48), EIA608_B2((B) + 56) -static const uint8_t eia608_parity_table[] = { EIA608_B1 (0), EIA608_B1 (64) }; -extern const char* eia608_mode_map[]; +static const uint8_t eia608_parity_table[] = { EIA608_B1(0), EIA608_B1(64) }; extern const char* eia608_style_map[]; #ifdef _MSC_VER @@ -46,66 +48,66 @@ extern const char* eia608_style_map[]; /*! \brief \param */ -static inline uint8_t eia608_parity_byte (uint8_t cc_data) { return eia608_parity_table[0x7F & cc_data]; } +static inline uint8_t eia608_parity_byte(uint8_t cc_data) { return eia608_parity_table[0x7F & cc_data]; } /*! \brief \param */ -static inline uint16_t eia608_parity_word (uint16_t cc_data) { return (uint16_t) ( (eia608_parity_byte ( (uint8_t) (cc_data>>8)) <<8) | eia608_parity_byte ( (uint8_t) cc_data)); } +static inline uint16_t eia608_parity_word(uint16_t cc_data) { return (uint16_t)((eia608_parity_byte((uint8_t)(cc_data >> 8)) << 8) | eia608_parity_byte((uint8_t)cc_data)); } /*! \brief \param */ -static inline uint16_t eia608_parity (uint16_t cc_data) { return eia608_parity_word (cc_data); } +static inline uint16_t eia608_parity(uint16_t cc_data) { return eia608_parity_word(cc_data); } /*! \brief \param */ -static inline int eia608_parity_varify (uint16_t cc_data) { return eia608_parity_word (cc_data) == cc_data ? 1 : 0; } +static inline int eia608_parity_varify(uint16_t cc_data) { return eia608_parity_word(cc_data) == cc_data ? 1 : 0; } /*! \brief \param */ -static inline int eia608_parity_strip (uint16_t cc_data) { return cc_data & 0x7F7F; } +static inline int eia608_parity_strip(uint16_t cc_data) { return cc_data & 0x7F7F; } /*! \brief \param */ -static inline int eia608_test_second_channel_bit (uint16_t cc_data) { return (cc_data & 0x0800); } +static inline int eia608_test_second_channel_bit(uint16_t cc_data) { return (cc_data & 0x0800); } //////////////////////////////////////////////////////////////////////////////// // cc_data types /*! \brief \param */ -static inline int eia608_is_basicna (uint16_t cc_data) { return 0x0000 != (0x6000 & cc_data); /*&& 0x1F00 < (0x7F00 & cc_data);*/ } +static inline int eia608_is_basicna(uint16_t cc_data) { return 0x0000 != (0x6000 & cc_data); /*&& 0x1F00 < (0x7F00 & cc_data);*/ } /*! \brief \param */ -static inline int eia608_is_preamble (uint16_t cc_data) { return 0x1040 == (0x7040 & cc_data); } +static inline int eia608_is_preamble(uint16_t cc_data) { return 0x1040 == (0x7040 & cc_data); } /*! \brief \param */ -static inline int eia608_is_midrowchange (uint16_t cc_data) { return 0x1120 == (0x7770 & cc_data); } +static inline int eia608_is_midrowchange(uint16_t cc_data) { return 0x1120 == (0x7770 & cc_data); } /*! \brief \param */ -static inline int eia608_is_specialna (uint16_t cc_data) { return 0x1130 == (0x7770 & cc_data); } +static inline int eia608_is_specialna(uint16_t cc_data) { return 0x1130 == (0x7770 & cc_data); } /*! \brief \param */ -static inline int eia608_is_xds (uint16_t cc_data) { return 0x0000 == (0x7070 & cc_data) && 0x0000 != (0x0F0F & cc_data); } +static inline int eia608_is_xds(uint16_t cc_data) { return 0x0000 == (0x7070 & cc_data) && 0x0000 != (0x0F0F & cc_data); } /*! \brief \param */ -static inline int eia608_is_westeu (uint16_t cc_data) { return 0x1220 == (0x7660 & cc_data); } +static inline int eia608_is_westeu(uint16_t cc_data) { return 0x1220 == (0x7660 & cc_data); } /*! \brief \param */ -static inline int eia608_is_control (uint16_t cc_data) { return 0x1420 == (0x7670 & cc_data) || 0x1720 == (0x7770 & cc_data); } +static inline int eia608_is_control(uint16_t cc_data) { return 0x1420 == (0x7670 & cc_data) || 0x1720 == (0x7770 & cc_data); } /*! \brief \param */ -static inline int eia608_is_norpak (uint16_t cc_data) { return 0x1724 == (0x777C & cc_data) || 0x1728 == (0x777C & cc_data); } +static inline int eia608_is_norpak(uint16_t cc_data) { return 0x1724 == (0x777C & cc_data) || 0x1728 == (0x777C & cc_data); } /*! \brief \param */ -static inline int eia608_is_padding (uint16_t cc_data) { return 0x8080 == cc_data; } +static inline int eia608_is_padding(uint16_t cc_data) { return 0x8080 == cc_data; } //////////////////////////////////////////////////////////////////////////////// // preamble @@ -123,23 +125,26 @@ typedef enum { /*! \brief \param */ -int eia608_parse_preamble (uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline); +int eia608_parse_preamble(uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline); /*! \brief \param */ -int eia608_parse_midrowchange (uint16_t cc_data, int* chan, eia608_style_t* style, int* underline); +int eia608_parse_midrowchange(uint16_t cc_data, int* chan, eia608_style_t* style, int* underline); /*! \brief \param */ -uint16_t eia608_row_column_pramble (int row, int col, int chan, int underline); +uint16_t eia608_row_column_pramble(int row, int col, int chan, int underline); /*! \brief \param */ -uint16_t eia608_row_style_pramble (int row, eia608_style_t style, int chan, int underline); - +uint16_t eia608_row_style_pramble(int row, int chan, eia608_style_t style, int underline); +/*! \brief + \param +*/ +uint16_t eia608_midrow_change(int chan, eia608_style_t style, int underline); //////////////////////////////////////////////////////////////////////////////// // control command -typedef enum { // yes, no? +typedef enum { eia608_tab_offset_0 = 0x1720, eia608_tab_offset_1 = 0x1721, eia608_tab_offset_2 = 0x1722, @@ -161,46 +166,43 @@ typedef enum { // yes, no? eia608_control_end_of_caption = 0x142F, } eia608_control_t; -#define eia608_control_popon eia608_control_resume_caption_loading -#define eia608_control_painton eia608_control_resume_direct_captioning - /*! \brief \param */ -uint16_t eia608_control_command (eia608_control_t cmd, int cc); +uint16_t eia608_control_command(eia608_control_t cmd, int cc); /*! \brief \param */ -static inline uint16_t eia608_tab (int size, int cc) { return eia608_control_command ( (eia608_control_t) (eia608_tab_offset_0 | (size&0x0F)),cc); } +static inline uint16_t eia608_tab(int size, int cc) { return eia608_control_command((eia608_control_t)(eia608_tab_offset_0 | (size & 0x0F)), cc); } /*! \brief \param */ -eia608_control_t eia608_parse_control (uint16_t cc_data, int* cc); - +eia608_control_t eia608_parse_control(uint16_t cc_data, int* cc); //////////////////////////////////////////////////////////////////////////////// // text /*! \brief \param c */ -uint16_t eia608_from_utf8_1 (const utf8_char_t* c, int chan); +uint16_t eia608_from_utf8_1(const utf8_char_t* c, int chan); /*! \brief \param */ -uint16_t eia608_from_utf8_2 (const utf8_char_t* c1, const utf8_char_t* c2); +uint16_t eia608_from_utf8_2(const utf8_char_t* c1, const utf8_char_t* c2); /*! \brief \param */ -uint16_t eia608_from_basicna (uint16_t bna1, uint16_t bna2); +uint16_t eia608_from_basicna(uint16_t bna1, uint16_t bna2); /*! \brief \param */ -int eia608_to_utf8 (uint16_t c, int* chan, utf8_char_t* char1, utf8_char_t* char2); +int eia608_to_utf8(uint16_t c, int* chan, utf8_char_t* char1, utf8_char_t* char2); //////////////////////////////////////////////////////////////////////////////// /*! \brief \param */ -void eia608_dump (uint16_t cc_data); +void eia608_dump(uint16_t cc_data); //////////////////////////////////////////////////////////////////////////////// - - +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/eia608_charmap.h b/deps/libcaption/caption/eia608_charmap.h index 261b37acc..37d334606 100644 --- a/deps/libcaption/caption/eia608_charmap.h +++ b/deps/libcaption/caption/eia608_charmap.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,208 +23,214 @@ /**********************************************************************************************/ #ifndef LIBCAPTION_EIA608_CHARMAP_H #define LIBCAPTION_EIA608_CHARMAP_H +#ifdef __cplusplus +extern "C" { +#endif #define EIA608_CHAR_COUNT 176 extern const char* eia608_char_map[EIA608_CHAR_COUNT]; // Helper char -#define EIA608_CHAR_NULL "" +#define EIA608_CHAR_NULL "" // Basic North American character set -#define EIA608_CHAR_SPACE "\x20" -#define EIA608_CHAR_EXCLAMATION_MARK "\x21" -#define EIA608_CHAR_QUOTATION_MARK "\x22" -#define EIA608_CHAR_NUMBER_SIGN "\x23" -#define EIA608_CHAR_DOLLAR_SIGN "\x24" -#define EIA608_CHAR_PERCENT_SIGN "\x25" -#define EIA608_CHAR_AMPERSAND "\x26" -#define EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK "\xE2\x80\x98" -#define EIA608_CHAR_LEFT_PARENTHESIS "\x28" -#define EIA608_CHAR_RIGHT_PARENTHESIS "\x29" -#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE "\xC3\xA1" -#define EIA608_CHAR_PLUS_SIGN "\x2B" -#define EIA608_CHAR_COMMA "\x2C" -#define EIA608_CHAR_HYPHEN_MINUS "\x2D" -#define EIA608_CHAR_FULL_STOP "\x2E" -#define EIA608_CHAR_SOLIDUS "\x2F" +#define EIA608_CHAR_SPACE "\x20" +#define EIA608_CHAR_EXCLAMATION_MARK "\x21" +#define EIA608_CHAR_QUOTATION_MARK "\x22" +#define EIA608_CHAR_NUMBER_SIGN "\x23" +#define EIA608_CHAR_DOLLAR_SIGN "\x24" +#define EIA608_CHAR_PERCENT_SIGN "\x25" +#define EIA608_CHAR_AMPERSAND "\x26" +#define EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK "\xE2\x80\x98" +#define EIA608_CHAR_LEFT_PARENTHESIS "\x28" +#define EIA608_CHAR_RIGHT_PARENTHESIS "\x29" +#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE "\xC3\xA1" +#define EIA608_CHAR_PLUS_SIGN "\x2B" +#define EIA608_CHAR_COMMA "\x2C" +#define EIA608_CHAR_HYPHEN_MINUS "\x2D" +#define EIA608_CHAR_FULL_STOP "\x2E" +#define EIA608_CHAR_SOLIDUS "\x2F" // Basic North American character set -#define EIA608_CHAR_DIGIT_ZERO "\x30" -#define EIA608_CHAR_DIGIT_ONE "\x31" -#define EIA608_CHAR_DIGIT_TWO "\x32" -#define EIA608_CHAR_DIGIT_THREE "\x33" -#define EIA608_CHAR_DIGIT_FOUR "\x34" -#define EIA608_CHAR_DIGIT_FIVE "\x35" -#define EIA608_CHAR_DIGIT_SIX "\x36" -#define EIA608_CHAR_DIGIT_SEVEN "\x37" -#define EIA608_CHAR_DIGIT_EIGHT "\x38" -#define EIA608_CHAR_DIGIT_NINE "\x39" -#define EIA608_CHAR_COLON "\x3A" -#define EIA608_CHAR_SEMICOLON "\x3B" -#define EIA608_CHAR_LESS_THAN_SIGN "\x3C" -#define EIA608_CHAR_EQUALS_SIGN "\x3D" -#define EIA608_CHAR_GREATER_THAN_SIGN "\x3E" -#define EIA608_CHAR_QUESTION_MARK "\x3F" +#define EIA608_CHAR_DIGIT_ZERO "\x30" +#define EIA608_CHAR_DIGIT_ONE "\x31" +#define EIA608_CHAR_DIGIT_TWO "\x32" +#define EIA608_CHAR_DIGIT_THREE "\x33" +#define EIA608_CHAR_DIGIT_FOUR "\x34" +#define EIA608_CHAR_DIGIT_FIVE "\x35" +#define EIA608_CHAR_DIGIT_SIX "\x36" +#define EIA608_CHAR_DIGIT_SEVEN "\x37" +#define EIA608_CHAR_DIGIT_EIGHT "\x38" +#define EIA608_CHAR_DIGIT_NINE "\x39" +#define EIA608_CHAR_COLON "\x3A" +#define EIA608_CHAR_SEMICOLON "\x3B" +#define EIA608_CHAR_LESS_THAN_SIGN "\x3C" +#define EIA608_CHAR_EQUALS_SIGN "\x3D" +#define EIA608_CHAR_GREATER_THAN_SIGN "\x3E" +#define EIA608_CHAR_QUESTION_MARK "\x3F" // Basic North American character set -#define EIA608_CHAR_COMMERCIAL_AT "\x40" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A "\x41" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_B "\x42" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_C "\x43" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_D "\x44" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E "\x45" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_F "\x46" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_G "\x47" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_H "\x48" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I "\x49" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_J "\x4A" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_K "\x4B" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_L "\x4C" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_M "\x4D" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_N "\x4E" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O "\x4F" +#define EIA608_CHAR_COMMERCIAL_AT "\x40" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A "\x41" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_B "\x42" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_C "\x43" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_D "\x44" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E "\x45" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_F "\x46" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_G "\x47" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_H "\x48" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I "\x49" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_J "\x4A" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_K "\x4B" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_L "\x4C" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_M "\x4D" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_N "\x4E" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O "\x4F" // Basic North American character set -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_P "\x50" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Q "\x51" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_R "\x52" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_S "\x53" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_T "\x54" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U "\x55" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_V "\x56" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_W "\x57" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_X "\x58" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Y "\x59" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Z "\x5A" -#define EIA608_CHAR_LEFT_SQUARE_BRACKET "\x5B" -#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE "\xC3\xA9" -#define EIA608_CHAR_RIGHT_SQUARE_BRACKET "\x5D" -#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE "\xC3\xAD" -#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE "\xC3\xB3" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_P "\x50" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Q "\x51" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_R "\x52" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_S "\x53" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_T "\x54" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U "\x55" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_V "\x56" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_W "\x57" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_X "\x58" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Y "\x59" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Z "\x5A" +#define EIA608_CHAR_LEFT_SQUARE_BRACKET "\x5B" +#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE "\xC3\xA9" +#define EIA608_CHAR_RIGHT_SQUARE_BRACKET "\x5D" +#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE "\xC3\xAD" +#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE "\xC3\xB3" // Basic North American character set -#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE "\xC3\xBA" -#define EIA608_CHAR_LATIN_SMALL_LETTER_A "\x61" -#define EIA608_CHAR_LATIN_SMALL_LETTER_B "\x62" -#define EIA608_CHAR_LATIN_SMALL_LETTER_C "\x63" -#define EIA608_CHAR_LATIN_SMALL_LETTER_D "\x64" -#define EIA608_CHAR_LATIN_SMALL_LETTER_E "\x65" -#define EIA608_CHAR_LATIN_SMALL_LETTER_F "\x66" -#define EIA608_CHAR_LATIN_SMALL_LETTER_G "\x67" -#define EIA608_CHAR_LATIN_SMALL_LETTER_H "\x68" -#define EIA608_CHAR_LATIN_SMALL_LETTER_I "\x69" -#define EIA608_CHAR_LATIN_SMALL_LETTER_J "\x6A" -#define EIA608_CHAR_LATIN_SMALL_LETTER_K "\x6B" -#define EIA608_CHAR_LATIN_SMALL_LETTER_L "\x6C" -#define EIA608_CHAR_LATIN_SMALL_LETTER_M "\x6D" -#define EIA608_CHAR_LATIN_SMALL_LETTER_N "\x6E" -#define EIA608_CHAR_LATIN_SMALL_LETTER_O "\x6F" +#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE "\xC3\xBA" +#define EIA608_CHAR_LATIN_SMALL_LETTER_A "\x61" +#define EIA608_CHAR_LATIN_SMALL_LETTER_B "\x62" +#define EIA608_CHAR_LATIN_SMALL_LETTER_C "\x63" +#define EIA608_CHAR_LATIN_SMALL_LETTER_D "\x64" +#define EIA608_CHAR_LATIN_SMALL_LETTER_E "\x65" +#define EIA608_CHAR_LATIN_SMALL_LETTER_F "\x66" +#define EIA608_CHAR_LATIN_SMALL_LETTER_G "\x67" +#define EIA608_CHAR_LATIN_SMALL_LETTER_H "\x68" +#define EIA608_CHAR_LATIN_SMALL_LETTER_I "\x69" +#define EIA608_CHAR_LATIN_SMALL_LETTER_J "\x6A" +#define EIA608_CHAR_LATIN_SMALL_LETTER_K "\x6B" +#define EIA608_CHAR_LATIN_SMALL_LETTER_L "\x6C" +#define EIA608_CHAR_LATIN_SMALL_LETTER_M "\x6D" +#define EIA608_CHAR_LATIN_SMALL_LETTER_N "\x6E" +#define EIA608_CHAR_LATIN_SMALL_LETTER_O "\x6F" // Basic North American character set -#define EIA608_CHAR_LATIN_SMALL_LETTER_P "\x70" -#define EIA608_CHAR_LATIN_SMALL_LETTER_Q "\x71" -#define EIA608_CHAR_LATIN_SMALL_LETTER_R "\x72" -#define EIA608_CHAR_LATIN_SMALL_LETTER_S "\x73" -#define EIA608_CHAR_LATIN_SMALL_LETTER_T "\x74" -#define EIA608_CHAR_LATIN_SMALL_LETTER_U "\x75" -#define EIA608_CHAR_LATIN_SMALL_LETTER_V "\x76" -#define EIA608_CHAR_LATIN_SMALL_LETTER_W "\x77" -#define EIA608_CHAR_LATIN_SMALL_LETTER_X "\x78" -#define EIA608_CHAR_LATIN_SMALL_LETTER_Y "\x79" -#define EIA608_CHAR_LATIN_SMALL_LETTER_Z "\x7A" -#define EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA "\xC3\xA7" -#define EIA608_CHAR_DIVISION_SIGN "\xC3\xB7" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE "\xC3\x91" -#define EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE "\xC3\xB1" -#define EIA608_CHAR_FULL_BLOCK "\xE2\x96\x88" +#define EIA608_CHAR_LATIN_SMALL_LETTER_P "\x70" +#define EIA608_CHAR_LATIN_SMALL_LETTER_Q "\x71" +#define EIA608_CHAR_LATIN_SMALL_LETTER_R "\x72" +#define EIA608_CHAR_LATIN_SMALL_LETTER_S "\x73" +#define EIA608_CHAR_LATIN_SMALL_LETTER_T "\x74" +#define EIA608_CHAR_LATIN_SMALL_LETTER_U "\x75" +#define EIA608_CHAR_LATIN_SMALL_LETTER_V "\x76" +#define EIA608_CHAR_LATIN_SMALL_LETTER_W "\x77" +#define EIA608_CHAR_LATIN_SMALL_LETTER_X "\x78" +#define EIA608_CHAR_LATIN_SMALL_LETTER_Y "\x79" +#define EIA608_CHAR_LATIN_SMALL_LETTER_Z "\x7A" +#define EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA "\xC3\xA7" +#define EIA608_CHAR_DIVISION_SIGN "\xC3\xB7" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE "\xC3\x91" +#define EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE "\xC3\xB1" +#define EIA608_CHAR_FULL_BLOCK "\xE2\x96\x88" // Special North American character set[edit] -#define EIA608_CHAR_REGISTERED_SIGN "\xC2\xAE" -#define EIA608_CHAR_DEGREE_SIGN "\xC2\xB0" -#define EIA608_CHAR_VULGAR_FRACTION_ONE_HALF "\xC2\xBD" -#define EIA608_CHAR_INVERTED_QUESTION_MARK "\xC2\xBF" -#define EIA608_CHAR_TRADE_MARK_SIGN "\xE2\x84\xA2" -#define EIA608_CHAR_CENT_SIGN "\xC2\xA2" -#define EIA608_CHAR_POUND_SIGN "\xC2\xA3" -#define EIA608_CHAR_EIGHTH_NOTE "\xE2\x99\xAA" -#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE "\xC3\xA0" -#define EIA608_CHAR_NO_BREAK_SPACE "\xC2\xA0" -#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE "\xC3\xA8" -#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX "\xC3\xA2" -#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX "\xC3\xAA" -#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX "\xC3\xAE" -#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX "\xC3\xB4" -#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX "\xC3\xBB" +#define EIA608_CHAR_REGISTERED_SIGN "\xC2\xAE" +#define EIA608_CHAR_DEGREE_SIGN "\xC2\xB0" +#define EIA608_CHAR_VULGAR_FRACTION_ONE_HALF "\xC2\xBD" +#define EIA608_CHAR_INVERTED_QUESTION_MARK "\xC2\xBF" +#define EIA608_CHAR_TRADE_MARK_SIGN "\xE2\x84\xA2" +#define EIA608_CHAR_CENT_SIGN "\xC2\xA2" +#define EIA608_CHAR_POUND_SIGN "\xC2\xA3" +#define EIA608_CHAR_EIGHTH_NOTE "\xE2\x99\xAA" +#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE "\xC3\xA0" +#define EIA608_CHAR_NO_BREAK_SPACE "\xC2\xA0" +#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE "\xC3\xA8" +#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX "\xC3\xA2" +#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX "\xC3\xAA" +#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX "\xC3\xAE" +#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX "\xC3\xB4" +#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX "\xC3\xBB" // Extended Western European character set : Extended Spanish/Miscellaneous -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE "\xC3\x81" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE "\xC3\x89" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE "\xC3\x93" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE "\xC3\x9A" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS "\xC3\x9C" -#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS "\xC3\xBC" -#define EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK "\xE2\x80\x99" -#define EIA608_CHAR_INVERTED_EXCLAMATION_MARK "\xC2\xA1" -#define EIA608_CHAR_ASTERISK "\x2A" -#define EIA608_CHAR_APOSTROPHE "\x27" -#define EIA608_CHAR_EM_DASH "\xE2\x80\x94" -#define EIA608_CHAR_COPYRIGHT_SIGN "\xC2\xA9" -#define EIA608_CHAR_SERVICE_MARK "\xE2\x84\xA0" -#define EIA608_CHAR_BULLET "\xE2\x80\xA2" -#define EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK "\xE2\x80\x9C" -#define EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK "\xE2\x80\x9D" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE "\xC3\x81" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE "\xC3\x89" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE "\xC3\x93" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE "\xC3\x9A" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS "\xC3\x9C" +#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS "\xC3\xBC" +#define EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK "\xE2\x80\x99" +#define EIA608_CHAR_INVERTED_EXCLAMATION_MARK "\xC2\xA1" +#define EIA608_CHAR_ASTERISK "\x2A" +#define EIA608_CHAR_APOSTROPHE "\x27" +#define EIA608_CHAR_EM_DASH "\xE2\x80\x94" +#define EIA608_CHAR_COPYRIGHT_SIGN "\xC2\xA9" +#define EIA608_CHAR_SERVICE_MARK "\xE2\x84\xA0" +#define EIA608_CHAR_BULLET "\xE2\x80\xA2" +#define EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK "\xE2\x80\x9C" +#define EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK "\xE2\x80\x9D" // Extended Western European character set : Extended French -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE "\xC3\x80" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX "\xC3\x82" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA "\xC3\x87" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE "\xC3\x88" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX "\xC3\x8A" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS "\xC3\x8B" -#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS "\xC3\xAB" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX "\xC3\x8E" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS "\xC3\x8F" -#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS "\xC3\xAF" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX "\xC3\x94" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE "\xC3\x99" -#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE "\xC3\xB9" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX "\xC3\x9B" -#define EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK "\xC2\xAB" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE "\xC3\x80" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX "\xC3\x82" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA "\xC3\x87" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE "\xC3\x88" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX "\xC3\x8A" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS "\xC3\x8B" +#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS "\xC3\xAB" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX "\xC3\x8E" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS "\xC3\x8F" +#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS "\xC3\xAF" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX "\xC3\x94" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE "\xC3\x99" +#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE "\xC3\xB9" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX "\xC3\x9B" +#define EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK "\xC2\xAB" #define EIA608_CHAR_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK "\xC2\xBB" // Extended Western European character set : Portuguese -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE "\xC3\x83" -#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE "\xC3\xA3" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE "\xC3\x8D" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE "\xC3\x8C" -#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE "\xC3\xAC" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE "\xC3\x92" -#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE "\xC3\xB2" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE "\xC3\x95" -#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE "\xC3\xB5" -#define EIA608_CHAR_LEFT_CURLY_BRACKET "\x7B" -#define EIA608_CHAR_RIGHT_CURLY_BRACKET "\x7D" -#define EIA608_CHAR_REVERSE_SOLIDUS "\x5C" -#define EIA608_CHAR_CIRCUMFLEX_ACCENT "\x5E" -#define EIA608_CHAR_LOW_LINE "\x5F" -#define EIA608_CHAR_VERTICAL_LINE "\x7C" -#define EIA608_CHAR_TILDE "\x7E" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE "\xC3\x83" +#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE "\xC3\xA3" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE "\xC3\x8D" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE "\xC3\x8C" +#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE "\xC3\xAC" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE "\xC3\x92" +#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE "\xC3\xB2" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE "\xC3\x95" +#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE "\xC3\xB5" +#define EIA608_CHAR_LEFT_CURLY_BRACKET "\x7B" +#define EIA608_CHAR_RIGHT_CURLY_BRACKET "\x7D" +#define EIA608_CHAR_REVERSE_SOLIDUS "\x5C" +#define EIA608_CHAR_CIRCUMFLEX_ACCENT "\x5E" +#define EIA608_CHAR_LOW_LINE "\x5F" +#define EIA608_CHAR_VERTICAL_LINE "\x7C" +#define EIA608_CHAR_TILDE "\x7E" // Extended Western European character set : German/Danish -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS "\xC3\x84" -#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS "\xC3\xA4" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS "\xC3\x96" -#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS "\xC3\xB6" -#define EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S "\xC3\x9F" -#define EIA608_CHAR_YEN_SIGN "\xC2\xA5" -#define EIA608_CHAR_CURRENCY_SIGN "\xC2\xA4" -#define EIA608_CHAR_BROKEN_BAR "\xC2\xA6" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE "\xC3\x85" -#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE "\xC3\xA5" -#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE "\xC3\x98" -#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE "\xC3\xB8" -#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT "\xE2\x94\x8C" // top left -#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT "\xE2\x94\x90" // top right -#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT "\xE2\x94\x94" // lower left -#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT "\xE2\x94\x98" // bottom right +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS "\xC3\x84" +#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS "\xC3\xA4" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS "\xC3\x96" +#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS "\xC3\xB6" +#define EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S "\xC3\x9F" +#define EIA608_CHAR_YEN_SIGN "\xC2\xA5" +#define EIA608_CHAR_CURRENCY_SIGN "\xC2\xA4" +#define EIA608_CHAR_BROKEN_BAR "\xC2\xA6" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE "\xC3\x85" +#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE "\xC3\xA5" +#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE "\xC3\x98" +#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE "\xC3\xB8" +#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT "\xE2\x94\x8C" // top left +#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT "\xE2\x94\x90" // top right +#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT "\xE2\x94\x94" // lower left +#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT "\xE2\x94\x98" // bottom right +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/avc.h b/deps/libcaption/caption/mpeg.h similarity index 61% rename from deps/libcaption/caption/avc.h rename to deps/libcaption/caption/mpeg.h index d4d4ed71b..699a29c6e 100644 --- a/deps/libcaption/caption/avc.h +++ b/deps/libcaption/caption/mpeg.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -21,26 +21,56 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#ifndef LIBCAPTION_AVC_H -#define LIBCAPTION_AVC_H -#include "cea708.h" +#ifndef LIBCAPTION_MPEG_H +#define LIBCAPTION_MPEG_H +#ifdef __cplusplus +extern "C" { +#endif + #include "caption.h" +#include "cea708.h" +#include "scc.h" #include +#include //////////////////////////////////////////////////////////////////////////////// -#define MAX_NALU_SIZE (4*1024*1024) +#define STREAM_TYPE_H262 0x02 +#define STREAM_TYPE_H264 0x1B +#define STREAM_TYPE_H265 0x24 +#define H262_SEI_PACKET 0xB2 +#define H264_SEI_PACKET 0x06 +#define H265_SEI_PACKET 0x27 // There is also 0x28 +#define MAX_NALU_SIZE (6 * 1024 * 1024) +#define MAX_REFRENCE_FRAMES 64 typedef struct { size_t size; - uint8_t data[MAX_NALU_SIZE]; -} avcnalu_t; + uint8_t data[MAX_NALU_SIZE + 1]; + double dts, cts; + libcaption_stauts_t status; + // Priority queue for out of order frame processing + // Should probablly be a linked list + size_t front; + size_t latent; + cea708_t cea708[MAX_REFRENCE_FRAMES]; +} mpeg_bitstream_t; -void avcnalu_init (avcnalu_t* nalu); -int avcnalu_parse_annexb (avcnalu_t* nalu, const uint8_t** data, size_t* size); -static inline uint8_t avcnalu_type (avcnalu_t* nalu) { return nalu->data[0] & 0x1F; } -static inline uint8_t* avcnalu_data (avcnalu_t* nalu) { return &nalu->data[0]; } -static inline size_t avcnalu_size (avcnalu_t* nalu) { return nalu->size; } +void mpeg_bitstream_init(mpeg_bitstream_t* packet); +//////////////////////////////////////////////////////////////////////////////// +// TODO make convenience functions for flv/mp4 +/*! \brief + \param +*/ +size_t mpeg_bitstream_parse(mpeg_bitstream_t* packet, caption_frame_t* frame, const uint8_t* data, size_t size, unsigned stream_type, double dts, double cts); +/*! \brief + \param +*/ +static inline libcaption_stauts_t mpeg_bitstream_status(mpeg_bitstream_t* packet) { return packet->status; } +/*! \brief + Flushes latent packets caused by out or order frames. + Returns number of latent frames remaining, 0 when complete; + \param +*/ +size_t mpeg_bitstream_flush(mpeg_bitstream_t* packet, caption_frame_t* frame); //////////////////////////////////////////////////////////////////////////////// -typedef struct _sei_message_t sei_message_t; - typedef enum { sei_type_buffering_period = 0, sei_type_pic_timing = 1, @@ -66,10 +96,14 @@ typedef enum { sei_type_stereo_video_info = 21, } sei_msgtype_t; //////////////////////////////////////////////////////////////////////////////// -// time in seconds -typedef struct { - double dts; - double cts; +typedef struct _sei_message_t { + size_t size; + sei_msgtype_t type; + struct _sei_message_t* next; +} sei_message_t; + +typedef struct { + double timestamp; sei_message_t* head; sei_message_t* tail; } sei_t; @@ -77,64 +111,57 @@ typedef struct { /*! \brief \param */ -void sei_init (sei_t* sei); -/*! \brief - \param -*/ -void sei_free (sei_t* sei); +void sei_init(sei_t* sei, double timestamp); /*! \brief \param */ -static inline double sei_dts (sei_t* sei) { return sei->dts; } -static inline double sei_cts (sei_t* sei) { return sei->cts; } -static inline double sei_pts (sei_t* sei) { return sei->dts + sei->cts; } +void sei_free(sei_t* sei); /*! \brief \param */ -int sei_parse_nalu (sei_t* sei, const uint8_t* data, size_t size, double dts, double cts); +void sei_cat(sei_t* to, sei_t* from, int itu_t_t35); /*! \brief \param */ -// TODO add dts,cts to nalu -static inline int sei_parse_avcnalu (sei_t* sei, avcnalu_t* nalu, double dts, double cts) { return sei_parse_nalu (sei,avcnalu_data (nalu),avcnalu_size (nalu),dts,cts); } +void sei_message_append(sei_t* sei, sei_message_t* msg); /*! \brief \param */ -static inline int sei_finish (sei_t* sei) { return sei_parse_nalu (sei,0,0,0.0,DBL_MAX); } +libcaption_stauts_t sei_parse(sei_t* sei, const uint8_t* data, size_t size, double timestamp); /*! \brief \param */ -static inline sei_message_t* sei_message_head (sei_t* sei) { return sei->head; } +static inline sei_message_t* sei_message_head(sei_t* sei) { return sei->head; } /*! \brief \param */ -static inline sei_message_t* sei_message_tail (sei_t* sei) { return sei->tail; } +static inline sei_message_t* sei_message_tail(sei_t* sei) { return sei->tail; } /*! \brief \param */ -sei_message_t* sei_message_next (sei_message_t* msg); +sei_message_t* sei_message_next(sei_message_t* msg); /*! \brief \param */ -sei_msgtype_t sei_message_type (sei_message_t* msg); +sei_msgtype_t sei_message_type(sei_message_t* msg); /*! \brief \param */ -size_t sei_message_size (sei_message_t* msg); +size_t sei_message_size(sei_message_t* msg); /*! \brief \param */ -uint8_t* sei_message_data (sei_message_t* msg); +uint8_t* sei_message_data(sei_message_t* msg); /*! \brief \param */ -sei_message_t* sei_message_new (sei_msgtype_t type, uint8_t* data, size_t size); +sei_message_t* sei_message_new(sei_msgtype_t type, uint8_t* data, size_t size); /*! \brief \param */ -static inline sei_message_t* sei_message_copy (sei_message_t* msg) +static inline sei_message_t* sei_message_copy(sei_message_t* msg) { - return sei_message_new (sei_message_type (msg), sei_message_data (msg), sei_message_size (msg)); + return sei_message_new(sei_message_type(msg), sei_message_data(msg), sei_message_size(msg)); } /** Free message and all accoiated data. Messaged added to sei_t by using sei_append_message MUST NOT be freed @@ -143,56 +170,43 @@ These messages will be freed by calling sei_free() /*! \brief \param */ -void sei_message_free (sei_message_t* msg); +void sei_message_free(sei_message_t* msg); //////////////////////////////////////////////////////////////////////////////// /*! \brief \param */ -static inline int sei_decode_cea708 (sei_message_t* msg, cea708_t* cea708) -{ - if (sei_type_user_data_registered_itu_t_t35 == sei_message_type (msg)) { - return cea708_parse (sei_message_data (msg), sei_message_size (msg), cea708); - } else { - return 0; - } -} -//////////////////////////////////////////////////////////////////////////////// +size_t sei_render_size(sei_t* sei); /*! \brief \param */ -size_t sei_render_size (sei_t* sei); +size_t sei_render(sei_t* sei, uint8_t* data); /*! \brief \param */ -size_t sei_render (sei_t* sei, uint8_t* data); +void sei_dump(sei_t* sei); /*! \brief \param */ -void sei_dump (sei_t* sei); +void sei_dump_messages(sei_message_t* head, double timestamp); +//////////////////////////////////////////////////////////////////////////////// /*! \brief \param */ -void sei_dump_messages (sei_message_t* head); -//////////////////////////////////////////////////////////////////////////////// +libcaption_stauts_t sei_from_scc(sei_t* sei, const scc_t* scc); /*! \brief \param */ -int sei_from_caption_frame (sei_t* sei, caption_frame_t* frame); +libcaption_stauts_t sei_from_caption_frame(sei_t* sei, caption_frame_t* frame); /*! \brief \param */ -libcaption_stauts_t sei_to_caption_frame (sei_t* sei, caption_frame_t* frame); +libcaption_stauts_t sei_from_caption_clear(sei_t* sei); /*! \brief \param */ -static inline int nalu_to_caption_frame (caption_frame_t* frame, const uint8_t* data, size_t size, double pts, double dts) -{ - sei_t sei; - sei_init (&sei); - sei_parse_nalu (&sei, data, size, pts, dts); - sei_to_caption_frame (&sei,frame); - sei_free (&sei); - return 1; -} +libcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame); //////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/scc.h b/deps/libcaption/caption/scc.h index dd4d6dcbe..85ec0ce17 100644 --- a/deps/libcaption/caption/scc.h +++ b/deps/libcaption/caption/scc.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,9 +23,25 @@ /**********************************************************************************************/ #ifndef LIBCAPTION_SCC_H #define LIBCAPTION_SCC_H +#ifdef __cplusplus +extern "C" { +#endif #include "eia608.h" -int scc_to_608 (const char* line, double* pts, uint16_t* cc, int cc_max); +typedef struct _scc_t { + double timestamp; + unsigned int cc_aloc; + unsigned int cc_size; + uint16_t cc_data[]; +} scc_t; + +scc_t* scc_new(int cc_count); +scc_t* scc_free(scc_t* scc); +size_t scc_to_608(scc_t** scc, const utf8_char_t* data); + +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/srt.h b/deps/libcaption/caption/srt.h index c7c156554..6c4c43776 100644 --- a/deps/libcaption/caption/srt.h +++ b/deps/libcaption/caption/srt.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,65 +23,73 @@ /**********************************************************************************************/ #ifndef LIBCAPTION_SRT_H #define LIBCAPTION_SRT_H +#ifdef __cplusplus +extern "C" { +#endif -#include "eia608.h" #include "caption.h" +#include "eia608.h" +#include "vtt.h" // timestamp and duration are in seconds -typedef struct _srt_t { - struct _srt_t* next; - double timestamp; - double duration; - size_t aloc; -} srt_t; - - - +typedef vtt_t srt_t; +typedef vtt_block_t srt_cue_t; /*! \brief \param */ -srt_t* srt_new (const utf8_char_t* data, size_t size, double timestamp, srt_t* prev, srt_t** head); +srt_t* srt_new(); /*! \brief \param */ -srt_t* srt_free_head (srt_t* head); +srt_t* srt_free_head(srt_t* head); // returns the head of the link list. must bee freed when done /*! \brief \param */ -srt_t* srt_parse (const utf8_char_t* data, size_t size); +srt_t* srt_parse(const utf8_char_t* data, size_t size); /*! \brief \param */ -void srt_free (srt_t* srt); +void srt_free(srt_t* srt); /*! \brief \param */ -static inline srt_t* srt_next (srt_t* srt) { return srt->next; } +static inline vtt_block_t* srt_next(vtt_block_t* srt) { return srt->next; } + /*! \brief \param */ -static inline utf8_char_t* srt_data (srt_t* srt) { return (utf8_char_t*) (srt) + sizeof (srt_t); } -// This only converts teh surrent SRT, It does not walk the list +static inline utf8_char_t* srt_cue_data(srt_cue_t* cue) { return vtt_block_data(cue); } + /*! \brief \param */ -int srt_to_caption_frame (srt_t* srt, caption_frame_t* frame); +static inline srt_cue_t* srt_cue_from_caption_frame(caption_frame_t* frame, srt_t* srt) { return vtt_cue_from_caption_frame(frame, srt); }; -// returns teh new srt. Head is not tracher internally. /*! \brief \param */ -srt_t* srt_from_caption_frame (caption_frame_t* frame, srt_t* prev, srt_t** head); +static inline void srt_cue_free_head(srt_t* srt) { vtt_cue_free_head(srt); }; + /*! \brief \param */ -void srt_dump (srt_t* srt); +static inline srt_cue_t* srt_cue_new(srt_t* srt, const utf8_char_t* data, size_t size) { return vtt_block_new(srt, data, size, VTT_CUE); }; + /*! \brief \param */ -void vtt_dump (srt_t* srt); +static inline int srt_cue_to_caption_frame(srt_cue_t* cue, caption_frame_t* frame) { return vtt_cue_to_caption_frame(cue, frame); }; +void srt_dump(srt_t* srt); +/*! \brief + \param +*/ +void vtt_dump(srt_t* srt); + +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/utf8.h b/deps/libcaption/caption/utf8.h index 576f47abf..3a17d75ab 100644 --- a/deps/libcaption/caption/utf8.h +++ b/deps/libcaption/caption/utf8.h @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,9 +23,12 @@ /**********************************************************************************************/ #ifndef LIBCAPTION_UTF8_H #define LIBCAPTION_UTF8_H +#ifdef __cplusplus +extern "C" { +#endif -#include #include +#include // These types exist to make the code more self dcoumenting // utf8_char_t point is a null teminate string of utf8 encodecd chars @@ -39,13 +42,21 @@ typedef size_t utf8_size_t; Skiped continuation bytes */ -const utf8_char_t* utf8_char_next (const char* s); + +const utf8_char_t* utf8_char_next(const utf8_char_t* c); /*! \brief \param returnes the length of the char in bytes */ -size_t utf8_char_length (const utf8_char_t* c); +size_t utf8_char_length(const utf8_char_t* c); + +/*! \brief + \param + + returns 1 if first charcter is white space +*/ +int utf8_char_whitespace(const utf8_char_t* c); /*! \brief \param @@ -53,11 +64,11 @@ size_t utf8_char_length (const utf8_char_t* c); returns length of the string in bytes size is number of charcter to count (0 to count until NULL term) */ -size_t utf8_string_length (const utf8_char_t* data, utf8_size_t size); +size_t utf8_string_length(const utf8_char_t* data, utf8_size_t size); /*! \brief \param */ -size_t utf8_char_copy (utf8_char_t* dst, const utf8_char_t* src); +size_t utf8_char_copy(utf8_char_t* dst, const utf8_char_t* src); /*! \brief \param @@ -65,33 +76,53 @@ size_t utf8_char_copy (utf8_char_t* dst, const utf8_char_t* src); returnes the number of utf8 charcters in a string givne the numbe of bytes to coutn until the a null terminator, pass 0 for size */ -utf8_size_t utf8_char_count (const char* data, size_t size); +utf8_size_t utf8_char_count(const char* data, size_t size); /*! \brief \param - returnes the length of the line in bytes triming not printable charcters at the end + returnes the length of the line in bytes triming not printable characters at the end */ -size_t utf8_trimmed_length (const char* data, size_t size); +utf8_size_t utf8_trimmed_length(const utf8_char_t* data, utf8_size_t charcters); /*! \brief \param returns the length in bytes of the line including the new line charcter(s) auto detects between windows(CRLF), unix(LF), mac(CR) and riscos (LFCR) line endings */ -size_t utf8_line_length (const char* data); +size_t utf8_line_length(const utf8_char_t* data); /*! \brief \param returns number of chars to include before split */ -utf8_size_t utf8_wrap_length (const utf8_char_t* data, utf8_size_t size); +utf8_size_t utf8_wrap_length(const utf8_char_t* data, utf8_size_t size); /*! \brief \param + returns number of new lines in the string +*/ +int utf8_line_count(const utf8_char_t* data); + +/*! \brief + \param + size in/out. In the the max seize, out is the size read; returns number of new lins in teh string */ -int utf8_line_count (const utf8_char_t* data); +#define UFTF_DEFAULT_MAX_FILE_SIZE = (50 * 1024 * 1024); + +utf8_char_t* utf8_load_text_file(const char* path, size_t* size); + +/*! \brief + \param + Compares 2 strings up to max len +*/ +#ifndef strnstr +char* strnstr(const char* string1, const char* string2, size_t len); +#endif +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/caption/vtt.h b/deps/libcaption/caption/vtt.h new file mode 100644 index 000000000..4130054c7 --- /dev/null +++ b/deps/libcaption/caption/vtt.h @@ -0,0 +1,145 @@ +/**********************************************************************************************/ +/* The MIT License */ +/* */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ +/* THE SOFTWARE. */ +/**********************************************************************************************/ +#ifndef LIBCAPTION_VTT_H +#define LIBCAPTION_VTT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "caption.h" +#include "eia608.h" + +enum VTT_BLOCK_TYPE { + VTT_REGION = 0, + VTT_STYLE = 1, + VTT_NOTE = 2, + VTT_CUE = 3 +}; + +// CUE represents a block of caption text +typedef struct _vtt_block_t { + struct _vtt_block_t* next; + enum VTT_BLOCK_TYPE type; + // CUE-Only + double timestamp; + double duration; // -1.0 for no duration + char* cue_settings; + char* cue_id; + // Standard block data + size_t text_size; + char* block_text; +} vtt_block_t; + +// VTT files are a collection of REGION, STYLE and CUE blocks. +// XXX: Comments (NOTE blocks) are ignored +typedef struct _vtt_t { + vtt_block_t* region_head; + vtt_block_t* region_tail; + vtt_block_t* style_head; + vtt_block_t* style_tail; + vtt_block_t* cue_head; + vtt_block_t* cue_tail; +} vtt_t; + +/*! \brief + \param +*/ +vtt_t* vtt_new(); +/*! \brief + \param +*/ +void vtt_free(vtt_t* vtt); + +/*! \brief + \param +*/ +vtt_block_t* vtt_block_new(vtt_t* vtt, const utf8_char_t* data, size_t size, enum VTT_BLOCK_TYPE type); + +/*! \brief + \param +*/ +void vtt_cue_free_head(vtt_t* vtt); + +/*! \brief + \param +*/ +void vtt_style_free_head(vtt_t* vtt); + +/*! \brief + \param +*/ +void vtt_region_free_head(vtt_t* vtt); + +// returns a vtt_t, containing linked lists of blocks. must be freed when done +/*! \brief + \param +*/ +vtt_t* vtt_parse(const utf8_char_t* data, size_t size); + +/*! \brief + \param +*/ +vtt_t* _vtt_parse(const utf8_char_t* data, size_t size, int srt_mode); + +/*! \brief + \param +*/ +static inline vtt_block_t* vtt_cue_next(vtt_block_t* block) { return block->next; } + +/*! \brief + \param +*/ +static inline utf8_char_t* vtt_block_data(vtt_block_t* block) { return (utf8_char_t*)(block) + sizeof(vtt_block_t); } + +/*! \brief + \param +*/ +static inline void vtt_crack_time(double tt, int* hh, int* mm, int* ss, int* ms) +{ + (*ms) = (int)((int64_t)(tt * 1000) % 1000); + (*ss) = (int)((int64_t)(tt) % 60); + (*mm) = (int)((int64_t)(tt / (60)) % 60); + (*hh) = (int)((int64_t)(tt / (60 * 60))); +} + +// This only converts the current CUE, it does not walk the list +/*! \brief + \param +*/ +int vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame); + +// returns the new cue +/*! \brief + \param +*/ +vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt); +/*! \brief + \param +*/ +void vtt_dump(vtt_t* vtt); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/deps/libcaption/caption/xds.h b/deps/libcaption/caption/xds.h index 7b510f50c..e457a597c 100644 --- a/deps/libcaption/caption/xds.h +++ b/deps/libcaption/caption/xds.h @@ -1,32 +1,48 @@ /**********************************************************************************************/ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* The MIT License */ /* */ -/* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file */ -/* except in compliance with the License. A copy of the License is located at */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ -/* http://aws.amazon.com/apache2.0/ */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ /* */ -/* or in the "license" file accompanying this file. This file is distributed on an "AS IS" */ -/* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the */ -/* License for the specific language governing permissions and limitations under the License. */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ +/* THE SOFTWARE. */ /**********************************************************************************************/ - #ifndef LIBCAPTION_XDS_H #define LIBCAPTION_XDS_H +#ifdef __cplusplus +extern "C" { +#endif -#include #include +#include typedef struct { int state; - uint8_t class; + uint8_t class_code; uint8_t type; uint32_t size; uint8_t content[32]; uint8_t checksum; } xds_t; -void xds_init (xds_t* xds); -int xds_decode (xds_t* xds, uint16_t cc); +void xds_init(xds_t* xds); +int xds_decode(xds_t* xds, uint16_t cc); +#ifdef __cplusplus +} +#endif #endif diff --git a/deps/libcaption/examples/add_captions.sh b/deps/libcaption/examples/add_captions.sh deleted file mode 100644 index 68e295ef8..000000000 --- a/deps/libcaption/examples/add_captions.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -if [ $# -lt 2 ] -then - echo "Need at least 2 arguments." - echo "$0 InputVideo InputSRT [OutputFilename]" - exit 1 -fi - -VIDEO=$1 -SRT=$2 - -if [ -z "$3" ] -then - OUTFILE="out.flv" -else - OUTFILE="$3" -fi - -echo "Video=$VIDEO" -echo "Captions=$SRT" -echo "Outfile=$OUTFILE" - -# ffmpeg -i $VIDEO -acodec copy -vcodec copy -f flv - | ./flv+srt - $SRT - | ffmpeg -i - -acodec copy -vcodec copy $OUTFILE -ffmpeg -i $VIDEO -threads 0 -vcodec libx264 -profile:v main -preset:v medium \ --r 30 -g 60 -keyint_min 60 -sc_threshold 0 -b:v 4000k -maxrate 4000k \ --bufsize 4000k -filter:v scale="trunc(oh*a/2)*2:720" \ --sws_flags lanczos+accurate_rnd -strict -2 -acodec aac -b:a 96k -ar 48000 -ac 2 \ --f flv - | ./flv+srt - $SRT - | ffmpeg -i - -acodec copy -vcodec copy -y $OUTFILE diff --git a/deps/libcaption/examples/captioner.c b/deps/libcaption/examples/captioner.c deleted file mode 100644 index 66b29fa9b..000000000 --- a/deps/libcaption/examples/captioner.c +++ /dev/null @@ -1,117 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include "caption.h" -#include "flv.h" - -char charcode[] = { - 0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 0, - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']','\n', 0, 'A', 'S', - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\'', '`', 0, '\\', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, -}; - -int data_ready (int fd) -{ - fd_set set; - struct timeval timeout = {0,0}; - FD_ZERO (&set); - FD_SET (fd,&set); - int cnt = select (fd+1, &set, 0, 0, &timeout); - FD_ZERO (&set); - return (0 < cnt); -} - -#define MAX_CAP_LENGTH (SCREEN_ROWS*SCREEN_COLS) -int main (int argc, char** argv) -{ - int fd; - ssize_t n; - flvtag_t tag; - struct input_event ev; - int has_audio, has_video; - const char* dev = argv[1]; - char text[MAX_CAP_LENGTH+1]; - memset (text,0,MAX_CAP_LENGTH+1); - - FILE* flv = flv_open_read ("-"); - FILE* out = flv_open_write ("-"); - fd = open (dev, O_RDONLY); - - if (fd == -1) { - fprintf (stderr, "Cannot open %s: %s.\n", dev, strerror (errno)); - return EXIT_FAILURE; - } - - if (!flv_read_header (flv,&has_audio,&has_video)) { - fprintf (stderr,"%s is not an flv file\n", argv[1]); - return EXIT_FAILURE; - } - - if (!flv_write_header (out,has_audio,has_video)) { - return EXIT_FAILURE; - } - - flvtag_init (&tag); - - while (flv_read_tag (flv,&tag)) { - if (flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag)) { - if (data_ready (fd)) { - n = read (fd, &ev, sizeof ev); - - if (n == (ssize_t)-1) { - if (errno == EINTR) { - continue; - } else { - break; - } - } else if (n != sizeof ev) { - errno = EIO; - break; - } - - int len = strlen (text); - char c = (EV_KEY == ev.type && 1 == ev.value && ev.code < 64) ? charcode[ev.code] : 0; - - if (0 < len && '\n' == c) { - fprintf (stderr,"='%s'\n", text); - flvtag_addcaption (&tag, text); - memset (text,0,MAX_CAP_LENGTH+1); - } else if (0 != c && len < MAX_CAP_LENGTH) { - text[len] = c; - } - } - } - - flv_write_tag (out,&tag); - } - - return EXIT_SUCCESS; -} diff --git a/deps/libcaption/examples/flv+scc.c b/deps/libcaption/examples/flv+scc.c deleted file mode 100644 index c36822411..000000000 --- a/deps/libcaption/examples/flv+scc.c +++ /dev/null @@ -1,23 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ diff --git a/deps/libcaption/examples/flv+srt.c b/deps/libcaption/examples/flv+srt.c deleted file mode 100644 index 7828c790f..000000000 --- a/deps/libcaption/examples/flv+srt.c +++ /dev/null @@ -1,96 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include -#include -#include -#include "srt.h" -#include "flv.h" -#include "avc.h" -// #include "sei.h" - -#define MAX_SRT_SIZE (10*1024*1024) -#define MAX_READ_SIZE 4096 - -srt_t* srt_from_file (const char* path) -{ - srt_t* head = 0; - size_t read, totl = 0; - FILE* file = fopen (path,"r"); - - if (file) { - char* srt_data = malloc (MAX_SRT_SIZE); - size_t srt_size = 0; - size_t size = MAX_SRT_SIZE; - char* data = srt_data; - - while (0 < (read = fread (data,1,size,file))) { - totl += read; data += read; size -= read; srt_size += read; - } - - head = srt_parse (srt_data,srt_size); - free (srt_data); - } - - return head; -} - -int main (int argc, char** argv) -{ - flvtag_t tag; - FILE* flv = flv_open_read (argv[1]); - FILE* out = flv_open_write (argv[3]); - - int has_audio, has_video; - flvtag_init (&tag); - - if (!flv_read_header (flv,&has_audio,&has_video)) { - fprintf (stderr,"%s is not an flv file\n", argv[1]); - return EXIT_FAILURE; - } - - srt_t* head = srt_from_file (argv[2]); - srt_t* srt = head; - - if (! head) { - fprintf (stderr,"%s is not an srt file\n", argv[2]); - return EXIT_FAILURE; - } - - flv_write_header (out,has_audio,has_video); - - while (flv_read_tag (flv,&tag)) { - // TODO handle B freame! - if (srt && flvtag_pts_seconds (&tag) >= srt->timestamp && flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag)) { - fprintf (stderr,"%f: %s\n", srt->timestamp, srt_data (srt)); - flvtag_addcaption (&tag, srt_data (srt)); - srt = srt->next; - } - - flv_write_tag (out,&tag); - // Write tag out here - } - - srt_free (head); - return EXIT_SUCCESS; -} diff --git a/deps/libcaption/examples/flv.c b/deps/libcaption/examples/flv.c deleted file mode 100644 index 67ce3c52a..000000000 --- a/deps/libcaption/examples/flv.c +++ /dev/null @@ -1,383 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include "flv.h" -#include -#include - -void flvtag_init (flvtag_t* tag) -{ - memset (tag,0,sizeof (flvtag_t)); -} - -void flvtag_free (flvtag_t* tag) -{ - if (tag->data) { - free (tag->data); - } - - flvtag_init (tag); -} - -int flvtag_reserve (flvtag_t* tag, uint32_t size) -{ - size += FLV_TAG_HEADER_SIZE + FLV_TAG_FOOTER_SIZE; - - if (size > tag->aloc) { - tag->data = realloc (tag->data,size); - tag->aloc = size; - } - - return 1; -} - -FILE* flv_open_read (const char* flv) -{ - if (0 == flv || 0 == strcmp ("-",flv)) { - return stdin; - } - - return fopen (flv,"rb"); -} - -FILE* flv_open_write (const char* flv) -{ - if (0 == flv || 0 == strcmp ("-",flv)) { - return stdout; - } - - return fopen (flv,"wb"); -} - -FILE* flv_close (FILE* flv) -{ - fclose (flv); - return 0; -} - -int flv_read_header (FILE* flv, int* has_audio, int* has_video) -{ - uint8_t h[FLV_HEADER_SIZE]; - - if (FLV_HEADER_SIZE != fread (&h[0],1,FLV_HEADER_SIZE,flv)) { - return 0; - } - - if ('F' != h[0] || 'L' != h[1] || 'V' != h[2]) { - return 0; - } - - (*has_audio) = h[4]&0x04; - (*has_video) = h[4]&0x01; - return 1; -} - -int flv_write_header (FILE* flv, int has_audio, int has_video) -{ - uint8_t h[FLV_HEADER_SIZE] = {'F', 'L', 'V', 1, (has_audio?0x04:0x00) | (has_video?0x01:0x00), 0, 0, 0, 9, 0, 0, 0, 0 }; - return FLV_HEADER_SIZE == fwrite (&h[0],1,FLV_HEADER_SIZE,flv); -} - -int flv_read_tag (FILE* flv, flvtag_t* tag) -{ - uint32_t size; - uint8_t h[FLV_TAG_HEADER_SIZE]; - - if (FLV_TAG_HEADER_SIZE != fread (&h[0],1,FLV_TAG_HEADER_SIZE,flv)) { - return 0; - } - - size = ( (h[1]<<16) | (h[2]<<8) |h[3]); - flvtag_reserve (tag, size); - // copy header to buffer - memcpy (tag->data,&h[0],FLV_TAG_HEADER_SIZE); - - if (size+FLV_TAG_FOOTER_SIZE != fread (&tag->data[FLV_TAG_HEADER_SIZE],1,size+FLV_TAG_FOOTER_SIZE,flv)) { - return 0; - } - - return 1; -} - -int flv_write_tag (FILE* flv, flvtag_t* tag) -{ - size_t size = flvtag_raw_size (tag); - return size == fwrite (flvtag_raw_data (tag),1,size,flv); -} -//////////////////////////////////////////////////////////////////////////////// -size_t flvtag_header_size (flvtag_t* tag) -{ - switch (flvtag_type (tag)) { - case flvtag_type_audio: - return FLV_TAG_HEADER_SIZE + (flvtag_soundformat_aac != flvtag_soundformat (tag) ? 1 : 2); - - case flvtag_type_video: - // CommandFrame does not have a compositionTime - return FLV_TAG_HEADER_SIZE + (flvtag_codecid_avc != flvtag_codecid (tag) ? 1 : (flvtag_frametype_commandframe != flvtag_frametype (tag) ? 5 : 2)); - - default: - return FLV_TAG_HEADER_SIZE; - } -} - -size_t flvtag_payload_size (flvtag_t* tag) -{ - return FLV_TAG_HEADER_SIZE + flvtag_size (tag) - flvtag_header_size (tag); -} - -uint8_t* flvtag_payload_data (flvtag_t* tag) -{ - size_t payload_offset = flvtag_header_size (tag); - return &tag->data[payload_offset]; -} -//////////////////////////////////////////////////////////////////////////////// -int flvtag_updatesize (flvtag_t* tag, uint32_t size) -{ - tag->data[1] = size>>16; // DataSize - tag->data[2] = size>>8; // DataSize - tag->data[3] = size>>0; // DataSize - size += 11; - tag->data[size+0] = size>>24; // PrevTagSize - tag->data[size+1] = size>>16; // PrevTagSize - tag->data[size+2] = size>>8; // PrevTagSize - tag->data[size+3] = size>>0; // PrevTagSize - return 1; -} - -#define FLVTAG_PREALOC 2048 -int flvtag_initavc (flvtag_t* tag, uint32_t dts, int32_t cts, flvtag_frametype_t type) -{ - flvtag_init (tag); - flvtag_reserve (tag,5+FLVTAG_PREALOC); - tag->data[0] = flvtag_type_video; - tag->data[4] = dts>>16; - tag->data[5] = dts>>8; - tag->data[6] = dts>>0; - tag->data[7] = dts>>24; - tag->data[8] = 0; // StreamID - tag->data[9] = 0; // StreamID - tag->data[10] = 0; // StreamID - // VideoTagHeader - tag->data[11] = ( (type<<4) %0xF0) |0x07; // CodecId - tag->data[12] = 0x01; // AVC NALU - tag->data[13] = cts>>16; - tag->data[14] = cts>>8; - tag->data[15] = cts>>0; - flvtag_updatesize (tag,5); - return 1; -} - -int flvtag_initamf (flvtag_t* tag, uint32_t dts) -{ - flvtag_init (tag); - flvtag_reserve (tag,FLVTAG_PREALOC); - tag->data[0] = flvtag_type_scriptdata; - tag->data[4] = dts>>16; - tag->data[5] = dts>>8; - tag->data[6] = dts>>0; - tag->data[7] = dts>>24; - tag->data[8] = 0; // StreamID - tag->data[9] = 0; // StreamID - tag->data[10] = 0; // StreamID - flvtag_updatesize (tag,0); - return 1; -} - -// shamelessly taken from libtomcrypt, an public domain project -static void base64_encode (const unsigned char* in, unsigned long inlen, unsigned char* out, unsigned long* outlen) -{ - static const char* codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - unsigned long i, len2, leven; - unsigned char* p; - - /* valid output size ? */ - len2 = 4 * ( (inlen + 2) / 3); - - if (*outlen < len2 + 1) { - *outlen = len2 + 1; - fprintf (stderr,"\n\nHERE\n\n"); - return; - } - - p = out; - leven = 3* (inlen / 3); - - for (i = 0; i < leven; i += 3) { - *p++ = codes[ (in[0] >> 2) & 0x3F]; - *p++ = codes[ ( ( (in[0] & 3) << 4) + (in[1] >> 4)) & 0x3F]; - *p++ = codes[ ( ( (in[1] & 0xf) << 2) + (in[2] >> 6)) & 0x3F]; - *p++ = codes[in[2] & 0x3F]; - in += 3; - } - - if (i < inlen) { - unsigned a = in[0]; - unsigned b = (i+1 < inlen) ? in[1] : 0; - - *p++ = codes[ (a >> 2) & 0x3F]; - *p++ = codes[ ( ( (a & 3) << 4) + (b >> 4)) & 0x3F]; - *p++ = (i+1 < inlen) ? codes[ ( ( (b & 0xf) << 2)) & 0x3F] : '='; - *p++ = '='; - } - - /* return ok */ - *outlen = p - out; -} - -const char onCaptionInfo708[] = { 0x02,0x00,0x0D, 'o','n','C','a','p','t','i','o','n','I','n','f','o', - 0x08, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x04, 't','y','p','e', - 0x02, 0x00, 0x03, '7','0','8', - 0x00, 0x04, 'd','a','t','a', - 0x02, 0x00,0x00 - }; - -int flvtag_amfcaption_708 (flvtag_t* tag, uint32_t timestamp, sei_message_t* msg) -{ - flvtag_initamf (tag,timestamp); - unsigned long size = 1 + (4 * ( (sei_message_size (msg) + 2) / 3)); - flvtag_reserve (tag, sizeof (onCaptionInfo708) + size + 3); - memcpy (flvtag_payload_data (tag),onCaptionInfo708,sizeof (onCaptionInfo708)); - uint8_t* data = flvtag_payload_data (tag) + sizeof (onCaptionInfo708); - base64_encode (sei_message_data (msg), sei_message_size (msg), data, &size); - - // Update the size of the base64 string - data[-2] = size >> 8; - data[-1] = size >> 0; - // write the last array element - data[size+0] = 0x00; - data[size+1] = 0x00; - data[size+2] = 0x09; - flvtag_updatesize (tag, sizeof (onCaptionInfo708) + size + 3); - - return 1; -} - -const char onCaptionInfoUTF8[] = { 0x02,0x00,0x0D, 'o','n','C','a','p','t','i','o','n','I','n','f','o', - 0x08, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x04, 't','y','p','e', - 0x02, 0x00, 0x04, 'U','T','F','8', - 0x00, 0x04, 'd','a','t','a', - 0x02, 0x00,0x00 - }; - -#define MAX_AMF_STRING 65636 -int flvtag_amfcaption_utf8 (flvtag_t* tag, uint32_t timestamp, const utf8_char_t* text) -{ - flvtag_initamf (tag,timestamp); - unsigned long size = strlen (text); - - if (MAX_AMF_STRING < size) { - size = MAX_AMF_STRING; - } - - flvtag_reserve (tag, sizeof (onCaptionInfoUTF8) + size + 3); - memcpy (flvtag_payload_data (tag),onCaptionInfoUTF8,sizeof (onCaptionInfoUTF8)); - uint8_t* data = flvtag_payload_data (tag) + sizeof (onCaptionInfo708); - memcpy (data,text,size); - // Update the size of the string - data[-2] = size >> 8; - data[-1] = size >> 0; - // write the last array element - data[size+0] = 0x00; - data[size+1] = 0x00; - data[size+2] = 0x09; - flvtag_updatesize (tag, sizeof (onCaptionInfoUTF8) + size + 3); - - return 1; -} - -#define LENGTH_SIZE 4 - -int flvtag_avcwritenal (flvtag_t* tag, uint8_t* data, size_t size) -{ - uint32_t flvsize = flvtag_size (tag); - flvtag_reserve (tag,flvsize+LENGTH_SIZE+size); - uint8_t* payload = tag->data + FLV_TAG_HEADER_SIZE + flvsize; - payload[0] = size>>24; // nalu size - payload[1] = size>>16; - payload[2] = size>>8; - payload[3] = size>>0; - memcpy (&payload[LENGTH_SIZE],data,size); - flvtag_updatesize (tag,flvsize+LENGTH_SIZE+size); - - return 1; -} - -int flvtag_addcaption (flvtag_t* tag, const utf8_char_t* text) -{ - if (flvtag_avcpackettype_nalu != flvtag_avcpackettype (tag)) { - return 0; - } - - sei_t sei; - caption_frame_t frame; - - sei_init (&sei); - caption_frame_init (&frame); - caption_frame_from_text (&frame, text); - sei_from_caption_frame (&sei, &frame); - - uint8_t* sei_data = malloc (sei_render_size (&sei)); - size_t sei_size = sei_render (&sei, sei_data); - - // rewrite tag - flvtag_t new_tag; - flvtag_initavc (&new_tag, flvtag_dts (tag), flvtag_cts (tag), flvtag_frametype (tag)); - uint8_t* data = flvtag_payload_data (tag); - ssize_t size = flvtag_payload_size (tag); - - while (0data); - sei_free (&sei); - tag->data = new_tag.data; - tag->aloc = new_tag.aloc; - return 1; -} diff --git a/deps/libcaption/examples/flv.h b/deps/libcaption/examples/flv.h deleted file mode 100644 index 9bdd93f08..000000000 --- a/deps/libcaption/examples/flv.h +++ /dev/null @@ -1,142 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#ifndef LIBCAPTION_FLV_H -#define LIBCAPTION_FLV_H - -#include -#include -#include -#define FLV_HEADER_SIZE 13 -#define FLV_FOOTER_SIZE 4 -#define FLV_TAG_HEADER_SIZE 11 -#define FLV_TAG_FOOTER_SIZE 4 -//////////////////////////////////////////////////////////////////////////////// -#include "avc.h" -//////////////////////////////////////////////////////////////////////////////// -typedef struct { - uint8_t* data; - size_t aloc; -} flvtag_t; - -void flvtag_init (flvtag_t* tag); -void flvtag_free (flvtag_t* tag); -void flvtag_swap (flvtag_t* tag1, flvtag_t* tag2); -//////////////////////////////////////////////////////////////////////////////// -typedef enum { - flvtag_type_audio = 0x08, - flvtag_type_video = 0x09, - flvtag_type_scriptdata = 0x12, -} flvtag_type_t; - -static inline flvtag_type_t flvtag_type (flvtag_t* tag) { return (flvtag_type_t) tag->data[0]&0x1F; } -//////////////////////////////////////////////////////////////////////////////// -typedef enum { - flvtag_soundformat_unknown = -1, - flvtag_soundformat_linearpcmplatformendian = 0, - flvtag_soundformat_adpcm = 1, - flvtag_soundformat_mp3 = 2, - flvtag_soundformat_linearpcmlittleendian = 3, - flvtag_soundformat_nellymoser_16khzmono = 4, - flvtag_soundformat_nellymoser_8khzmono = 5, - flvtag_soundformat_nellymoser = 6, - flvtag_soundformat_g711alawlogarithmicpcm = 7, - flvtag_soundformat_g711mulawlogarithmicpcm = 8, - flvtag_soundformat_reserved = 9, - flvtag_soundformat_aac = 10, - flvtag_soundformat_speex = 11, - flvtag_soundformat_mp3_8khz = 14, - flvtag_soundformat_devicespecificsound = 15 -} flvtag_soundformat_t; - -static inline flvtag_soundformat_t flvtag_soundformat (flvtag_t* tag) { return (flvtag_type_audio!=flvtag_type (tag)) ?flvtag_soundformat_unknown: (flvtag_soundformat_t) (tag->data[0]>>4) &0x0F; } -//////////////////////////////////////////////////////////////////////////////// -typedef enum { - flvtag_codecid_unknown = -1, - flvtag_codecid_sorensonh263 = 2, - flvtag_codecid_screenvideo = 3, - flvtag_codecid_on2vp6 = 4, - flvtag_codecid_on2vp6withalphachannel = 5, - flvtag_codecid_screenvideoversion2 = 6, - flvtag_codecid_avc = 7 -} flvtag_codecid_t; - -static inline flvtag_codecid_t flvtag_codecid (flvtag_t* tag) { return (flvtag_type_video!=flvtag_type (tag)) ? (flvtag_codecid_unknown) : (tag->data[11]&0x0F); } -//////////////////////////////////////////////////////////////////////////////// -typedef enum { - flvtag_frametype_unknown = -1, - flvtag_frametype_keyframe = 1, - flvtag_frametype_interframe = 2, - flvtag_frametype_disposableinterframe = 3, - flvtag_frametype_generatedkeyframe = 4, - flvtag_frametype_commandframe = 5 -} flvtag_frametype_t; - -static inline flvtag_frametype_t flvtag_frametype (flvtag_t* tag) { return (flvtag_type_video!=flvtag_type (tag)) ?flvtag_frametype_keyframe: ( (tag->data[11]>>4) &0x0F); } -//////////////////////////////////////////////////////////////////////////////// -typedef enum { - flvtag_avcpackettype_unknown = -1, - flvtag_avcpackettype_sequenceheader = 0, - flvtag_avcpackettype_nalu = 1, - flvtag_avcpackettype_endofsequence = 2 -} flvtag_avcpackettype_t; - -static inline flvtag_avcpackettype_t flvtag_avcpackettype (flvtag_t* tag) { return (flvtag_codecid_avc!=flvtag_codecid (tag)) ?flvtag_avcpackettype_unknown:tag->data[12]; } -//////////////////////////////////////////////////////////////////////////////// -static inline size_t flvtag_size (flvtag_t* tag) { return (tag->data[1]<<16) | (tag->data[2]<<8) | tag->data[3]; } -static inline uint32_t flvtag_timestamp (flvtag_t* tag) { return (tag->data[7]<<24) | (tag->data[4]<<16) | (tag->data[5]<<8) | tag->data[6]; } -static inline uint32_t flvtag_dts (flvtag_t* tag) { return flvtag_timestamp (tag); } -static inline uint32_t flvtag_cts (flvtag_t* tag) { return (flvtag_avcpackettype_nalu!=flvtag_avcpackettype (tag)) ?0: (tag->data[13]<<16) | (tag->data[14]<<8) |tag->data[15]; } -static inline uint32_t flvtag_pts (flvtag_t* tag) { return flvtag_dts (tag)+flvtag_cts (tag); } -static inline double flvtag_dts_seconds (flvtag_t* tag) { return flvtag_dts (tag) / 1000.0; } -static inline double flvtag_cts_seconds (flvtag_t* tag) { return flvtag_cts (tag) / 1000.0; } -static inline double flvtag_pts_seconds (flvtag_t* tag) { return flvtag_pts (tag) / 1000.0; } -//////////////////////////////////////////////////////////////////////////////// -size_t flvtag_header_size (flvtag_t* tag); -size_t flvtag_payload_size (flvtag_t* tag); -uint8_t* flvtag_payload_data (flvtag_t* tag); -//////////////////////////////////////////////////////////////////////////////// -FILE* flv_open_read (const char* flv); -FILE* flv_open_write (const char* flv); -FILE* flv_close (FILE* flv); -//////////////////////////////////////////////////////////////////////////////// -static inline const uint8_t* flvtag_raw_data (flvtag_t* tag) { return tag->data; } -static inline const size_t flvtag_raw_size (flvtag_t* tag) { return flvtag_size (tag)+FLV_TAG_HEADER_SIZE+FLV_TAG_FOOTER_SIZE; } -//////////////////////////////////////////////////////////////////////////////// -int flv_read_tag (FILE* flv, flvtag_t* tag); -int flv_write_tag (FILE* flv, flvtag_t* tag); -int flv_read_header (FILE* flv, int* has_audio, int* has_video); -int flv_write_header (FILE* flv, int has_audio, int has_video); -//////////////////////////////////////////////////////////////////////////////// -// If the tage has more that on sei message, they will be combined into one -sei_t* flv_read_sei (FILE* flv, flvtag_t* tag); -//////////////////////////////////////////////////////////////////////////////// -int flvtag_initavc (flvtag_t* tag, uint32_t dts, int32_t cts, flvtag_frametype_t type); -int flvtag_avcwritenal (flvtag_t* tag, uint8_t* data, size_t size); -int flvtag_addcaption (flvtag_t* tag, const utf8_char_t* text); -//////////////////////////////////////////////////////////////////////////////// -int flvtag_amfcaption_708 (flvtag_t* tag, uint32_t timestamp, sei_message_t* msg); -//////////////////////////////////////////////////////////////////////////////// -// This method is expermental, and not currently available on Twitch -int flvtag_amfcaption_utf8 (flvtag_t* tag, uint32_t timestamp, const utf8_char_t* text); -#endif diff --git a/deps/libcaption/examples/flv2srt.c b/deps/libcaption/examples/flv2srt.c deleted file mode 100644 index 5afa1711e..000000000 --- a/deps/libcaption/examples/flv2srt.c +++ /dev/null @@ -1,92 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include "flv.h" -#include "srt.h" -#include "avc.h" - -#define LENGTH_SIZE 4 -int main (int argc, char** argv) -{ - const char* path = argv[1]; - - sei_t sei; - flvtag_t tag; - srt_t* srt = 0, *head = 0; - int i, has_audio, has_video; - caption_frame_t frame; - - flvtag_init (&tag); - caption_frame_init (&frame); - - FILE* flv = flv_open_read (path); - - if (!flv_read_header (flv,&has_audio,&has_video)) { - fprintf (stderr,"'%s' Not an flv file\n", path); - } else { - fprintf (stderr,"Reading from '%s'\n", path); - } - - while (flv_read_tag (flv,&tag)) { - if (flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag)) { - ssize_t size = flvtag_payload_size (&tag); - uint8_t* data = flvtag_payload_data (&tag); - - while (0 -#include -#include -#include "flv.h" -#include "avc.h" - -#define CAPTION_METHOD_SEI_708 0 // embedded 708 -#define CAPTION_METHOD_AMF_708 1 // onCaptionInfo type = 708 -#define CAPTION_METHOD_AMF_UTF8 2 // onCaptionInfo type = utf8 -#define CAPTION_METHOD CAPTION_METHOD_SEI_708 - -void get_dudes (char* str) -{ - sprintf (str, " %s%s %s(-_-)%s %s(-_-)%s.%s(-_-)%s %s%s", EIA608_CHAR_EIGHTH_NOTE, EIA608_CHAR_EIGHTH_NOTE, - ! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, - ! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, - ! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, - ! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, - ! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, - ! (rand() % 2) ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, - EIA608_CHAR_EIGHTH_NOTE, EIA608_CHAR_EIGHTH_NOTE); -} - -void write_amfcaptions_708 (FILE* out, uint32_t timestamp, const char* text) -{ - sei_t sei; - flvtag_t tag; - sei_message_t* msg; - caption_frame_t frame; - sei_init (&sei); - flvtag_init (&tag); - caption_frame_init (&frame); - caption_frame_from_text (&frame, text); - sei_from_caption_frame (&sei, &frame); - // caption_frame_dump (&frame); - - for (msg = sei_message_head (&sei); msg; msg=sei_message_next (msg),++timestamp) { - flvtag_amfcaption_708 (&tag,timestamp,msg); - flv_write_tag (out,&tag); - } - - sei_free (&sei); - flvtag_free (&tag); -} - - -void write_amfcaptions_utf8 (FILE* out, uint32_t timestamp, const utf8_char_t* text) -{ - flvtag_t tag; - flvtag_init (&tag); - flvtag_amfcaption_utf8 (&tag,timestamp,text); - flv_write_tag (out,&tag); - flvtag_free (&tag); -} - -int main (int argc, char** argv) -{ - flvtag_t tag; - uint32_t nextParty = 1000; - int has_audio, has_video; - FILE* flv = flv_open_read (argv[1]); - FILE* out = flv_open_write (argv[2]); - char partyDudes[64]; - - flvtag_init (&tag); - - if (!flv_read_header (flv,&has_audio,&has_video)) { - fprintf (stderr,"%s is not an flv file\n", argv[1]); - return EXIT_FAILURE; - } - - flv_write_header (out,has_audio,has_video); - - while (flv_read_tag (flv,&tag)) { - - if (flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag) && nextParty <= flvtag_timestamp (&tag)) { - get_dudes (partyDudes); - - if (CAPTION_METHOD == CAPTION_METHOD_SEI_708) { - flvtag_addcaption (&tag, partyDudes); - } else if (CAPTION_METHOD == CAPTION_METHOD_AMF_708) { - write_amfcaptions_708 (out,nextParty,partyDudes); - } else if (CAPTION_METHOD == CAPTION_METHOD_AMF_708) { - write_amfcaptions_utf8 (out, nextParty, partyDudes); - } else { - fprintf (stderr,"Unknnow method\n"); - return EXIT_FAILURE; - - } - - fprintf (stderr,"%d: %s\n",nextParty, partyDudes); - nextParty += 500; // party all the time - } - - flv_write_tag (out,&tag); - } - - return EXIT_SUCCESS; -} diff --git a/deps/libcaption/examples/rollup.c b/deps/libcaption/examples/rollup.c deleted file mode 100644 index e290af6a6..000000000 --- a/deps/libcaption/examples/rollup.c +++ /dev/null @@ -1,95 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include -#include -#include -#include "flv.h" -#include "avc.h" -#include "srt.h" -#include "wonderland.h" - -#define SECONDS_PER_LINE 3.0 -srt_t* appennd_caption (const utf8_char_t* data, srt_t* prev, srt_t** head) -{ - - int r, c, chan = 0; - ssize_t size = (ssize_t) strlen (data); - size_t char_count, char_length, line_length = 0, trimmed_length = 0; - - for (r = 0 ; 0 < size && SCREEN_ROWS > r ; ++r) { - line_length = utf8_line_length (data); - trimmed_length = utf8_trimmed_length (data,line_length); - char_count = utf8_char_count (data,trimmed_length); - - // If char_count is greater than one line can display, split it. - if (SCREEN_COLS < char_count) { - char_count = utf8_wrap_length (data,SCREEN_COLS); - line_length = utf8_string_length (data,char_count+1); - } - - // fprintf (stderr,"%.*s\n", line_length, data); - prev = srt_new (data, line_length, prev ? prev->timestamp + SECONDS_PER_LINE : 0, prev, head); - - data += line_length; - size -= (ssize_t) line_length; - } - - return prev; -} - -int main (int argc, char** argv) -{ - int i = 0; - flvtag_t tag; - srt_t* head = 0, *tail = 0; - int has_audio, has_video; - FILE* flv = flv_open_read (argv[1]); - FILE* out = flv_open_write (argv[2]); - flvtag_init (&tag); - - for (i = 0 ; wonderland[i][0]; ++i) { - tail = appennd_caption (wonderland[i], tail, &head); - } - - - if (!flv_read_header (flv,&has_audio,&has_video)) { - fprintf (stderr,"%s is not an flv file\n", argv[1]); - return EXIT_FAILURE; - } - - flv_write_header (out,has_audio,has_video); - - while (flv_read_tag (flv,&tag)) { - if (head && flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag) && head->timestamp <= flvtag_pts_seconds (&tag)) { - fprintf (stderr,"%f %s\n", flvtag_pts_seconds (&tag), srt_data (head)); - flvtag_addcaption (&tag, srt_data (head)); - head = srt_free_head (head); - } - - - flv_write_tag (out,&tag); - } - - return EXIT_SUCCESS; -} diff --git a/deps/libcaption/examples/rtmpspit.c b/deps/libcaption/examples/rtmpspit.c deleted file mode 100644 index c7ef1131a..000000000 --- a/deps/libcaption/examples/rtmpspit.c +++ /dev/null @@ -1,165 +0,0 @@ -#include "flv.h" -#include -#include -#include -#include -#include -#include -#include - - -int MyRTMP_Write (RTMP* r, const char* buf, int size) -{ - RTMPPacket* pkt = &r->m_write; - char* enc; - int s2 = size, ret, num; - - pkt->m_nChannel = 0x04; /* source channel */ - pkt->m_nInfoField2 = r->m_stream_id; - - while (s2) { - if (!pkt->m_nBytesRead) { - if (size < 11) { - /* FLV pkt too small */ - return 0; - } - - if (buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V') { - buf += 13; - s2 -= 13; - } - - pkt->m_packetType = *buf++; - pkt->m_nBodySize = AMF_DecodeInt24 (buf); - buf += 3; - pkt->m_nTimeStamp = AMF_DecodeInt24 (buf); - buf += 3; - pkt->m_nTimeStamp |= *buf++ << 24; - buf += 3; - s2 -= 11; - - if ( ( (pkt->m_packetType == RTMP_PACKET_TYPE_AUDIO - || pkt->m_packetType == RTMP_PACKET_TYPE_VIDEO) && - !pkt->m_nTimeStamp) || pkt->m_packetType == RTMP_PACKET_TYPE_INFO) { - pkt->m_headerType = RTMP_PACKET_SIZE_LARGE; - } else { - pkt->m_headerType = RTMP_PACKET_SIZE_MEDIUM; - } - - if (!RTMPPacket_Alloc (pkt, pkt->m_nBodySize)) { - RTMP_Log (RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__); - return FALSE; - } - - enc = pkt->m_body; - } else { - enc = pkt->m_body + pkt->m_nBytesRead; - } - - num = pkt->m_nBodySize - pkt->m_nBytesRead; - - if (num > s2) { - num = s2; - } - - memcpy (enc, buf, num); - pkt->m_nBytesRead += num; - s2 -= num; - buf += num; - - if (pkt->m_nBytesRead == pkt->m_nBodySize) { - ret = RTMP_SendPacket (r, pkt, FALSE); - RTMPPacket_Free (pkt); - pkt->m_nBytesRead = 0; - - if (!ret) { - return -1; - } - - buf += 4; - s2 -= 4; - - if (s2 < 0) { - break; - } - } - } - - return size+s2; -} - -int main (int argc, const char** argv) -{ - FILE* flv; - RTMP* rtmp; - RTMPPacket rtmpPacket; - - flvtag_t tag; - int32_t timestamp = 0; - int has_audio, has_video; - char* url = 0; - - if (2 >= argc) { - fprintf (stderr,"Usage %s [input] [url]\n",argv[0]); - } - - url = (char*) argv[2]; - flv = flv_open_read (argv[1]); - - if (! flv) { - fprintf (stderr,"Could not open %s\n",argv[1]); - return EXIT_FAILURE; - } - - if (! flv_read_header (flv, &has_audio, &has_video)) { - fprintf (stderr,"Not an flv file %s\n",argv[1]); - return EXIT_FAILURE; - } - - flvtag_init (&tag); - rtmp = RTMP_Alloc(); - RTMP_Init (rtmp); - fprintf (stderr,"Connecting to %s\n", url); - RTMP_SetupURL (rtmp, url); - RTMP_EnableWrite (rtmp); - - RTMP_Connect (rtmp, NULL); - RTMP_ConnectStream (rtmp, 0); - memset (&rtmpPacket, 0, sizeof (RTMPPacket)); - - if (! RTMP_IsConnected (rtmp)) { - fprintf (stderr,"RTMP_IsConnected() Error\n"); - return EXIT_FAILURE; - } - - while (flv_read_tag (flv,&tag)) { - if (! RTMP_IsConnected (rtmp) || RTMP_IsTimedout (rtmp)) { - fprintf (stderr,"RTMP_IsConnected() Error\n"); - return EXIT_FAILURE; - } - - if (flvtag_timestamp (&tag) > timestamp) { - usleep (1000 * (flvtag_timestamp (&tag) - timestamp)); - timestamp = flvtag_timestamp (&tag); - } - - MyRTMP_Write (rtmp, (const char*) flvtag_raw_data (&tag),flvtag_raw_size (&tag)); - - // Handle RTMP ping and such - fd_set sockset; struct timeval timeout = {0,0}; - FD_ZERO (&sockset); FD_SET (RTMP_Socket (rtmp), &sockset); - register int result = select (RTMP_Socket (rtmp) + 1, &sockset, NULL, NULL, &timeout); - - if (result == 1 && FD_ISSET (RTMP_Socket (rtmp), &sockset)) { - RTMP_ReadPacket (rtmp, &rtmpPacket); - - if (! RTMPPacket_IsReady (&rtmpPacket)) { - fprintf (stderr,"Received RTMP packet\n"); - RTMP_ClientPacket (rtmp,&rtmpPacket); - RTMPPacket_Free (&rtmpPacket); - } - } - } - - return EXIT_SUCCESS; -} diff --git a/deps/libcaption/examples/scc2srt.c b/deps/libcaption/examples/scc2srt.c deleted file mode 100644 index 565fd237c..000000000 --- a/deps/libcaption/examples/scc2srt.c +++ /dev/null @@ -1,96 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include -#include -#include -#include "srt.h" -#include "scc.h" - -#define MAX_SCC_SIZE (10*1024*1024) -#define MAX_READ_SIZE 4096 -#define MAX_CC 128 - -size_t read_file (FILE* file, utf8_char_t* data, size_t size) -{ - size_t read, totl = 0; - - while (0 < (read = fread (data,1,MAX_READ_SIZE -#include -#include -#include "srt.h" - -#define MAX_SRT_SIZE (10*1024*1024) -#define MAX_READ_SIZE 4096 - -size_t read_file (FILE* file, utf8_char_t* data, size_t size) -{ - size_t read, totl = 0; - - while (0 < (read = fread (data,1,MAX_READ_SIZE -#include -#include -#include "srt.h" -#include "avc.h" -// #include "sei.h" - -#define MAX_SRT_SIZE (10*1024*1024) -#define MAX_READ_SIZE 4096 - -size_t read_file (FILE* file, utf8_char_t* data, size_t size) -{ - size_t read, totl = 0; - - while (0 < (read = fread (data,1,MAX_READ_SIZEnext) { - caption_frame_init (&frame); - srt_to_caption_frame (srt,&frame); - caption_frame_dump (&frame); - } - - srt_free (head); -} diff --git a/deps/libcaption/examples/ts.c b/deps/libcaption/examples/ts.c deleted file mode 100644 index fc82c532c..000000000 --- a/deps/libcaption/examples/ts.c +++ /dev/null @@ -1,117 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include "ts.h" -#include - -void ts_init (ts_t* ts) -{ - memset (ts,0,sizeof (ts_t)); -} - -static int64_t ts_parse_pts (const uint8_t* data) -{ - // 0000 1110 1111 1111 1111 1110 1111 1111 1111 1110 - uint64_t pts = 0; - pts |= (uint64_t) (data[0] & 0x0E) << 29; - pts |= (uint64_t) (data[1] & 0xFF) << 22; - pts |= (uint64_t) (data[2] & 0xFE) << 14; - pts |= (uint64_t) (data[3] & 0xFF) << 7; - pts |= (uint64_t) (data[4] & 0xFE) >> 1; - return pts; -} - -int ts_parse_packet (ts_t* ts, const uint8_t* data) -{ - size_t i = 0; - int pusi = !! (data[i + 1] & 0x40); // Payload Unit Start Indicator - int16_t pid = ( (data[i + 1] & 0x1F) << 8) | data[i + 2]; // PID - int adaption_present = !! (data[i + 3] & 0x20); // Adaptation field exist - int payload_present = !! (data[i + 3] & 0x10); // Contains payload - i += 4; - - ts->data = 0; - ts->size = 0; - - if (adaption_present) { - uint8_t adaption_length = data[i + 0]; // adaption field length - i += 1 + adaption_length; - } - - if (pid == 0) { - if (payload_present) { - // Skip the payload. - i += data[i] + 1; - } - - ts->pmtpid = ( (data[i + 10] & 0x1F) << 8) | data[i + 11]; - } else if (pid == ts->pmtpid) { - // PMT - if (payload_present) { - // Skip the payload. - i += data[i] + 1; - } - - uint16_t section_length = ( (data[i + 1] & 0x0F) << 8) | data[i + 2]; - int current = data[i + 5] & 0x01; - int16_t program_info_length = ( (data[i + 10] & 0x0F) << 8) | data[i + 11]; - int16_t descriptor_loop_length = section_length - (9 + program_info_length + 4); // 4 for the crc - - i += 12 + program_info_length; - - if (current) { - while (descriptor_loop_length >= 5) { - uint8_t stream_type = data[i]; - int16_t elementary_pid = ( (data[i + 1] & 0x1F) << 8) | data[i + 2]; - int16_t esinfo_length = ( (data[i + 3] & 0x0F) << 8) | data[i + 4]; - - if (0x1B == stream_type) { - ts->avcpid = elementary_pid; - } - - i += 5 + esinfo_length; - descriptor_loop_length -= 5 + esinfo_length; - } - } - } else if (payload_present && pid == ts->avcpid) { - if (pusi) { - // int data_alignment = !! (data[i + 6] & 0x04); - int has_pts = !! (data[i + 7] & 0x80); - int has_dts = !! (data[i + 7] & 0x40); - uint8_t header_length = data[i + 8]; - - if (has_pts) { - ts->pts = ts_parse_pts (&data[i + 9]); - ts->dts = has_dts ? ts_parse_pts (&data[i + 14]) : ts->pts; - } - - i += 9 + header_length; - } - - ts->data = &data[i]; - ts->size = TS_PACKET_SIZE-i; - return LIBCAPTION_READY; - } - - return LIBCAPTION_OK; -} diff --git a/deps/libcaption/examples/ts.h b/deps/libcaption/examples/ts.h deleted file mode 100644 index fb9e490d9..000000000 --- a/deps/libcaption/examples/ts.h +++ /dev/null @@ -1,49 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#ifndef LIBCAPTION_TS_H -#define LIBCAPTION_TS_H -#include "caption.h" -typedef struct { - int16_t pmtpid; - int16_t avcpid; - int64_t pts; - int64_t dts; - size_t size; - const uint8_t* data; -} ts_t; - -/*! \brief - \param - - Expects 188 byte TS packet -*/ -#define TS_PACKET_SIZE 188 -void ts_init (ts_t* ts); -int ts_parse_packet (ts_t* ts, const uint8_t* data); -// return timestamp in seconds -static inline double ts_dts_seconds (ts_t* ts) { return ts->dts / 90000.0; } -static inline double ts_pts_seconds (ts_t* ts) { return ts->pts / 90000.0; } -static inline double ts_cts_seconds (ts_t* ts) { return (ts->dts - ts->pts) / 90000.0; } - -#endif diff --git a/deps/libcaption/examples/ts2srt.c b/deps/libcaption/examples/ts2srt.c deleted file mode 100644 index 18d0b1a93..000000000 --- a/deps/libcaption/examples/ts2srt.c +++ /dev/null @@ -1,101 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ -#include "ts.h" -#include "srt.h" -#include "avc.h" -#include - -int main (int argc, char** argv) -{ - const char* path = argv[1]; - - ts_t ts; - sei_t sei; - avcnalu_t nalu; - srt_t* srt = 0, *head = 0; - caption_frame_t frame; - uint8_t pkt[TS_PACKET_SIZE]; - ts_init (&ts); - avcnalu_init (&nalu); - caption_frame_init (&frame); - - FILE* file = fopen (path,"rb+"); - - while (TS_PACKET_SIZE == fread (&pkt[0],1,TS_PACKET_SIZE, file)) { - switch (ts_parse_packet (&ts,&pkt[0])) { - case LIBCAPTION_OK: - // fprintf (stderr,"read ts packet\n"); - break; - - case LIBCAPTION_READY: { - // fprintf (stderr,"read ts packet DATA\n"); - while (ts.size) { - // fprintf (stderr,"ts.size %d (%02X%02X%02X%02X)\n",ts.size, ts.data[0], ts.data[1], ts.data[2], ts.data[3]); - - switch (avcnalu_parse_annexb (&nalu, &ts.data, &ts.size)) { - case LIBCAPTION_OK: - break; - - case LIBCAPTION_ERROR: - // fprintf (stderr,"LIBCAPTION_ERROR == avcnalu_parse_annexb()\n"); - avcnalu_init (&nalu); - break; - - case LIBCAPTION_READY: { - - if (6 == avcnalu_type (&nalu)) { - // fprintf (stderr,"NALU %d (%d)\n", avcnalu_type (&nalu), avcnalu_size (&nalu)); - sei_init (&sei); - sei_parse_avcnalu (&sei, &nalu, ts_dts_seconds (&ts), ts_cts_seconds (&ts)); - - // sei_dump (&sei); - - if (LIBCAPTION_READY == sei_to_caption_frame (&sei,&frame)) { - // caption_frame_dump (&frame); - srt = srt_from_caption_frame (&frame,srt,&head); - - // srt_dump (srt); - } - - sei_free (&sei); - } - - avcnalu_init (&nalu); - } break; - } - } - } break; - - case LIBCAPTION_ERROR: - // fprintf (stderr,"read ts packet ERROR\n"); - break; - } - - } - - srt_dump (head); - srt_free (head); - - return 1; -} diff --git a/deps/libcaption/examples/wonderland.h b/deps/libcaption/examples/wonderland.h deleted file mode 100644 index 5c68f0433..000000000 --- a/deps/libcaption/examples/wonderland.h +++ /dev/null @@ -1,2794 +0,0 @@ -const char* wonderland[] = { - "Project Gutenberg’s Alice’s Adventures in Wonderland, by Lewis Carroll", - "This eBook is for the use of anyone anywhere at no cost and with", - "almost no restrictions whatsoever. You may copy it, give it away or", - "re-use it under the terms of the Project Gutenberg License included", - "with this eBook or online at www.gutenberg.org", - "Title: Alice’s Adventures in Wonderland", - "Author: Lewis Carroll", - "Posting Date: June 25, 2008 [EBook #11]", - "Release Date: March, 1994", - "Last Updated: October 6, 2016", - "Language: English", - "Character set encoding: UTF-8", - "*** START OF THIS PROJECT GUTENBERG EBOOK ALICE’S ADVENTURES IN WONDERLAND ***", - "ALICE’S ADVENTURES IN WONDERLAND", - "Lewis Carroll", - "THE MILLENNIUM FULCRUM EDITION 3.0", - "CHAPTER I. Down the Rabbit-Hole", - "Alice was beginning to get very tired of sitting by her sister on the", - "bank, and of having nothing to do: once or twice she had peeped into the", - "book her sister was reading, but it had no pictures or conversations in", - "it, ‘and what is the use of a book,’ thought Alice ‘without pictures or", - "conversations?’", - "So she was considering in her own mind (as well as she could, for the", - "hot day made her feel very sleepy and stupid), whether the pleasure", - "of making a daisy-chain would be worth the trouble of getting up and", - "picking the daisies, when suddenly a White Rabbit with pink eyes ran", - "close by her.", - "There was nothing so VERY remarkable in that; nor did Alice think it so", - "VERY much out of the way to hear the Rabbit say to itself, ‘Oh dear!", - "Oh dear! I shall be late!’ (when she thought it over afterwards, it", - "occurred to her that she ought to have wondered at this, but at the time", - "it all seemed quite natural); but when the Rabbit actually TOOK A WATCH", - "OUT OF ITS WAISTCOAT-POCKET, and looked at it, and then hurried on,", - "Alice started to her feet, for it flashed across her mind that she had", - "never before seen a rabbit with either a waistcoat-pocket, or a watch", - "to take out of it, and burning with curiosity, she ran across the field", - "after it, and fortunately was just in time to see it pop down a large", - "rabbit-hole under the hedge.", - "In another moment down went Alice after it, never once considering how", - "in the world she was to get out again.", - "The rabbit-hole went straight on like a tunnel for some way, and then", - "dipped suddenly down, so suddenly that Alice had not a moment to think", - "about stopping herself before she found herself falling down a very deep", - "well.", - "Either the well was very deep, or she fell very slowly, for she had", - "plenty of time as she went down to look about her and to wonder what was", - "going to happen next. First, she tried to look down and make out what", - "she was coming to, but it was too dark to see anything; then she", - "looked at the sides of the well, and noticed that they were filled with", - "cupboards and book-shelves; here and there she saw maps and pictures", - "hung upon pegs. She took down a jar from one of the shelves as", - "she passed; it was labelled ‘ORANGE MARMALADE’, but to her great", - "disappointment it was empty: she did not like to drop the jar for fear", - "of killing somebody, so managed to put it into one of the cupboards as", - "she fell past it.", - "‘Well!’ thought Alice to herself, ‘after such a fall as this, I shall", - "think nothing of tumbling down stairs! How brave they’ll all think me at", - "home! Why, I wouldn’t say anything about it, even if I fell off the top", - "of the house!’ (Which was very likely true.)", - "Down, down, down. Would the fall NEVER come to an end! ‘I wonder how", - "many miles I’ve fallen by this time?’ she said aloud. ‘I must be getting", - "somewhere near the centre of the earth. Let me see: that would be four", - "thousand miles down, I think--’ (for, you see, Alice had learnt several", - "things of this sort in her lessons in the schoolroom, and though this", - "was not a VERY good opportunity for showing off her knowledge, as there", - "was no one to listen to her, still it was good practice to say it over)", - "‘--yes, that’s about the right distance--but then I wonder what Latitude", - "or Longitude I’ve got to?’ (Alice had no idea what Latitude was, or", - "Longitude either, but thought they were nice grand words to say.)", - "Presently she began again. ‘I wonder if I shall fall right THROUGH the", - "earth! How funny it’ll seem to come out among the people that walk with", - "their heads downward! The Antipathies, I think--’ (she was rather glad", - "there WAS no one listening, this time, as it didn’t sound at all the", - "right word) ‘--but I shall have to ask them what the name of the country", - "is, you know. Please, Ma’am, is this New Zealand or Australia?’ (and", - "she tried to curtsey as she spoke--fancy CURTSEYING as you’re falling", - "through the air! Do you think you could manage it?) ‘And what an", - "ignorant little girl she’ll think me for asking! No, it’ll never do to", - "ask: perhaps I shall see it written up somewhere.’", - "Down, down, down. There was nothing else to do, so Alice soon began", - "talking again. ‘Dinah’ll miss me very much to-night, I should think!’", - "(Dinah was the cat.) ‘I hope they’ll remember her saucer of milk at", - "tea-time. Dinah my dear! I wish you were down here with me! There are no", - "mice in the air, I’m afraid, but you might catch a bat, and that’s very", - "like a mouse, you know. But do cats eat bats, I wonder?’ And here Alice", - "began to get rather sleepy, and went on saying to herself, in a dreamy", - "sort of way, ‘Do cats eat bats? Do cats eat bats?’ and sometimes, ‘Do", - "bats eat cats?’ for, you see, as she couldn’t answer either question,", - "it didn’t much matter which way she put it. She felt that she was dozing", - "off, and had just begun to dream that she was walking hand in hand with", - "Dinah, and saying to her very earnestly, ‘Now, Dinah, tell me the truth:", - "did you ever eat a bat?’ when suddenly, thump! thump! down she came upon", - "a heap of sticks and dry leaves, and the fall was over.", - "Alice was not a bit hurt, and she jumped up on to her feet in a moment:", - "she looked up, but it was all dark overhead; before her was another", - "long passage, and the White Rabbit was still in sight, hurrying down it.", - "There was not a moment to be lost: away went Alice like the wind, and", - "was just in time to hear it say, as it turned a corner, ‘Oh my ears", - "and whiskers, how late it’s getting!’ She was close behind it when she", - "turned the corner, but the Rabbit was no longer to be seen: she found", - "herself in a long, low hall, which was lit up by a row of lamps hanging", - "from the roof.", - "There were doors all round the hall, but they were all locked; and when", - "Alice had been all the way down one side and up the other, trying every", - "door, she walked sadly down the middle, wondering how she was ever to", - "get out again.", - "Suddenly she came upon a little three-legged table, all made of solid", - "glass; there was nothing on it except a tiny golden key, and Alice’s", - "first thought was that it might belong to one of the doors of the hall;", - "but, alas! either the locks were too large, or the key was too small,", - "but at any rate it would not open any of them. However, on the second", - "time round, she came upon a low curtain she had not noticed before, and", - "behind it was a little door about fifteen inches high: she tried the", - "little golden key in the lock, and to her great delight it fitted!", - "Alice opened the door and found that it led into a small passage, not", - "much larger than a rat-hole: she knelt down and looked along the passage", - "into the loveliest garden you ever saw. How she longed to get out of", - "that dark hall, and wander about among those beds of bright flowers and", - "those cool fountains, but she could not even get her head through the", - "doorway; ‘and even if my head would go through,’ thought poor Alice, ‘it", - "would be of very little use without my shoulders. Oh, how I wish I could", - "shut up like a telescope! I think I could, if I only knew how to begin.’", - "For, you see, so many out-of-the-way things had happened lately,", - "that Alice had begun to think that very few things indeed were really", - "impossible.", - "There seemed to be no use in waiting by the little door, so she went", - "back to the table, half hoping she might find another key on it, or at", - "any rate a book of rules for shutting people up like telescopes: this", - "time she found a little bottle on it, [‘which certainly was not here", - "before,’ said Alice,) and round the neck of the bottle was a paper", - "label, with the words ‘DRINK ME’ beautifully printed on it in large", - "letters.", - "It was all very well to say ‘Drink me,’ but the wise little Alice was", - "not going to do THAT in a hurry. ‘No, I’ll look first,’ she said, ‘and", - "see whether it’s marked “poison” or not’; for she had read several nice", - "little histories about children who had got burnt, and eaten up by wild", - "beasts and other unpleasant things, all because they WOULD not remember", - "the simple rules their friends had taught them: such as, that a red-hot", - "poker will burn you if you hold it too long; and that if you cut your", - "finger VERY deeply with a knife, it usually bleeds; and she had never", - "forgotten that, if you drink much from a bottle marked ‘poison,’ it is", - "almost certain to disagree with you, sooner or later.", - "However, this bottle was NOT marked ‘poison,’ so Alice ventured to taste", - "it, and finding it very nice, (it had, in fact, a sort of mixed flavour", - "of cherry-tart, custard, pine-apple, roast turkey, toffee, and hot", - "buttered toast,) she very soon finished it off.", - " * * * * * * *", - " * * * * * *", - " * * * * * * *", - "‘What a curious feeling!’ said Alice; ‘I must be shutting up like a", - "telescope.’", - "And so it was indeed: she was now only ten inches high, and her face", - "brightened up at the thought that she was now the right size for going", - "through the little door into that lovely garden. First, however, she", - "waited for a few minutes to see if she was going to shrink any further:", - "she felt a little nervous about this; ‘for it might end, you know,’ said", - "Alice to herself, ‘in my going out altogether, like a candle. I wonder", - "what I should be like then?’ And she tried to fancy what the flame of a", - "candle is like after the candle is blown out, for she could not remember", - "ever having seen such a thing.", - "After a while, finding that nothing more happened, she decided on going", - "into the garden at once; but, alas for poor Alice! when she got to the", - "door, she found she had forgotten the little golden key, and when she", - "went back to the table for it, she found she could not possibly reach", - "it: she could see it quite plainly through the glass, and she tried her", - "best to climb up one of the legs of the table, but it was too slippery;", - "and when she had tired herself out with trying, the poor little thing", - "sat down and cried.", - "‘Come, there’s no use in crying like that!’ said Alice to herself,", - "rather sharply; ‘I advise you to leave off this minute!’ She generally", - "gave herself very good advice, (though she very seldom followed it),", - "and sometimes she scolded herself so severely as to bring tears into", - "her eyes; and once she remembered trying to box her own ears for having", - "cheated herself in a game of croquet she was playing against herself,", - "for this curious child was very fond of pretending to be two people.", - "‘But it’s no use now,’ thought poor Alice, ‘to pretend to be two people!", - "Why, there’s hardly enough of me left to make ONE respectable person!’", - "Soon her eye fell on a little glass box that was lying under the table:", - "she opened it, and found in it a very small cake, on which the words", - "‘EAT ME’ were beautifully marked in currants. ‘Well, I’ll eat it,’ said", - "Alice, ‘and if it makes me grow larger, I can reach the key; and if it", - "makes me grow smaller, I can creep under the door; so either way I’ll", - "get into the garden, and I don’t care which happens!’", - "She ate a little bit, and said anxiously to herself, ‘Which way? Which", - "way?’, holding her hand on the top of her head to feel which way it was", - "growing, and she was quite surprised to find that she remained the same", - "size: to be sure, this generally happens when one eats cake, but Alice", - "had got so much into the way of expecting nothing but out-of-the-way", - "things to happen, that it seemed quite dull and stupid for life to go on", - "in the common way.", - "So she set to work, and very soon finished off the cake.", - " * * * * * * *", - " * * * * * *", - " * * * * * * *", - "CHAPTER II. The Pool of Tears", - "‘Curiouser and curiouser!’ cried Alice (she was so much surprised, that", - "for the moment she quite forgot how to speak good English); ‘now I’m", - "opening out like the largest telescope that ever was! Good-bye, feet!’", - "(for when she looked down at her feet, they seemed to be almost out of", - "sight, they were getting so far off). ‘Oh, my poor little feet, I wonder", - "who will put on your shoes and stockings for you now, dears? I’m sure", - "_I_ shan’t be able! I shall be a great deal too far off to trouble", - "myself about you: you must manage the best way you can;--but I must be", - "kind to them,’ thought Alice, ‘or perhaps they won’t walk the way I want", - "to go! Let me see: I’ll give them a new pair of boots every Christmas.’", - "And she went on planning to herself how she would manage it. ‘They must", - "go by the carrier,’ she thought; ‘and how funny it’ll seem, sending", - "presents to one’s own feet! And how odd the directions will look!", - " ALICE’S RIGHT FOOT, ESQ.", - " HEARTHRUG,", - " NEAR THE FENDER,", - " (WITH ALICE’S LOVE).", - "Oh dear, what nonsense I’m talking!’", - "Just then her head struck against the roof of the hall: in fact she was", - "now more than nine feet high, and she at once took up the little golden", - "key and hurried off to the garden door.", - "Poor Alice! It was as much as she could do, lying down on one side, to", - "look through into the garden with one eye; but to get through was more", - "hopeless than ever: she sat down and began to cry again.", - "‘You ought to be ashamed of yourself,’ said Alice, ‘a great girl like", - "you,’ (she might well say this), ‘to go on crying in this way! Stop this", - "moment, I tell you!’ But she went on all the same, shedding gallons of", - "tears, until there was a large pool all round her, about four inches", - "deep and reaching half down the hall.", - "After a time she heard a little pattering of feet in the distance, and", - "she hastily dried her eyes to see what was coming. It was the White", - "Rabbit returning, splendidly dressed, with a pair of white kid gloves in", - "one hand and a large fan in the other: he came trotting along in a great", - "hurry, muttering to himself as he came, ‘Oh! the Duchess, the Duchess!", - "Oh! won’t she be savage if I’ve kept her waiting!’ Alice felt so", - "desperate that she was ready to ask help of any one; so, when the Rabbit", - "came near her, she began, in a low, timid voice, ‘If you please, sir--’", - "The Rabbit started violently, dropped the white kid gloves and the fan,", - "and skurried away into the darkness as hard as he could go.", - "Alice took up the fan and gloves, and, as the hall was very hot, she", - "kept fanning herself all the time she went on talking: ‘Dear, dear! How", - "queer everything is to-day! And yesterday things went on just as usual.", - "I wonder if I’ve been changed in the night? Let me think: was I the", - "same when I got up this morning? I almost think I can remember feeling a", - "little different. But if I’m not the same, the next question is, Who", - "in the world am I? Ah, THAT’S the great puzzle!’ And she began thinking", - "over all the children she knew that were of the same age as herself, to", - "see if she could have been changed for any of them.", - "‘I’m sure I’m not Ada,’ she said, ‘for her hair goes in such long", - "ringlets, and mine doesn’t go in ringlets at all; and I’m sure I can’t", - "be Mabel, for I know all sorts of things, and she, oh! she knows such a", - "very little! Besides, SHE’S she, and I’m I, and--oh dear, how puzzling", - "it all is! I’ll try if I know all the things I used to know. Let me", - "see: four times five is twelve, and four times six is thirteen, and", - "four times seven is--oh dear! I shall never get to twenty at that rate!", - "However, the Multiplication Table doesn’t signify: let’s try Geography.", - "London is the capital of Paris, and Paris is the capital of Rome, and", - "Rome--no, THAT’S all wrong, I’m certain! I must have been changed for", - "Mabel! I’ll try and say “How doth the little--“’ and she crossed her", - "hands on her lap as if she were saying lessons, and began to repeat it,", - "but her voice sounded hoarse and strange, and the words did not come the", - "same as they used to do:--", - " ‘How doth the little crocodile", - " Improve his shining tail,", - " And pour the waters of the Nile", - " On every golden scale!", - " ‘How cheerfully he seems to grin,", - " How neatly spread his claws,", - " And welcome little fishes in", - " With gently smiling jaws!’", - "‘I’m sure those are not the right words,’ said poor Alice, and her eyes", - "filled with tears again as she went on, ‘I must be Mabel after all, and", - "I shall have to go and live in that poky little house, and have next to", - "no toys to play with, and oh! ever so many lessons to learn! No, I’ve", - "made up my mind about it; if I’m Mabel, I’ll stay down here! It’ll be no", - "use their putting their heads down and saying “Come up again, dear!” I", - "shall only look up and say “Who am I then? Tell me that first, and then,", - "if I like being that person, I’ll come up: if not, I’ll stay down here", - "till I’m somebody else”--but, oh dear!’ cried Alice, with a sudden burst", - "of tears, ‘I do wish they WOULD put their heads down! I am so VERY tired", - "of being all alone here!’", - "As she said this she looked down at her hands, and was surprised to see", - "that she had put on one of the Rabbit’s little white kid gloves while", - "she was talking. ‘How CAN I have done that?’ she thought. ‘I must", - "be growing small again.’ She got up and went to the table to measure", - "herself by it, and found that, as nearly as she could guess, she was now", - "about two feet high, and was going on shrinking rapidly: she soon found", - "out that the cause of this was the fan she was holding, and she dropped", - "it hastily, just in time to avoid shrinking away altogether.", - "‘That WAS a narrow escape!’ said Alice, a good deal frightened at the", - "sudden change, but very glad to find herself still in existence; ‘and", - "now for the garden!’ and she ran with all speed back to the little door:", - "but, alas! the little door was shut again, and the little golden key was", - "lying on the glass table as before, ‘and things are worse than ever,’", - "thought the poor child, ‘for I never was so small as this before, never!", - "And I declare it’s too bad, that it is!’", - "As she said these words her foot slipped, and in another moment, splash!", - "she was up to her chin in salt water. Her first idea was that she", - "had somehow fallen into the sea, ‘and in that case I can go back by", - "railway,’ she said to herself. (Alice had been to the seaside once in", - "her life, and had come to the general conclusion, that wherever you go", - "to on the English coast you find a number of bathing machines in the", - "sea, some children digging in the sand with wooden spades, then a row", - "of lodging houses, and behind them a railway station.) However, she soon", - "made out that she was in the pool of tears which she had wept when she", - "was nine feet high.", - "‘I wish I hadn’t cried so much!’ said Alice, as she swam about, trying", - "to find her way out. ‘I shall be punished for it now, I suppose, by", - "being drowned in my own tears! That WILL be a queer thing, to be sure!", - "However, everything is queer to-day.’", - "Just then she heard something splashing about in the pool a little way", - "off, and she swam nearer to make out what it was: at first she thought", - "it must be a walrus or hippopotamus, but then she remembered how small", - "she was now, and she soon made out that it was only a mouse that had", - "slipped in like herself.", - "‘Would it be of any use, now,’ thought Alice, ‘to speak to this mouse?", - "Everything is so out-of-the-way down here, that I should think very", - "likely it can talk: at any rate, there’s no harm in trying.’ So she", - "began: ‘O Mouse, do you know the way out of this pool? I am very tired", - "of swimming about here, O Mouse!’ (Alice thought this must be the right", - "way of speaking to a mouse: she had never done such a thing before, but", - "she remembered having seen in her brother’s Latin Grammar, ‘A mouse--of", - "a mouse--to a mouse--a mouse--O mouse!’) The Mouse looked at her rather", - "inquisitively, and seemed to her to wink with one of its little eyes,", - "but it said nothing.", - "‘Perhaps it doesn’t understand English,’ thought Alice; ‘I daresay it’s", - "a French mouse, come over with William the Conqueror.’ (For, with all", - "her knowledge of history, Alice had no very clear notion how long ago", - "anything had happened.) So she began again: ‘Ou est ma chatte?’ which", - "was the first sentence in her French lesson-book. The Mouse gave a", - "sudden leap out of the water, and seemed to quiver all over with fright.", - "‘Oh, I beg your pardon!’ cried Alice hastily, afraid that she had hurt", - "the poor animal’s feelings. ‘I quite forgot you didn’t like cats.’", - "‘Not like cats!’ cried the Mouse, in a shrill, passionate voice. ‘Would", - "YOU like cats if you were me?’", - "‘Well, perhaps not,’ said Alice in a soothing tone: ‘don’t be angry", - "about it. And yet I wish I could show you our cat Dinah: I think you’d", - "take a fancy to cats if you could only see her. She is such a dear quiet", - "thing,’ Alice went on, half to herself, as she swam lazily about in the", - "pool, ‘and she sits purring so nicely by the fire, licking her paws and", - "washing her face--and she is such a nice soft thing to nurse--and she’s", - "such a capital one for catching mice--oh, I beg your pardon!’ cried", - "Alice again, for this time the Mouse was bristling all over, and she", - "felt certain it must be really offended. ‘We won’t talk about her any", - "more if you’d rather not.’", - "‘We indeed!’ cried the Mouse, who was trembling down to the end of his", - "tail. ‘As if I would talk on such a subject! Our family always HATED", - "cats: nasty, low, vulgar things! Don’t let me hear the name again!’", - "‘I won’t indeed!’ said Alice, in a great hurry to change the subject of", - "conversation. ‘Are you--are you fond--of--of dogs?’ The Mouse did not", - "answer, so Alice went on eagerly: ‘There is such a nice little dog near", - "our house I should like to show you! A little bright-eyed terrier, you", - "know, with oh, such long curly brown hair! And it’ll fetch things when", - "you throw them, and it’ll sit up and beg for its dinner, and all sorts", - "of things--I can’t remember half of them--and it belongs to a farmer,", - "you know, and he says it’s so useful, it’s worth a hundred pounds! He", - "says it kills all the rats and--oh dear!’ cried Alice in a sorrowful", - "tone, ‘I’m afraid I’ve offended it again!’ For the Mouse was swimming", - "away from her as hard as it could go, and making quite a commotion in", - "the pool as it went.", - "So she called softly after it, ‘Mouse dear! Do come back again, and we", - "won’t talk about cats or dogs either, if you don’t like them!’ When the", - "Mouse heard this, it turned round and swam slowly back to her: its", - "face was quite pale (with passion, Alice thought), and it said in a low", - "trembling voice, ‘Let us get to the shore, and then I’ll tell you my", - "history, and you’ll understand why it is I hate cats and dogs.’", - "It was high time to go, for the pool was getting quite crowded with the", - "birds and animals that had fallen into it: there were a Duck and a Dodo,", - "a Lory and an Eaglet, and several other curious creatures. Alice led the", - "way, and the whole party swam to the shore.", - "CHAPTER III. A Caucus-Race and a Long Tale", - "They were indeed a queer-looking party that assembled on the bank--the", - "birds with draggled feathers, the animals with their fur clinging close", - "to them, and all dripping wet, cross, and uncomfortable.", - "The first question of course was, how to get dry again: they had a", - "consultation about this, and after a few minutes it seemed quite natural", - "to Alice to find herself talking familiarly with them, as if she had", - "known them all her life. Indeed, she had quite a long argument with the", - "Lory, who at last turned sulky, and would only say, ‘I am older than", - "you, and must know better’; and this Alice would not allow without", - "knowing how old it was, and, as the Lory positively refused to tell its", - "age, there was no more to be said.", - "At last the Mouse, who seemed to be a person of authority among them,", - "called out, ‘Sit down, all of you, and listen to me! I’LL soon make you", - "dry enough!’ They all sat down at once, in a large ring, with the Mouse", - "in the middle. Alice kept her eyes anxiously fixed on it, for she felt", - "sure she would catch a bad cold if she did not get dry very soon.", - "‘Ahem!’ said the Mouse with an important air, ‘are you all ready? This", - "is the driest thing I know. Silence all round, if you please! “William", - "the Conqueror, whose cause was favoured by the pope, was soon submitted", - "to by the English, who wanted leaders, and had been of late much", - "accustomed to usurpation and conquest. Edwin and Morcar, the earls of", - "Mercia and Northumbria--“’", - "‘Ugh!’ said the Lory, with a shiver.", - "‘I beg your pardon!’ said the Mouse, frowning, but very politely: ‘Did", - "you speak?’", - "‘Not I!’ said the Lory hastily.", - "‘I thought you did,’ said the Mouse. ‘--I proceed. “Edwin and Morcar,", - "the earls of Mercia and Northumbria, declared for him: and even Stigand,", - "the patriotic archbishop of Canterbury, found it advisable--“’", - "‘Found WHAT?’ said the Duck.", - "‘Found IT,’ the Mouse replied rather crossly: ‘of course you know what", - "“it” means.’", - "‘I know what “it” means well enough, when I find a thing,’ said the", - "Duck: ‘it’s generally a frog or a worm. The question is, what did the", - "archbishop find?’", - "The Mouse did not notice this question, but hurriedly went on, ‘“--found", - "it advisable to go with Edgar Atheling to meet William and offer him the", - "crown. William’s conduct at first was moderate. But the insolence of his", - "Normans--” How are you getting on now, my dear?’ it continued, turning", - "to Alice as it spoke.", - "‘As wet as ever,’ said Alice in a melancholy tone: ‘it doesn’t seem to", - "dry me at all.’", - "‘In that case,’ said the Dodo solemnly, rising to its feet, ‘I move", - "that the meeting adjourn, for the immediate adoption of more energetic", - "remedies--’", - "‘Speak English!’ said the Eaglet. ‘I don’t know the meaning of half", - "those long words, and, what’s more, I don’t believe you do either!’ And", - "the Eaglet bent down its head to hide a smile: some of the other birds", - "tittered audibly.", - "‘What I was going to say,’ said the Dodo in an offended tone, ‘was, that", - "the best thing to get us dry would be a Caucus-race.’", - "‘What IS a Caucus-race?’ said Alice; not that she wanted much to know,", - "but the Dodo had paused as if it thought that SOMEBODY ought to speak,", - "and no one else seemed inclined to say anything.", - "‘Why,’ said the Dodo, ‘the best way to explain it is to do it.’ (And, as", - "you might like to try the thing yourself, some winter day, I will tell", - "you how the Dodo managed it.)", - "First it marked out a race-course, in a sort of circle, [‘the exact", - "shape doesn’t matter,’ it said,) and then all the party were placed", - "along the course, here and there. There was no ‘One, two, three, and", - "away,’ but they began running when they liked, and left off when they", - "liked, so that it was not easy to know when the race was over. However,", - "when they had been running half an hour or so, and were quite dry again,", - "the Dodo suddenly called out ‘The race is over!’ and they all crowded", - "round it, panting, and asking, ‘But who has won?’", - "This question the Dodo could not answer without a great deal of thought,", - "and it sat for a long time with one finger pressed upon its forehead", - "(the position in which you usually see Shakespeare, in the pictures", - "of him), while the rest waited in silence. At last the Dodo said,", - "‘EVERYBODY has won, and all must have prizes.’", - "‘But who is to give the prizes?’ quite a chorus of voices asked.", - "‘Why, SHE, of course,’ said the Dodo, pointing to Alice with one finger;", - "and the whole party at once crowded round her, calling out in a confused", - "way, ‘Prizes! Prizes!’", - "Alice had no idea what to do, and in despair she put her hand in her", - "pocket, and pulled out a box of comfits, (luckily the salt water had", - "not got into it), and handed them round as prizes. There was exactly one", - "a-piece all round.", - "‘But she must have a prize herself, you know,’ said the Mouse.", - "‘Of course,’ the Dodo replied very gravely. ‘What else have you got in", - "your pocket?’ he went on, turning to Alice.", - "‘Only a thimble,’ said Alice sadly.", - "‘Hand it over here,’ said the Dodo.", - "Then they all crowded round her once more, while the Dodo solemnly", - "presented the thimble, saying ‘We beg your acceptance of this elegant", - "thimble’; and, when it had finished this short speech, they all cheered.", - "Alice thought the whole thing very absurd, but they all looked so grave", - "that she did not dare to laugh; and, as she could not think of anything", - "to say, she simply bowed, and took the thimble, looking as solemn as she", - "could.", - "The next thing was to eat the comfits: this caused some noise and", - "confusion, as the large birds complained that they could not taste", - "theirs, and the small ones choked and had to be patted on the back.", - "However, it was over at last, and they sat down again in a ring, and", - "begged the Mouse to tell them something more.", - "‘You promised to tell me your history, you know,’ said Alice, ‘and why", - "it is you hate--C and D,’ she added in a whisper, half afraid that it", - "would be offended again.", - "‘Mine is a long and a sad tale!’ said the Mouse, turning to Alice, and", - "sighing.", - "‘It IS a long tail, certainly,’ said Alice, looking down with wonder at", - "the Mouse’s tail; ‘but why do you call it sad?’ And she kept on puzzling", - "about it while the Mouse was speaking, so that her idea of the tale was", - "something like this:--", - " ‘Fury said to a", - " mouse, That he", - " met in the", - " house,", - " “Let us", - " both go to", - " law: I will", - " prosecute", - " YOU.--Come,", - " I’ll take no", - " denial; We", - " must have a", - " trial: For", - " really this", - " morning I’ve", - " nothing", - " to do.”", - " Said the", - " mouse to the", - " cur, “Such", - " a trial,", - " dear Sir,", - " With", - " no jury", - " or judge,", - " would be", - " wasting", - " our", - " breath.”", - " “I’ll be", - " judge, I’ll", - " be jury,”", - " Said", - " cunning", - " old Fury:", - " “I’ll", - " try the", - " whole", - " cause,", - " and", - " condemn", - " you", - " to", - " death.”’", - "‘You are not attending!’ said the Mouse to Alice severely. ‘What are you", - "thinking of?’", - "‘I beg your pardon,’ said Alice very humbly: ‘you had got to the fifth", - "bend, I think?’", - "‘I had NOT!’ cried the Mouse, sharply and very angrily.", - "‘A knot!’ said Alice, always ready to make herself useful, and looking", - "anxiously about her. ‘Oh, do let me help to undo it!’", - "‘I shall do nothing of the sort,’ said the Mouse, getting up and walking", - "away. ‘You insult me by talking such nonsense!’", - "‘I didn’t mean it!’ pleaded poor Alice. ‘But you’re so easily offended,", - "you know!’", - "The Mouse only growled in reply.", - "‘Please come back and finish your story!’ Alice called after it; and the", - "others all joined in chorus, ‘Yes, please do!’ but the Mouse only shook", - "its head impatiently, and walked a little quicker.", - "‘What a pity it wouldn’t stay!’ sighed the Lory, as soon as it was quite", - "out of sight; and an old Crab took the opportunity of saying to her", - "daughter ‘Ah, my dear! Let this be a lesson to you never to lose", - "YOUR temper!’ ‘Hold your tongue, Ma!’ said the young Crab, a little", - "snappishly. ‘You’re enough to try the patience of an oyster!’", - "‘I wish I had our Dinah here, I know I do!’ said Alice aloud, addressing", - "nobody in particular. ‘She’d soon fetch it back!’", - "‘And who is Dinah, if I might venture to ask the question?’ said the", - "Lory.", - "Alice replied eagerly, for she was always ready to talk about her pet:", - "‘Dinah’s our cat. And she’s such a capital one for catching mice you", - "can’t think! And oh, I wish you could see her after the birds! Why,", - "she’ll eat a little bird as soon as look at it!’", - "This speech caused a remarkable sensation among the party. Some of the", - "birds hurried off at once: one old Magpie began wrapping itself up very", - "carefully, remarking, ‘I really must be getting home; the night-air", - "doesn’t suit my throat!’ and a Canary called out in a trembling voice to", - "its children, ‘Come away, my dears! It’s high time you were all in bed!’", - "On various pretexts they all moved off, and Alice was soon left alone.", - "‘I wish I hadn’t mentioned Dinah!’ she said to herself in a melancholy", - "tone. ‘Nobody seems to like her, down here, and I’m sure she’s the best", - "cat in the world! Oh, my dear Dinah! I wonder if I shall ever see you", - "any more!’ And here poor Alice began to cry again, for she felt very", - "lonely and low-spirited. In a little while, however, she again heard", - "a little pattering of footsteps in the distance, and she looked up", - "eagerly, half hoping that the Mouse had changed his mind, and was coming", - "back to finish his story.", - "CHAPTER IV. The Rabbit Sends in a Little Bill", - "It was the White Rabbit, trotting slowly back again, and looking", - "anxiously about as it went, as if it had lost something; and she heard", - "it muttering to itself ‘The Duchess! The Duchess! Oh my dear paws! Oh", - "my fur and whiskers! She’ll get me executed, as sure as ferrets are", - "ferrets! Where CAN I have dropped them, I wonder?’ Alice guessed in a", - "moment that it was looking for the fan and the pair of white kid gloves,", - "and she very good-naturedly began hunting about for them, but they were", - "nowhere to be seen--everything seemed to have changed since her swim in", - "the pool, and the great hall, with the glass table and the little door,", - "had vanished completely.", - "Very soon the Rabbit noticed Alice, as she went hunting about, and", - "called out to her in an angry tone, ‘Why, Mary Ann, what ARE you doing", - "out here? Run home this moment, and fetch me a pair of gloves and a fan!", - "Quick, now!’ And Alice was so much frightened that she ran off at once", - "in the direction it pointed to, without trying to explain the mistake it", - "had made.", - "‘He took me for his housemaid,’ she said to herself as she ran. ‘How", - "surprised he’ll be when he finds out who I am! But I’d better take him", - "his fan and gloves--that is, if I can find them.’ As she said this, she", - "came upon a neat little house, on the door of which was a bright brass", - "plate with the name ‘W. RABBIT’ engraved upon it. She went in without", - "knocking, and hurried upstairs, in great fear lest she should meet the", - "real Mary Ann, and be turned out of the house before she had found the", - "fan and gloves.", - "‘How queer it seems,’ Alice said to herself, ‘to be going messages for", - "a rabbit! I suppose Dinah’ll be sending me on messages next!’ And she", - "began fancying the sort of thing that would happen: ‘“Miss Alice! Come", - "here directly, and get ready for your walk!” “Coming in a minute,", - "nurse! But I’ve got to see that the mouse doesn’t get out.” Only I don’t", - "think,’ Alice went on, ‘that they’d let Dinah stop in the house if it", - "began ordering people about like that!’", - "By this time she had found her way into a tidy little room with a table", - "in the window, and on it (as she had hoped) a fan and two or three pairs", - "of tiny white kid gloves: she took up the fan and a pair of the gloves,", - "and was just going to leave the room, when her eye fell upon a little", - "bottle that stood near the looking-glass. There was no label this time", - "with the words ‘DRINK ME,’ but nevertheless she uncorked it and put it", - "to her lips. ‘I know SOMETHING interesting is sure to happen,’ she said", - "to herself, ‘whenever I eat or drink anything; so I’ll just see what", - "this bottle does. I do hope it’ll make me grow large again, for really", - "I’m quite tired of being such a tiny little thing!’", - "It did so indeed, and much sooner than she had expected: before she had", - "drunk half the bottle, she found her head pressing against the ceiling,", - "and had to stoop to save her neck from being broken. She hastily put", - "down the bottle, saying to herself ‘That’s quite enough--I hope I shan’t", - "grow any more--As it is, I can’t get out at the door--I do wish I hadn’t", - "drunk quite so much!’", - "Alas! it was too late to wish that! She went on growing, and growing,", - "and very soon had to kneel down on the floor: in another minute there", - "was not even room for this, and she tried the effect of lying down with", - "one elbow against the door, and the other arm curled round her head.", - "Still she went on growing, and, as a last resource, she put one arm out", - "of the window, and one foot up the chimney, and said to herself ‘Now I", - "can do no more, whatever happens. What WILL become of me?’", - "Luckily for Alice, the little magic bottle had now had its full effect,", - "and she grew no larger: still it was very uncomfortable, and, as there", - "seemed to be no sort of chance of her ever getting out of the room", - "again, no wonder she felt unhappy.", - "‘It was much pleasanter at home,’ thought poor Alice, ‘when one wasn’t", - "always growing larger and smaller, and being ordered about by mice and", - "rabbits. I almost wish I hadn’t gone down that rabbit-hole--and yet--and", - "yet--it’s rather curious, you know, this sort of life! I do wonder what", - "CAN have happened to me! When I used to read fairy-tales, I fancied that", - "kind of thing never happened, and now here I am in the middle of one!", - "There ought to be a book written about me, that there ought! And when I", - "grow up, I’ll write one--but I’m grown up now,’ she added in a sorrowful", - "tone; ‘at least there’s no room to grow up any more HERE.’", - "‘But then,’ thought Alice, ‘shall I NEVER get any older than I am", - "now? That’ll be a comfort, one way--never to be an old woman--but", - "then--always to have lessons to learn! Oh, I shouldn’t like THAT!’", - "‘Oh, you foolish Alice!’ she answered herself. ‘How can you learn", - "lessons in here? Why, there’s hardly room for YOU, and no room at all", - "for any lesson-books!’", - "And so she went on, taking first one side and then the other, and making", - "quite a conversation of it altogether; but after a few minutes she heard", - "a voice outside, and stopped to listen.", - "‘Mary Ann! Mary Ann!’ said the voice. ‘Fetch me my gloves this moment!’", - "Then came a little pattering of feet on the stairs. Alice knew it was", - "the Rabbit coming to look for her, and she trembled till she shook the", - "house, quite forgetting that she was now about a thousand times as large", - "as the Rabbit, and had no reason to be afraid of it.", - "Presently the Rabbit came up to the door, and tried to open it; but, as", - "the door opened inwards, and Alice’s elbow was pressed hard against it,", - "that attempt proved a failure. Alice heard it say to itself ‘Then I’ll", - "go round and get in at the window.’", - "‘THAT you won’t’ thought Alice, and, after waiting till she fancied", - "she heard the Rabbit just under the window, she suddenly spread out her", - "hand, and made a snatch in the air. She did not get hold of anything,", - "but she heard a little shriek and a fall, and a crash of broken glass,", - "from which she concluded that it was just possible it had fallen into a", - "cucumber-frame, or something of the sort.", - "Next came an angry voice--the Rabbit’s--‘Pat! Pat! Where are you?’ And", - "then a voice she had never heard before, ‘Sure then I’m here! Digging", - "for apples, yer honour!’", - "‘Digging for apples, indeed!’ said the Rabbit angrily. ‘Here! Come and", - "help me out of THIS!’ (Sounds of more broken glass.)", - "‘Now tell me, Pat, what’s that in the window?’", - "‘Sure, it’s an arm, yer honour!’ (He pronounced it ‘arrum.’)", - "‘An arm, you goose! Who ever saw one that size? Why, it fills the whole", - "window!’", - "‘Sure, it does, yer honour: but it’s an arm for all that.’", - "‘Well, it’s got no business there, at any rate: go and take it away!’", - "There was a long silence after this, and Alice could only hear whispers", - "now and then; such as, ‘Sure, I don’t like it, yer honour, at all, at", - "all!’ ‘Do as I tell you, you coward!’ and at last she spread out her", - "hand again, and made another snatch in the air. This time there were", - "TWO little shrieks, and more sounds of broken glass. ‘What a number of", - "cucumber-frames there must be!’ thought Alice. ‘I wonder what they’ll do", - "next! As for pulling me out of the window, I only wish they COULD! I’m", - "sure I don’t want to stay in here any longer!’", - "She waited for some time without hearing anything more: at last came a", - "rumbling of little cartwheels, and the sound of a good many voices", - "all talking together: she made out the words: ‘Where’s the other", - "ladder?--Why, I hadn’t to bring but one; Bill’s got the other--Bill!", - "fetch it here, lad!--Here, put ‘em up at this corner--No, tie ‘em", - "together first--they don’t reach half high enough yet--Oh! they’ll", - "do well enough; don’t be particular--Here, Bill! catch hold of this", - "rope--Will the roof bear?--Mind that loose slate--Oh, it’s coming", - "down! Heads below!’ (a loud crash)--‘Now, who did that?--It was Bill, I", - "fancy--Who’s to go down the chimney?--Nay, I shan’t! YOU do it!--That I", - "won’t, then!--Bill’s to go down--Here, Bill! the master says you’re to", - "go down the chimney!’", - "‘Oh! So Bill’s got to come down the chimney, has he?’ said Alice to", - "herself. ‘Shy, they seem to put everything upon Bill! I wouldn’t be in", - "Bill’s place for a good deal: this fireplace is narrow, to be sure; but", - "I THINK I can kick a little!’", - "She drew her foot as far down the chimney as she could, and waited", - "till she heard a little animal (she couldn’t guess of what sort it was)", - "scratching and scrambling about in the chimney close above her: then,", - "saying to herself ‘This is Bill,’ she gave one sharp kick, and waited to", - "see what would happen next.", - "The first thing she heard was a general chorus of ‘There goes Bill!’", - "then the Rabbit’s voice along--‘Catch him, you by the hedge!’ then", - "silence, and then another confusion of voices--‘Hold up his head--Brandy", - "now--Don’t choke him--How was it, old fellow? What happened to you? Tell", - "us all about it!’", - "Last came a little feeble, squeaking voice, [‘That’s Bill,’ thought", - "Alice,) ‘Well, I hardly know--No more, thank ye; I’m better now--but I’m", - "a deal too flustered to tell you--all I know is, something comes at me", - "like a Jack-in-the-box, and up I goes like a sky-rocket!’", - "‘So you did, old fellow!’ said the others.", - "‘We must burn the house down!’ said the Rabbit’s voice; and Alice called", - "out as loud as she could, ‘If you do. I’ll set Dinah at you!’", - "There was a dead silence instantly, and Alice thought to herself, ‘I", - "wonder what they WILL do next! If they had any sense, they’d take the", - "roof off.’ After a minute or two, they began moving about again, and", - "Alice heard the Rabbit say, ‘A barrowful will do, to begin with.’", - "‘A barrowful of WHAT?’ thought Alice; but she had not long to doubt,", - "for the next moment a shower of little pebbles came rattling in at the", - "window, and some of them hit her in the face. ‘I’ll put a stop to this,’", - "she said to herself, and shouted out, ‘You’d better not do that again!’", - "which produced another dead silence.", - "Alice noticed with some surprise that the pebbles were all turning into", - "little cakes as they lay on the floor, and a bright idea came into her", - "head. ‘If I eat one of these cakes,’ she thought, ‘it’s sure to make", - "SOME change in my size; and as it can’t possibly make me larger, it must", - "make me smaller, I suppose.’", - "So she swallowed one of the cakes, and was delighted to find that she", - "began shrinking directly. As soon as she was small enough to get through", - "the door, she ran out of the house, and found quite a crowd of little", - "animals and birds waiting outside. The poor little Lizard, Bill, was", - "in the middle, being held up by two guinea-pigs, who were giving it", - "something out of a bottle. They all made a rush at Alice the moment she", - "appeared; but she ran off as hard as she could, and soon found herself", - "safe in a thick wood.", - "‘The first thing I’ve got to do,’ said Alice to herself, as she wandered", - "about in the wood, ‘is to grow to my right size again; and the second", - "thing is to find my way into that lovely garden. I think that will be", - "the best plan.’", - "It sounded an excellent plan, no doubt, and very neatly and simply", - "arranged; the only difficulty was, that she had not the smallest idea", - "how to set about it; and while she was peering about anxiously among", - "the trees, a little sharp bark just over her head made her look up in a", - "great hurry.", - "An enormous puppy was looking down at her with large round eyes, and", - "feebly stretching out one paw, trying to touch her. ‘Poor little thing!’", - "said Alice, in a coaxing tone, and she tried hard to whistle to it; but", - "she was terribly frightened all the time at the thought that it might be", - "hungry, in which case it would be very likely to eat her up in spite of", - "all her coaxing.", - "Hardly knowing what she did, she picked up a little bit of stick, and", - "held it out to the puppy; whereupon the puppy jumped into the air off", - "all its feet at once, with a yelp of delight, and rushed at the stick,", - "and made believe to worry it; then Alice dodged behind a great thistle,", - "to keep herself from being run over; and the moment she appeared on the", - "other side, the puppy made another rush at the stick, and tumbled head", - "over heels in its hurry to get hold of it; then Alice, thinking it was", - "very like having a game of play with a cart-horse, and expecting every", - "moment to be trampled under its feet, ran round the thistle again; then", - "the puppy began a series of short charges at the stick, running a very", - "little way forwards each time and a long way back, and barking hoarsely", - "all the while, till at last it sat down a good way off, panting, with", - "its tongue hanging out of its mouth, and its great eyes half shut.", - "This seemed to Alice a good opportunity for making her escape; so she", - "set off at once, and ran till she was quite tired and out of breath, and", - "till the puppy’s bark sounded quite faint in the distance.", - "‘And yet what a dear little puppy it was!’ said Alice, as she leant", - "against a buttercup to rest herself, and fanned herself with one of the", - "leaves: ‘I should have liked teaching it tricks very much, if--if I’d", - "only been the right size to do it! Oh dear! I’d nearly forgotten that", - "I’ve got to grow up again! Let me see--how IS it to be managed? I", - "suppose I ought to eat or drink something or other; but the great", - "question is, what?’", - "The great question certainly was, what? Alice looked all round her at", - "the flowers and the blades of grass, but she did not see anything that", - "looked like the right thing to eat or drink under the circumstances.", - "There was a large mushroom growing near her, about the same height as", - "herself; and when she had looked under it, and on both sides of it, and", - "behind it, it occurred to her that she might as well look and see what", - "was on the top of it.", - "She stretched herself up on tiptoe, and peeped over the edge of the", - "mushroom, and her eyes immediately met those of a large caterpillar,", - "that was sitting on the top with its arms folded, quietly smoking a long", - "hookah, and taking not the smallest notice of her or of anything else.", - "CHAPTER V. Advice from a Caterpillar", - "The Caterpillar and Alice looked at each other for some time in silence:", - "at last the Caterpillar took the hookah out of its mouth, and addressed", - "her in a languid, sleepy voice.", - "‘Who are YOU?’ said the Caterpillar.", - "This was not an encouraging opening for a conversation. Alice replied,", - "rather shyly, ‘I--I hardly know, sir, just at present--at least I know", - "who I WAS when I got up this morning, but I think I must have been", - "changed several times since then.’", - "‘What do you mean by that?’ said the Caterpillar sternly. ‘Explain", - "yourself!’", - "‘I can’t explain MYSELF, I’m afraid, sir’ said Alice, ‘because I’m not", - "myself, you see.’", - "‘I don’t see,’ said the Caterpillar.", - "‘I’m afraid I can’t put it more clearly,’ Alice replied very politely,", - "‘for I can’t understand it myself to begin with; and being so many", - "different sizes in a day is very confusing.’", - "‘It isn’t,’ said the Caterpillar.", - "‘Well, perhaps you haven’t found it so yet,’ said Alice; ‘but when you", - "have to turn into a chrysalis--you will some day, you know--and then", - "after that into a butterfly, I should think you’ll feel it a little", - "queer, won’t you?’", - "‘Not a bit,’ said the Caterpillar.", - "‘Well, perhaps your feelings may be different,’ said Alice; ‘all I know", - "is, it would feel very queer to ME.’", - "‘You!’ said the Caterpillar contemptuously. ‘Who are YOU?’", - "Which brought them back again to the beginning of the conversation.", - "Alice felt a little irritated at the Caterpillar’s making such VERY", - "short remarks, and she drew herself up and said, very gravely, ‘I think,", - "you ought to tell me who YOU are, first.’", - "‘Why?’ said the Caterpillar.", - "Here was another puzzling question; and as Alice could not think of any", - "good reason, and as the Caterpillar seemed to be in a VERY unpleasant", - "state of mind, she turned away.", - "‘Come back!’ the Caterpillar called after her. ‘I’ve something important", - "to say!’", - "This sounded promising, certainly: Alice turned and came back again.", - "‘Keep your temper,’ said the Caterpillar.", - "‘Is that all?’ said Alice, swallowing down her anger as well as she", - "could.", - "‘No,’ said the Caterpillar.", - "Alice thought she might as well wait, as she had nothing else to do, and", - "perhaps after all it might tell her something worth hearing. For some", - "minutes it puffed away without speaking, but at last it unfolded its", - "arms, took the hookah out of its mouth again, and said, ‘So you think", - "you’re changed, do you?’", - "‘I’m afraid I am, sir,’ said Alice; ‘I can’t remember things as I", - "used--and I don’t keep the same size for ten minutes together!’", - "‘Can’t remember WHAT things?’ said the Caterpillar.", - "‘Well, I’ve tried to say “HOW DOTH THE LITTLE BUSY BEE,” but it all came", - "different!’ Alice replied in a very melancholy voice.", - "‘Repeat, “YOU ARE OLD, FATHER WILLIAM,”’ said the Caterpillar.", - "Alice folded her hands, and began:--", - " ‘You are old, Father William,’ the young man said,", - " ‘And your hair has become very white;", - " And yet you incessantly stand on your head--", - " Do you think, at your age, it is right?’", - " ‘In my youth,’ Father William replied to his son,", - " ‘I feared it might injure the brain;", - " But, now that I’m perfectly sure I have none,", - " Why, I do it again and again.’", - " ‘You are old,’ said the youth, ‘as I mentioned before,", - " And have grown most uncommonly fat;", - " Yet you turned a back-somersault in at the door--", - " Pray, what is the reason of that?’", - " ‘In my youth,’ said the sage, as he shook his grey locks,", - " ‘I kept all my limbs very supple", - " By the use of this ointment--one shilling the box--", - " Allow me to sell you a couple?’", - " ‘You are old,’ said the youth, ‘and your jaws are too weak", - " For anything tougher than suet;", - " Yet you finished the goose, with the bones and the beak--", - " Pray how did you manage to do it?’", - " ‘In my youth,’ said his father, ‘I took to the law,", - " And argued each case with my wife;", - " And the muscular strength, which it gave to my jaw,", - " Has lasted the rest of my life.’", - " ‘You are old,’ said the youth, ‘one would hardly suppose", - " That your eye was as steady as ever;", - " Yet you balanced an eel on the end of your nose--", - " What made you so awfully clever?’", - " ‘I have answered three questions, and that is enough,’", - " Said his father; ‘don’t give yourself airs!", - " Do you think I can listen all day to such stuff?", - " Be off, or I’ll kick you down stairs!’", - "‘That is not said right,’ said the Caterpillar.", - "‘Not QUITE right, I’m afraid,’ said Alice, timidly; ‘some of the words", - "have got altered.’", - "‘It is wrong from beginning to end,’ said the Caterpillar decidedly, and", - "there was silence for some minutes.", - "The Caterpillar was the first to speak.", - "‘What size do you want to be?’ it asked.", - "‘Oh, I’m not particular as to size,’ Alice hastily replied; ‘only one", - "doesn’t like changing so often, you know.’", - "‘I DON’T know,’ said the Caterpillar.", - "Alice said nothing: she had never been so much contradicted in her life", - "before, and she felt that she was losing her temper.", - "‘Are you content now?’ said the Caterpillar.", - "‘Well, I should like to be a LITTLE larger, sir, if you wouldn’t mind,’", - "said Alice: ‘three inches is such a wretched height to be.’", - "‘It is a very good height indeed!’ said the Caterpillar angrily, rearing", - "itself upright as it spoke (it was exactly three inches high).", - "‘But I’m not used to it!’ pleaded poor Alice in a piteous tone. And", - "she thought of herself, ‘I wish the creatures wouldn’t be so easily", - "offended!’", - "‘You’ll get used to it in time,’ said the Caterpillar; and it put the", - "hookah into its mouth and began smoking again.", - "This time Alice waited patiently until it chose to speak again. In", - "a minute or two the Caterpillar took the hookah out of its mouth", - "and yawned once or twice, and shook itself. Then it got down off the", - "mushroom, and crawled away in the grass, merely remarking as it went,", - "‘One side will make you grow taller, and the other side will make you", - "grow shorter.’", - "‘One side of WHAT? The other side of WHAT?’ thought Alice to herself.", - "‘Of the mushroom,’ said the Caterpillar, just as if she had asked it", - "aloud; and in another moment it was out of sight.", - "Alice remained looking thoughtfully at the mushroom for a minute, trying", - "to make out which were the two sides of it; and as it was perfectly", - "round, she found this a very difficult question. However, at last she", - "stretched her arms round it as far as they would go, and broke off a bit", - "of the edge with each hand.", - "‘And now which is which?’ she said to herself, and nibbled a little of", - "the right-hand bit to try the effect: the next moment she felt a violent", - "blow underneath her chin: it had struck her foot!", - "She was a good deal frightened by this very sudden change, but she felt", - "that there was no time to be lost, as she was shrinking rapidly; so she", - "set to work at once to eat some of the other bit. Her chin was pressed", - "so closely against her foot, that there was hardly room to open her", - "mouth; but she did it at last, and managed to swallow a morsel of the", - "lefthand bit.", - " * * * * * * *", - " * * * * * *", - " * * * * * * *", - "‘Come, my head’s free at last!’ said Alice in a tone of delight, which", - "changed into alarm in another moment, when she found that her shoulders", - "were nowhere to be found: all she could see, when she looked down, was", - "an immense length of neck, which seemed to rise like a stalk out of a", - "sea of green leaves that lay far below her.", - "‘What CAN all that green stuff be?’ said Alice. ‘And where HAVE my", - "shoulders got to? And oh, my poor hands, how is it I can’t see you?’", - "She was moving them about as she spoke, but no result seemed to follow,", - "except a little shaking among the distant green leaves.", - "As there seemed to be no chance of getting her hands up to her head, she", - "tried to get her head down to them, and was delighted to find that her", - "neck would bend about easily in any direction, like a serpent. She had", - "just succeeded in curving it down into a graceful zigzag, and was going", - "to dive in among the leaves, which she found to be nothing but the tops", - "of the trees under which she had been wandering, when a sharp hiss made", - "her draw back in a hurry: a large pigeon had flown into her face, and", - "was beating her violently with its wings.", - "‘Serpent!’ screamed the Pigeon.", - "‘I’m NOT a serpent!’ said Alice indignantly. ‘Let me alone!’", - "‘Serpent, I say again!’ repeated the Pigeon, but in a more subdued tone,", - "and added with a kind of sob, ‘I’ve tried every way, and nothing seems", - "to suit them!’", - "‘I haven’t the least idea what you’re talking about,’ said Alice.", - "‘I’ve tried the roots of trees, and I’ve tried banks, and I’ve tried", - "hedges,’ the Pigeon went on, without attending to her; ‘but those", - "serpents! There’s no pleasing them!’", - "Alice was more and more puzzled, but she thought there was no use in", - "saying anything more till the Pigeon had finished.", - "‘As if it wasn’t trouble enough hatching the eggs,’ said the Pigeon;", - "‘but I must be on the look-out for serpents night and day! Why, I", - "haven’t had a wink of sleep these three weeks!’", - "‘I’m very sorry you’ve been annoyed,’ said Alice, who was beginning to", - "see its meaning.", - "‘And just as I’d taken the highest tree in the wood,’ continued the", - "Pigeon, raising its voice to a shriek, ‘and just as I was thinking I", - "should be free of them at last, they must needs come wriggling down from", - "the sky! Ugh, Serpent!’", - "‘But I’m NOT a serpent, I tell you!’ said Alice. ‘I’m a--I’m a--’", - "‘Well! WHAT are you?’ said the Pigeon. ‘I can see you’re trying to", - "invent something!’", - "‘I--I’m a little girl,’ said Alice, rather doubtfully, as she remembered", - "the number of changes she had gone through that day.", - "‘A likely story indeed!’ said the Pigeon in a tone of the deepest", - "contempt. ‘I’ve seen a good many little girls in my time, but never ONE", - "with such a neck as that! No, no! You’re a serpent; and there’s no use", - "denying it. I suppose you’ll be telling me next that you never tasted an", - "egg!’", - "‘I HAVE tasted eggs, certainly,’ said Alice, who was a very truthful", - "child; ‘but little girls eat eggs quite as much as serpents do, you", - "know.’", - "‘I don’t believe it,’ said the Pigeon; ‘but if they do, why then they’re", - "a kind of serpent, that’s all I can say.’", - "This was such a new idea to Alice, that she was quite silent for a", - "minute or two, which gave the Pigeon the opportunity of adding, ‘You’re", - "looking for eggs, I know THAT well enough; and what does it matter to me", - "whether you’re a little girl or a serpent?’", - "‘It matters a good deal to ME,’ said Alice hastily; ‘but I’m not looking", - "for eggs, as it happens; and if I was, I shouldn’t want YOURS: I don’t", - "like them raw.’", - "‘Well, be off, then!’ said the Pigeon in a sulky tone, as it settled", - "down again into its nest. Alice crouched down among the trees as well as", - "she could, for her neck kept getting entangled among the branches, and", - "every now and then she had to stop and untwist it. After a while she", - "remembered that she still held the pieces of mushroom in her hands, and", - "she set to work very carefully, nibbling first at one and then at the", - "other, and growing sometimes taller and sometimes shorter, until she had", - "succeeded in bringing herself down to her usual height.", - "It was so long since she had been anything near the right size, that it", - "felt quite strange at first; but she got used to it in a few minutes,", - "and began talking to herself, as usual. ‘Come, there’s half my plan done", - "now! How puzzling all these changes are! I’m never sure what I’m going", - "to be, from one minute to another! However, I’ve got back to my right", - "size: the next thing is, to get into that beautiful garden--how IS that", - "to be done, I wonder?’ As she said this, she came suddenly upon an open", - "place, with a little house in it about four feet high. ‘Whoever lives", - "there,’ thought Alice, ‘it’ll never do to come upon them THIS size: why,", - "I should frighten them out of their wits!’ So she began nibbling at the", - "righthand bit again, and did not venture to go near the house till she", - "had brought herself down to nine inches high.", - "CHAPTER VI. Pig and Pepper", - "For a minute or two she stood looking at the house, and wondering what", - "to do next, when suddenly a footman in livery came running out of the", - "wood--(she considered him to be a footman because he was in livery:", - "otherwise, judging by his face only, she would have called him a", - "fish)--and rapped loudly at the door with his knuckles. It was opened", - "by another footman in livery, with a round face, and large eyes like a", - "frog; and both footmen, Alice noticed, had powdered hair that curled all", - "over their heads. She felt very curious to know what it was all about,", - "and crept a little way out of the wood to listen.", - "The Fish-Footman began by producing from under his arm a great letter,", - "nearly as large as himself, and this he handed over to the other,", - "saying, in a solemn tone, ‘For the Duchess. An invitation from the Queen", - "to play croquet.’ The Frog-Footman repeated, in the same solemn tone,", - "only changing the order of the words a little, ‘From the Queen. An", - "invitation for the Duchess to play croquet.’", - "Then they both bowed low, and their curls got entangled together.", - "Alice laughed so much at this, that she had to run back into the", - "wood for fear of their hearing her; and when she next peeped out the", - "Fish-Footman was gone, and the other was sitting on the ground near the", - "door, staring stupidly up into the sky.", - "Alice went timidly up to the door, and knocked.", - "‘There’s no sort of use in knocking,’ said the Footman, ‘and that for", - "two reasons. First, because I’m on the same side of the door as you", - "are; secondly, because they’re making such a noise inside, no one could", - "possibly hear you.’ And certainly there was a most extraordinary noise", - "going on within--a constant howling and sneezing, and every now and then", - "a great crash, as if a dish or kettle had been broken to pieces.", - "‘Please, then,’ said Alice, ‘how am I to get in?’", - "‘There might be some sense in your knocking,’ the Footman went on", - "without attending to her, ‘if we had the door between us. For instance,", - "if you were INSIDE, you might knock, and I could let you out, you know.’", - "He was looking up into the sky all the time he was speaking, and this", - "Alice thought decidedly uncivil. ‘But perhaps he can’t help it,’ she", - "said to herself; ‘his eyes are so VERY nearly at the top of his head.", - "But at any rate he might answer questions.--How am I to get in?’ she", - "repeated, aloud.", - "‘I shall sit here,’ the Footman remarked, ‘till tomorrow--’", - "At this moment the door of the house opened, and a large plate came", - "skimming out, straight at the Footman’s head: it just grazed his nose,", - "and broke to pieces against one of the trees behind him.", - "‘--or next day, maybe,’ the Footman continued in the same tone, exactly", - "as if nothing had happened.", - "‘How am I to get in?’ asked Alice again, in a louder tone.", - "‘ARE you to get in at all?’ said the Footman. ‘That’s the first", - "question, you know.’", - "It was, no doubt: only Alice did not like to be told so. ‘It’s really", - "dreadful,’ she muttered to herself, ‘the way all the creatures argue.", - "It’s enough to drive one crazy!’", - "The Footman seemed to think this a good opportunity for repeating his", - "remark, with variations. ‘I shall sit here,’ he said, ‘on and off, for", - "days and days.’", - "‘But what am I to do?’ said Alice.", - "‘Anything you like,’ said the Footman, and began whistling.", - "‘Oh, there’s no use in talking to him,’ said Alice desperately: ‘he’s", - "perfectly idiotic!’ And she opened the door and went in.", - "The door led right into a large kitchen, which was full of smoke from", - "one end to the other: the Duchess was sitting on a three-legged stool in", - "the middle, nursing a baby; the cook was leaning over the fire, stirring", - "a large cauldron which seemed to be full of soup.", - "‘There’s certainly too much pepper in that soup!’ Alice said to herself,", - "as well as she could for sneezing.", - "There was certainly too much of it in the air. Even the Duchess", - "sneezed occasionally; and as for the baby, it was sneezing and howling", - "alternately without a moment’s pause. The only things in the kitchen", - "that did not sneeze, were the cook, and a large cat which was sitting on", - "the hearth and grinning from ear to ear.", - "‘Please would you tell me,’ said Alice, a little timidly, for she was", - "not quite sure whether it was good manners for her to speak first, ‘why", - "your cat grins like that?’", - "‘It’s a Cheshire cat,’ said the Duchess, ‘and that’s why. Pig!’", - "She said the last word with such sudden violence that Alice quite", - "jumped; but she saw in another moment that it was addressed to the baby,", - "and not to her, so she took courage, and went on again:--", - "‘I didn’t know that Cheshire cats always grinned; in fact, I didn’t know", - "that cats COULD grin.’", - "‘They all can,’ said the Duchess; ‘and most of ‘em do.’", - "‘I don’t know of any that do,’ Alice said very politely, feeling quite", - "pleased to have got into a conversation.", - "‘You don’t know much,’ said the Duchess; ‘and that’s a fact.’", - "Alice did not at all like the tone of this remark, and thought it would", - "be as well to introduce some other subject of conversation. While she", - "was trying to fix on one, the cook took the cauldron of soup off the", - "fire, and at once set to work throwing everything within her reach at", - "the Duchess and the baby--the fire-irons came first; then followed a", - "shower of saucepans, plates, and dishes. The Duchess took no notice of", - "them even when they hit her; and the baby was howling so much already,", - "that it was quite impossible to say whether the blows hurt it or not.", - "‘Oh, PLEASE mind what you’re doing!’ cried Alice, jumping up and down in", - "an agony of terror. ‘Oh, there goes his PRECIOUS nose’; as an unusually", - "large saucepan flew close by it, and very nearly carried it off.", - "‘If everybody minded their own business,’ the Duchess said in a hoarse", - "growl, ‘the world would go round a deal faster than it does.’", - "‘Which would NOT be an advantage,’ said Alice, who felt very glad to get", - "an opportunity of showing off a little of her knowledge. ‘Just think of", - "what work it would make with the day and night! You see the earth takes", - "twenty-four hours to turn round on its axis--’", - "‘Talking of axes,’ said the Duchess, ‘chop off her head!’", - "Alice glanced rather anxiously at the cook, to see if she meant to take", - "the hint; but the cook was busily stirring the soup, and seemed not to", - "be listening, so she went on again: ‘Twenty-four hours, I THINK; or is", - "it twelve? I--’", - "‘Oh, don’t bother ME,’ said the Duchess; ‘I never could abide figures!’", - "And with that she began nursing her child again, singing a sort of", - "lullaby to it as she did so, and giving it a violent shake at the end of", - "every line:", - " ‘Speak roughly to your little boy,", - " And beat him when he sneezes:", - " He only does it to annoy,", - " Because he knows it teases.’", - " CHORUS.", - " (In which the cook and the baby joined):--", - " ‘Wow! wow! wow!’", - "While the Duchess sang the second verse of the song, she kept tossing", - "the baby violently up and down, and the poor little thing howled so,", - "that Alice could hardly hear the words:--", - " ‘I speak severely to my boy,", - " I beat him when he sneezes;", - " For he can thoroughly enjoy", - " The pepper when he pleases!’", - " CHORUS.", - " ‘Wow! wow! wow!’", - "‘Here! you may nurse it a bit, if you like!’ the Duchess said to Alice,", - "flinging the baby at her as she spoke. ‘I must go and get ready to play", - "croquet with the Queen,’ and she hurried out of the room. The cook threw", - "a frying-pan after her as she went out, but it just missed her.", - "Alice caught the baby with some difficulty, as it was a queer-shaped", - "little creature, and held out its arms and legs in all directions, ‘just", - "like a star-fish,’ thought Alice. The poor little thing was snorting", - "like a steam-engine when she caught it, and kept doubling itself up and", - "straightening itself out again, so that altogether, for the first minute", - "or two, it was as much as she could do to hold it.", - "As soon as she had made out the proper way of nursing it, (which was to", - "twist it up into a sort of knot, and then keep tight hold of its right", - "ear and left foot, so as to prevent its undoing itself,) she carried", - "it out into the open air. ‘IF I don’t take this child away with me,’", - "thought Alice, ‘they’re sure to kill it in a day or two: wouldn’t it be", - "murder to leave it behind?’ She said the last words out loud, and the", - "little thing grunted in reply (it had left off sneezing by this time).", - "‘Don’t grunt,’ said Alice; ‘that’s not at all a proper way of expressing", - "yourself.’", - "The baby grunted again, and Alice looked very anxiously into its face to", - "see what was the matter with it. There could be no doubt that it had", - "a VERY turn-up nose, much more like a snout than a real nose; also its", - "eyes were getting extremely small for a baby: altogether Alice did not", - "like the look of the thing at all. ‘But perhaps it was only sobbing,’", - "she thought, and looked into its eyes again, to see if there were any", - "tears.", - "No, there were no tears. ‘If you’re going to turn into a pig, my dear,’", - "said Alice, seriously, ‘I’ll have nothing more to do with you. Mind", - "now!’ The poor little thing sobbed again (or grunted, it was impossible", - "to say which), and they went on for some while in silence.", - "Alice was just beginning to think to herself, ‘Now, what am I to do with", - "this creature when I get it home?’ when it grunted again, so violently,", - "that she looked down into its face in some alarm. This time there could", - "be NO mistake about it: it was neither more nor less than a pig, and she", - "felt that it would be quite absurd for her to carry it further.", - "So she set the little creature down, and felt quite relieved to see", - "it trot away quietly into the wood. ‘If it had grown up,’ she said", - "to herself, ‘it would have made a dreadfully ugly child: but it makes", - "rather a handsome pig, I think.’ And she began thinking over other", - "children she knew, who might do very well as pigs, and was just saying", - "to herself, ‘if one only knew the right way to change them--’ when she", - "was a little startled by seeing the Cheshire Cat sitting on a bough of a", - "tree a few yards off.", - "The Cat only grinned when it saw Alice. It looked good-natured, she", - "thought: still it had VERY long claws and a great many teeth, so she", - "felt that it ought to be treated with respect.", - "‘Cheshire Puss,’ she began, rather timidly, as she did not at all know", - "whether it would like the name: however, it only grinned a little wider.", - "‘Come, it’s pleased so far,’ thought Alice, and she went on. ‘Would you", - "tell me, please, which way I ought to go from here?’", - "‘That depends a good deal on where you want to get to,’ said the Cat.", - "‘I don’t much care where--’ said Alice.", - "‘Then it doesn’t matter which way you go,’ said the Cat.", - "‘--so long as I get SOMEWHERE,’ Alice added as an explanation.", - "‘Oh, you’re sure to do that,’ said the Cat, ‘if you only walk long", - "enough.’", - "Alice felt that this could not be denied, so she tried another question.", - "‘What sort of people live about here?’", - "‘In THAT direction,’ the Cat said, waving its right paw round, ‘lives", - "a Hatter: and in THAT direction,’ waving the other paw, ‘lives a March", - "Hare. Visit either you like: they’re both mad.’", - "‘But I don’t want to go among mad people,’ Alice remarked.", - "‘Oh, you can’t help that,’ said the Cat: ‘we’re all mad here. I’m mad.", - "You’re mad.’", - "‘How do you know I’m mad?’ said Alice.", - "‘You must be,’ said the Cat, ‘or you wouldn’t have come here.’", - "Alice didn’t think that proved it at all; however, she went on ‘And how", - "do you know that you’re mad?’", - "‘To begin with,’ said the Cat, ‘a dog’s not mad. You grant that?’", - "‘I suppose so,’ said Alice.", - "‘Well, then,’ the Cat went on, ‘you see, a dog growls when it’s angry,", - "and wags its tail when it’s pleased. Now I growl when I’m pleased, and", - "wag my tail when I’m angry. Therefore I’m mad.’", - "‘I call it purring, not growling,’ said Alice.", - "‘Call it what you like,’ said the Cat. ‘Do you play croquet with the", - "Queen to-day?’", - "‘I should like it very much,’ said Alice, ‘but I haven’t been invited", - "yet.’", - "‘You’ll see me there,’ said the Cat, and vanished.", - "Alice was not much surprised at this, she was getting so used to queer", - "things happening. While she was looking at the place where it had been,", - "it suddenly appeared again.", - "‘By-the-bye, what became of the baby?’ said the Cat. ‘I’d nearly", - "forgotten to ask.’", - "‘It turned into a pig,’ Alice quietly said, just as if it had come back", - "in a natural way.", - "‘I thought it would,’ said the Cat, and vanished again.", - "Alice waited a little, half expecting to see it again, but it did not", - "appear, and after a minute or two she walked on in the direction in", - "which the March Hare was said to live. ‘I’ve seen hatters before,’ she", - "said to herself; ‘the March Hare will be much the most interesting, and", - "perhaps as this is May it won’t be raving mad--at least not so mad as", - "it was in March.’ As she said this, she looked up, and there was the Cat", - "again, sitting on a branch of a tree.", - "‘Did you say pig, or fig?’ said the Cat.", - "‘I said pig,’ replied Alice; ‘and I wish you wouldn’t keep appearing and", - "vanishing so suddenly: you make one quite giddy.’", - "‘All right,’ said the Cat; and this time it vanished quite slowly,", - "beginning with the end of the tail, and ending with the grin, which", - "remained some time after the rest of it had gone.", - "‘Well! I’ve often seen a cat without a grin,’ thought Alice; ‘but a grin", - "without a cat! It’s the most curious thing I ever saw in my life!’", - "She had not gone much farther before she came in sight of the house", - "of the March Hare: she thought it must be the right house, because the", - "chimneys were shaped like ears and the roof was thatched with fur. It", - "was so large a house, that she did not like to go nearer till she had", - "nibbled some more of the lefthand bit of mushroom, and raised herself to", - "about two feet high: even then she walked up towards it rather timidly,", - "saying to herself ‘Suppose it should be raving mad after all! I almost", - "wish I’d gone to see the Hatter instead!’", - "CHAPTER VII. A Mad Tea-Party", - "There was a table set out under a tree in front of the house, and the", - "March Hare and the Hatter were having tea at it: a Dormouse was sitting", - "between them, fast asleep, and the other two were using it as a", - "cushion, resting their elbows on it, and talking over its head. ‘Very", - "uncomfortable for the Dormouse,’ thought Alice; ‘only, as it’s asleep, I", - "suppose it doesn’t mind.’", - "The table was a large one, but the three were all crowded together at", - "one corner of it: ‘No room! No room!’ they cried out when they saw Alice", - "coming. ‘There’s PLENTY of room!’ said Alice indignantly, and she sat", - "down in a large arm-chair at one end of the table.", - "‘Have some wine,’ the March Hare said in an encouraging tone.", - "Alice looked all round the table, but there was nothing on it but tea.", - "‘I don’t see any wine,’ she remarked.", - "‘There isn’t any,’ said the March Hare.", - "‘Then it wasn’t very civil of you to offer it,’ said Alice angrily.", - "‘It wasn’t very civil of you to sit down without being invited,’ said", - "the March Hare.", - "‘I didn’t know it was YOUR table,’ said Alice; ‘it’s laid for a great", - "many more than three.’", - "‘Your hair wants cutting,’ said the Hatter. He had been looking at Alice", - "for some time with great curiosity, and this was his first speech.", - "‘You should learn not to make personal remarks,’ Alice said with some", - "severity; ‘it’s very rude.’", - "The Hatter opened his eyes very wide on hearing this; but all he SAID", - "was, ‘Why is a raven like a writing-desk?’", - "‘Come, we shall have some fun now!’ thought Alice. ‘I’m glad they’ve", - "begun asking riddles.--I believe I can guess that,’ she added aloud.", - "‘Do you mean that you think you can find out the answer to it?’ said the", - "March Hare.", - "‘Exactly so,’ said Alice.", - "‘Then you should say what you mean,’ the March Hare went on.", - "‘I do,’ Alice hastily replied; ‘at least--at least I mean what I", - "say--that’s the same thing, you know.’", - "‘Not the same thing a bit!’ said the Hatter. ‘You might just as well say", - "that “I see what I eat” is the same thing as “I eat what I see”!’", - "‘You might just as well say,’ added the March Hare, ‘that “I like what I", - "get” is the same thing as “I get what I like”!’", - "‘You might just as well say,’ added the Dormouse, who seemed to be", - "talking in his sleep, ‘that “I breathe when I sleep” is the same thing", - "as “I sleep when I breathe”!’", - "‘It IS the same thing with you,’ said the Hatter, and here the", - "conversation dropped, and the party sat silent for a minute, while Alice", - "thought over all she could remember about ravens and writing-desks,", - "which wasn’t much.", - "The Hatter was the first to break the silence. ‘What day of the month", - "is it?’ he said, turning to Alice: he had taken his watch out of his", - "pocket, and was looking at it uneasily, shaking it every now and then,", - "and holding it to his ear.", - "Alice considered a little, and then said ‘The fourth.’", - "‘Two days wrong!’ sighed the Hatter. ‘I told you butter wouldn’t suit", - "the works!’ he added looking angrily at the March Hare.", - "‘It was the BEST butter,’ the March Hare meekly replied.", - "‘Yes, but some crumbs must have got in as well,’ the Hatter grumbled:", - "‘you shouldn’t have put it in with the bread-knife.’", - "The March Hare took the watch and looked at it gloomily: then he dipped", - "it into his cup of tea, and looked at it again: but he could think of", - "nothing better to say than his first remark, ‘It was the BEST butter,", - "you know.’", - "Alice had been looking over his shoulder with some curiosity. ‘What a", - "funny watch!’ she remarked. ‘It tells the day of the month, and doesn’t", - "tell what o’clock it is!’", - "‘Why should it?’ muttered the Hatter. ‘Does YOUR watch tell you what", - "year it is?’", - "‘Of course not,’ Alice replied very readily: ‘but that’s because it", - "stays the same year for such a long time together.’", - "‘Which is just the case with MINE,’ said the Hatter.", - "Alice felt dreadfully puzzled. The Hatter’s remark seemed to have no", - "sort of meaning in it, and yet it was certainly English. ‘I don’t quite", - "understand you,’ she said, as politely as she could.", - "‘The Dormouse is asleep again,’ said the Hatter, and he poured a little", - "hot tea upon its nose.", - "The Dormouse shook its head impatiently, and said, without opening its", - "eyes, ‘Of course, of course; just what I was going to remark myself.’", - "‘Have you guessed the riddle yet?’ the Hatter said, turning to Alice", - "again.", - "‘No, I give it up,’ Alice replied: ‘what’s the answer?’", - "‘I haven’t the slightest idea,’ said the Hatter.", - "‘Nor I,’ said the March Hare.", - "Alice sighed wearily. ‘I think you might do something better with the", - "time,’ she said, ‘than waste it in asking riddles that have no answers.’", - "‘If you knew Time as well as I do,’ said the Hatter, ‘you wouldn’t talk", - "about wasting IT. It’s HIM.’", - "‘I don’t know what you mean,’ said Alice.", - "‘Of course you don’t!’ the Hatter said, tossing his head contemptuously.", - "‘I dare say you never even spoke to Time!’", - "‘Perhaps not,’ Alice cautiously replied: ‘but I know I have to beat time", - "when I learn music.’", - "‘Ah! that accounts for it,’ said the Hatter. ‘He won’t stand beating.", - "Now, if you only kept on good terms with him, he’d do almost anything", - "you liked with the clock. For instance, suppose it were nine o’clock in", - "the morning, just time to begin lessons: you’d only have to whisper a", - "hint to Time, and round goes the clock in a twinkling! Half-past one,", - "time for dinner!’", - "[‘I only wish it was,’ the March Hare said to itself in a whisper.)", - "‘That would be grand, certainly,’ said Alice thoughtfully: ‘but then--I", - "shouldn’t be hungry for it, you know.’", - "‘Not at first, perhaps,’ said the Hatter: ‘but you could keep it to", - "half-past one as long as you liked.’", - "‘Is that the way YOU manage?’ Alice asked.", - "The Hatter shook his head mournfully. ‘Not I!’ he replied. ‘We", - "quarrelled last March--just before HE went mad, you know--’ (pointing", - "with his tea spoon at the March Hare,) ‘--it was at the great concert", - "given by the Queen of Hearts, and I had to sing", - " “Twinkle, twinkle, little bat!", - " How I wonder what you’re at!”", - "You know the song, perhaps?’", - "‘I’ve heard something like it,’ said Alice.", - "‘It goes on, you know,’ the Hatter continued, ‘in this way:--", - " “Up above the world you fly,", - " Like a tea-tray in the sky.", - " Twinkle, twinkle--“’", - "Here the Dormouse shook itself, and began singing in its sleep ‘Twinkle,", - "twinkle, twinkle, twinkle--’ and went on so long that they had to pinch", - "it to make it stop.", - "‘Well, I’d hardly finished the first verse,’ said the Hatter, ‘when the", - "Queen jumped up and bawled out, “He’s murdering the time! Off with his", - "head!”’", - "‘How dreadfully savage!’ exclaimed Alice.", - "‘And ever since that,’ the Hatter went on in a mournful tone, ‘he won’t", - "do a thing I ask! It’s always six o’clock now.’", - "A bright idea came into Alice’s head. ‘Is that the reason so many", - "tea-things are put out here?’ she asked.", - "‘Yes, that’s it,’ said the Hatter with a sigh: ‘it’s always tea-time,", - "and we’ve no time to wash the things between whiles.’", - "‘Then you keep moving round, I suppose?’ said Alice.", - "‘Exactly so,’ said the Hatter: ‘as the things get used up.’", - "‘But what happens when you come to the beginning again?’ Alice ventured", - "to ask.", - "‘Suppose we change the subject,’ the March Hare interrupted, yawning.", - "‘I’m getting tired of this. I vote the young lady tells us a story.’", - "‘I’m afraid I don’t know one,’ said Alice, rather alarmed at the", - "proposal.", - "‘Then the Dormouse shall!’ they both cried. ‘Wake up, Dormouse!’ And", - "they pinched it on both sides at once.", - "The Dormouse slowly opened his eyes. ‘I wasn’t asleep,’ he said in a", - "hoarse, feeble voice: ‘I heard every word you fellows were saying.’", - "‘Tell us a story!’ said the March Hare.", - "‘Yes, please do!’ pleaded Alice.", - "‘And be quick about it,’ added the Hatter, ‘or you’ll be asleep again", - "before it’s done.’", - "‘Once upon a time there were three little sisters,’ the Dormouse began", - "in a great hurry; ‘and their names were Elsie, Lacie, and Tillie; and", - "they lived at the bottom of a well--’", - "‘What did they live on?’ said Alice, who always took a great interest in", - "questions of eating and drinking.", - "‘They lived on treacle,’ said the Dormouse, after thinking a minute or", - "two.", - "‘They couldn’t have done that, you know,’ Alice gently remarked; ‘they’d", - "have been ill.’", - "‘So they were,’ said the Dormouse; ‘VERY ill.’", - "Alice tried to fancy to herself what such an extraordinary ways of", - "living would be like, but it puzzled her too much, so she went on: ‘But", - "why did they live at the bottom of a well?’", - "‘Take some more tea,’ the March Hare said to Alice, very earnestly.", - "‘I’ve had nothing yet,’ Alice replied in an offended tone, ‘so I can’t", - "take more.’", - "‘You mean you can’t take LESS,’ said the Hatter: ‘it’s very easy to take", - "MORE than nothing.’", - "‘Nobody asked YOUR opinion,’ said Alice.", - "‘Who’s making personal remarks now?’ the Hatter asked triumphantly.", - "Alice did not quite know what to say to this: so she helped herself", - "to some tea and bread-and-butter, and then turned to the Dormouse, and", - "repeated her question. ‘Why did they live at the bottom of a well?’", - "The Dormouse again took a minute or two to think about it, and then", - "said, ‘It was a treacle-well.’", - "‘There’s no such thing!’ Alice was beginning very angrily, but the", - "Hatter and the March Hare went ‘Sh! sh!’ and the Dormouse sulkily", - "remarked, ‘If you can’t be civil, you’d better finish the story for", - "yourself.’", - "‘No, please go on!’ Alice said very humbly; ‘I won’t interrupt again. I", - "dare say there may be ONE.’", - "‘One, indeed!’ said the Dormouse indignantly. However, he consented to", - "go on. ‘And so these three little sisters--they were learning to draw,", - "you know--’", - "‘What did they draw?’ said Alice, quite forgetting her promise.", - "‘Treacle,’ said the Dormouse, without considering at all this time.", - "‘I want a clean cup,’ interrupted the Hatter: ‘let’s all move one place", - "on.’", - "He moved on as he spoke, and the Dormouse followed him: the March Hare", - "moved into the Dormouse’s place, and Alice rather unwillingly took", - "the place of the March Hare. The Hatter was the only one who got any", - "advantage from the change: and Alice was a good deal worse off than", - "before, as the March Hare had just upset the milk-jug into his plate.", - "Alice did not wish to offend the Dormouse again, so she began very", - "cautiously: ‘But I don’t understand. Where did they draw the treacle", - "from?’", - "‘You can draw water out of a water-well,’ said the Hatter; ‘so I should", - "think you could draw treacle out of a treacle-well--eh, stupid?’", - "‘But they were IN the well,’ Alice said to the Dormouse, not choosing to", - "notice this last remark.", - "‘Of course they were’, said the Dormouse; ‘--well in.’", - "This answer so confused poor Alice, that she let the Dormouse go on for", - "some time without interrupting it.", - "‘They were learning to draw,’ the Dormouse went on, yawning and rubbing", - "its eyes, for it was getting very sleepy; ‘and they drew all manner of", - "things--everything that begins with an M--’", - "‘Why with an M?’ said Alice.", - "‘Why not?’ said the March Hare.", - "Alice was silent.", - "The Dormouse had closed its eyes by this time, and was going off into", - "a doze; but, on being pinched by the Hatter, it woke up again with", - "a little shriek, and went on: ‘--that begins with an M, such as", - "mouse-traps, and the moon, and memory, and muchness--you know you say", - "things are “much of a muchness”--did you ever see such a thing as a", - "drawing of a muchness?’", - "‘Really, now you ask me,’ said Alice, very much confused, ‘I don’t", - "think--’", - "‘Then you shouldn’t talk,’ said the Hatter.", - "This piece of rudeness was more than Alice could bear: she got up in", - "great disgust, and walked off; the Dormouse fell asleep instantly, and", - "neither of the others took the least notice of her going, though she", - "looked back once or twice, half hoping that they would call after her:", - "the last time she saw them, they were trying to put the Dormouse into", - "the teapot.", - "‘At any rate I’ll never go THERE again!’ said Alice as she picked her", - "way through the wood. ‘It’s the stupidest tea-party I ever was at in all", - "my life!’", - "Just as she said this, she noticed that one of the trees had a door", - "leading right into it. ‘That’s very curious!’ she thought. ‘But", - "everything’s curious today. I think I may as well go in at once.’ And in", - "she went.", - "Once more she found herself in the long hall, and close to the little", - "glass table. ‘Now, I’ll manage better this time,’ she said to herself,", - "and began by taking the little golden key, and unlocking the door that", - "led into the garden. Then she went to work nibbling at the mushroom (she", - "had kept a piece of it in her pocket) till she was about a foot high:", - "then she walked down the little passage: and THEN--she found herself at", - "last in the beautiful garden, among the bright flower-beds and the cool", - "fountains.", - "CHAPTER VIII. The Queen’s Croquet-Ground", - "A large rose-tree stood near the entrance of the garden: the roses", - "growing on it were white, but there were three gardeners at it, busily", - "painting them red. Alice thought this a very curious thing, and she went", - "nearer to watch them, and just as she came up to them she heard one of", - "them say, ‘Look out now, Five! Don’t go splashing paint over me like", - "that!’", - "‘I couldn’t help it,’ said Five, in a sulky tone; ‘Seven jogged my", - "elbow.’", - "On which Seven looked up and said, ‘That’s right, Five! Always lay the", - "blame on others!’", - "‘YOU’D better not talk!’ said Five. ‘I heard the Queen say only", - "yesterday you deserved to be beheaded!’", - "‘What for?’ said the one who had spoken first.", - "‘That’s none of YOUR business, Two!’ said Seven.", - "‘Yes, it IS his business!’ said Five, ‘and I’ll tell him--it was for", - "bringing the cook tulip-roots instead of onions.’", - "Seven flung down his brush, and had just begun ‘Well, of all the unjust", - "things--’ when his eye chanced to fall upon Alice, as she stood watching", - "them, and he checked himself suddenly: the others looked round also, and", - "all of them bowed low.", - "‘Would you tell me,’ said Alice, a little timidly, ‘why you are painting", - "those roses?’", - "Five and Seven said nothing, but looked at Two. Two began in a low", - "voice, ‘Why the fact is, you see, Miss, this here ought to have been a", - "RED rose-tree, and we put a white one in by mistake; and if the Queen", - "was to find it out, we should all have our heads cut off, you know.", - "So you see, Miss, we’re doing our best, afore she comes, to--’ At this", - "moment Five, who had been anxiously looking across the garden, called", - "out ‘The Queen! The Queen!’ and the three gardeners instantly threw", - "themselves flat upon their faces. There was a sound of many footsteps,", - "and Alice looked round, eager to see the Queen.", - "First came ten soldiers carrying clubs; these were all shaped like", - "the three gardeners, oblong and flat, with their hands and feet at the", - "corners: next the ten courtiers; these were ornamented all over with", - "diamonds, and walked two and two, as the soldiers did. After these came", - "the royal children; there were ten of them, and the little dears came", - "jumping merrily along hand in hand, in couples: they were all ornamented", - "with hearts. Next came the guests, mostly Kings and Queens, and among", - "them Alice recognised the White Rabbit: it was talking in a hurried", - "nervous manner, smiling at everything that was said, and went by without", - "noticing her. Then followed the Knave of Hearts, carrying the King’s", - "crown on a crimson velvet cushion; and, last of all this grand", - "procession, came THE KING AND QUEEN OF HEARTS.", - "Alice was rather doubtful whether she ought not to lie down on her face", - "like the three gardeners, but she could not remember ever having heard", - "of such a rule at processions; ‘and besides, what would be the use of", - "a procession,’ thought she, ‘if people had all to lie down upon their", - "faces, so that they couldn’t see it?’ So she stood still where she was,", - "and waited.", - "When the procession came opposite to Alice, they all stopped and looked", - "at her, and the Queen said severely ‘Who is this?’ She said it to the", - "Knave of Hearts, who only bowed and smiled in reply.", - "‘Idiot!’ said the Queen, tossing her head impatiently; and, turning to", - "Alice, she went on, ‘What’s your name, child?’", - "‘My name is Alice, so please your Majesty,’ said Alice very politely;", - "but she added, to herself, ‘Why, they’re only a pack of cards, after", - "all. I needn’t be afraid of them!’", - "‘And who are THESE?’ said the Queen, pointing to the three gardeners who", - "were lying round the rosetree; for, you see, as they were lying on their", - "faces, and the pattern on their backs was the same as the rest of the", - "pack, she could not tell whether they were gardeners, or soldiers, or", - "courtiers, or three of her own children.", - "‘How should I know?’ said Alice, surprised at her own courage. ‘It’s no", - "business of MINE.’", - "The Queen turned crimson with fury, and, after glaring at her for a", - "moment like a wild beast, screamed ‘Off with her head! Off--’", - "‘Nonsense!’ said Alice, very loudly and decidedly, and the Queen was", - "silent.", - "The King laid his hand upon her arm, and timidly said ‘Consider, my", - "dear: she is only a child!’", - "The Queen turned angrily away from him, and said to the Knave ‘Turn them", - "over!’", - "The Knave did so, very carefully, with one foot.", - "‘Get up!’ said the Queen, in a shrill, loud voice, and the three", - "gardeners instantly jumped up, and began bowing to the King, the Queen,", - "the royal children, and everybody else.", - "‘Leave off that!’ screamed the Queen. ‘You make me giddy.’ And then,", - "turning to the rose-tree, she went on, ‘What HAVE you been doing here?’", - "‘May it please your Majesty,’ said Two, in a very humble tone, going", - "down on one knee as he spoke, ‘we were trying--’", - "‘I see!’ said the Queen, who had meanwhile been examining the roses.", - "‘Off with their heads!’ and the procession moved on, three of the", - "soldiers remaining behind to execute the unfortunate gardeners, who ran", - "to Alice for protection.", - "‘You shan’t be beheaded!’ said Alice, and she put them into a large", - "flower-pot that stood near. The three soldiers wandered about for a", - "minute or two, looking for them, and then quietly marched off after the", - "others.", - "‘Are their heads off?’ shouted the Queen.", - "‘Their heads are gone, if it please your Majesty!’ the soldiers shouted", - "in reply.", - "‘That’s right!’ shouted the Queen. ‘Can you play croquet?’", - "The soldiers were silent, and looked at Alice, as the question was", - "evidently meant for her.", - "‘Yes!’ shouted Alice.", - "‘Come on, then!’ roared the Queen, and Alice joined the procession,", - "wondering very much what would happen next.", - "‘It’s--it’s a very fine day!’ said a timid voice at her side. She was", - "walking by the White Rabbit, who was peeping anxiously into her face.", - "‘Very,’ said Alice: ‘--where’s the Duchess?’", - "‘Hush! Hush!’ said the Rabbit in a low, hurried tone. He looked", - "anxiously over his shoulder as he spoke, and then raised himself upon", - "tiptoe, put his mouth close to her ear, and whispered ‘She’s under", - "sentence of execution.’", - "‘What for?’ said Alice.", - "‘Did you say “What a pity!”?’ the Rabbit asked.", - "‘No, I didn’t,’ said Alice: ‘I don’t think it’s at all a pity. I said", - "“What for?”’", - "‘She boxed the Queen’s ears--’ the Rabbit began. Alice gave a little", - "scream of laughter. ‘Oh, hush!’ the Rabbit whispered in a frightened", - "tone. ‘The Queen will hear you! You see, she came rather late, and the", - "Queen said--’", - "‘Get to your places!’ shouted the Queen in a voice of thunder, and", - "people began running about in all directions, tumbling up against each", - "other; however, they got settled down in a minute or two, and the game", - "began. Alice thought she had never seen such a curious croquet-ground in", - "her life; it was all ridges and furrows; the balls were live hedgehogs,", - "the mallets live flamingoes, and the soldiers had to double themselves", - "up and to stand on their hands and feet, to make the arches.", - "The chief difficulty Alice found at first was in managing her flamingo:", - "she succeeded in getting its body tucked away, comfortably enough, under", - "her arm, with its legs hanging down, but generally, just as she had got", - "its neck nicely straightened out, and was going to give the hedgehog a", - "blow with its head, it WOULD twist itself round and look up in her face,", - "with such a puzzled expression that she could not help bursting out", - "laughing: and when she had got its head down, and was going to begin", - "again, it was very provoking to find that the hedgehog had unrolled", - "itself, and was in the act of crawling away: besides all this, there was", - "generally a ridge or furrow in the way wherever she wanted to send the", - "hedgehog to, and, as the doubled-up soldiers were always getting up", - "and walking off to other parts of the ground, Alice soon came to the", - "conclusion that it was a very difficult game indeed.", - "The players all played at once without waiting for turns, quarrelling", - "all the while, and fighting for the hedgehogs; and in a very short", - "time the Queen was in a furious passion, and went stamping about, and", - "shouting ‘Off with his head!’ or ‘Off with her head!’ about once in a", - "minute.", - "Alice began to feel very uneasy: to be sure, she had not as yet had any", - "dispute with the Queen, but she knew that it might happen any minute,", - "‘and then,’ thought she, ‘what would become of me? They’re dreadfully", - "fond of beheading people here; the great wonder is, that there’s any one", - "left alive!’", - "She was looking about for some way of escape, and wondering whether she", - "could get away without being seen, when she noticed a curious appearance", - "in the air: it puzzled her very much at first, but, after watching it", - "a minute or two, she made it out to be a grin, and she said to herself", - "‘It’s the Cheshire Cat: now I shall have somebody to talk to.’", - "‘How are you getting on?’ said the Cat, as soon as there was mouth", - "enough for it to speak with.", - "Alice waited till the eyes appeared, and then nodded. ‘It’s no use", - "speaking to it,’ she thought, ‘till its ears have come, or at least one", - "of them.’ In another minute the whole head appeared, and then Alice put", - "down her flamingo, and began an account of the game, feeling very glad", - "she had someone to listen to her. The Cat seemed to think that there was", - "enough of it now in sight, and no more of it appeared.", - "‘I don’t think they play at all fairly,’ Alice began, in rather a", - "complaining tone, ‘and they all quarrel so dreadfully one can’t hear", - "oneself speak--and they don’t seem to have any rules in particular;", - "at least, if there are, nobody attends to them--and you’ve no idea how", - "confusing it is all the things being alive; for instance, there’s the", - "arch I’ve got to go through next walking about at the other end of the", - "ground--and I should have croqueted the Queen’s hedgehog just now, only", - "it ran away when it saw mine coming!’", - "‘How do you like the Queen?’ said the Cat in a low voice.", - "‘Not at all,’ said Alice: ‘she’s so extremely--’ Just then she noticed", - "that the Queen was close behind her, listening: so she went on,", - "‘--likely to win, that it’s hardly worth while finishing the game.’", - "The Queen smiled and passed on.", - "‘Who ARE you talking to?’ said the King, going up to Alice, and looking", - "at the Cat’s head with great curiosity.", - "‘It’s a friend of mine--a Cheshire Cat,’ said Alice: ‘allow me to", - "introduce it.’", - "‘I don’t like the look of it at all,’ said the King: ‘however, it may", - "kiss my hand if it likes.’", - "‘I’d rather not,’ the Cat remarked.", - "‘Don’t be impertinent,’ said the King, ‘and don’t look at me like that!’", - "He got behind Alice as he spoke.", - "‘A cat may look at a king,’ said Alice. ‘I’ve read that in some book,", - "but I don’t remember where.’", - "‘Well, it must be removed,’ said the King very decidedly, and he called", - "the Queen, who was passing at the moment, ‘My dear! I wish you would", - "have this cat removed!’", - "The Queen had only one way of settling all difficulties, great or small.", - "‘Off with his head!’ she said, without even looking round.", - "‘I’ll fetch the executioner myself,’ said the King eagerly, and he", - "hurried off.", - "Alice thought she might as well go back, and see how the game was going", - "on, as she heard the Queen’s voice in the distance, screaming with", - "passion. She had already heard her sentence three of the players to be", - "executed for having missed their turns, and she did not like the look", - "of things at all, as the game was in such confusion that she never knew", - "whether it was her turn or not. So she went in search of her hedgehog.", - "The hedgehog was engaged in a fight with another hedgehog, which seemed", - "to Alice an excellent opportunity for croqueting one of them with the", - "other: the only difficulty was, that her flamingo was gone across to the", - "other side of the garden, where Alice could see it trying in a helpless", - "sort of way to fly up into a tree.", - "By the time she had caught the flamingo and brought it back, the fight", - "was over, and both the hedgehogs were out of sight: ‘but it doesn’t", - "matter much,’ thought Alice, ‘as all the arches are gone from this side", - "of the ground.’ So she tucked it away under her arm, that it might not", - "escape again, and went back for a little more conversation with her", - "friend.", - "When she got back to the Cheshire Cat, she was surprised to find quite a", - "large crowd collected round it: there was a dispute going on between", - "the executioner, the King, and the Queen, who were all talking at once,", - "while all the rest were quite silent, and looked very uncomfortable.", - "The moment Alice appeared, she was appealed to by all three to settle", - "the question, and they repeated their arguments to her, though, as they", - "all spoke at once, she found it very hard indeed to make out exactly", - "what they said.", - "The executioner’s argument was, that you couldn’t cut off a head unless", - "there was a body to cut it off from: that he had never had to do such a", - "thing before, and he wasn’t going to begin at HIS time of life.", - "The King’s argument was, that anything that had a head could be", - "beheaded, and that you weren’t to talk nonsense.", - "The Queen’s argument was, that if something wasn’t done about it in less", - "than no time she’d have everybody executed, all round. (It was this last", - "remark that had made the whole party look so grave and anxious.)", - "Alice could think of nothing else to say but ‘It belongs to the Duchess:", - "you’d better ask HER about it.’", - "‘She’s in prison,’ the Queen said to the executioner: ‘fetch her here.’", - "And the executioner went off like an arrow.", - " The Cat’s head began fading away the moment he was gone, and,", - "by the time he had come back with the Duchess, it had entirely", - "disappeared; so the King and the executioner ran wildly up and down", - "looking for it, while the rest of the party went back to the game.", - "CHAPTER IX. The Mock Turtle’s Story", - "‘You can’t think how glad I am to see you again, you dear old thing!’", - "said the Duchess, as she tucked her arm affectionately into Alice’s, and", - "they walked off together.", - "Alice was very glad to find her in such a pleasant temper, and thought", - "to herself that perhaps it was only the pepper that had made her so", - "savage when they met in the kitchen.", - "‘When I’M a Duchess,’ she said to herself, (not in a very hopeful tone", - "though), ‘I won’t have any pepper in my kitchen AT ALL. Soup does very", - "well without--Maybe it’s always pepper that makes people hot-tempered,’", - "she went on, very much pleased at having found out a new kind of", - "rule, ‘and vinegar that makes them sour--and camomile that makes", - "them bitter--and--and barley-sugar and such things that make children", - "sweet-tempered. I only wish people knew that: then they wouldn’t be so", - "stingy about it, you know--’", - "She had quite forgotten the Duchess by this time, and was a little", - "startled when she heard her voice close to her ear. ‘You’re thinking", - "about something, my dear, and that makes you forget to talk. I can’t", - "tell you just now what the moral of that is, but I shall remember it in", - "a bit.’", - "‘Perhaps it hasn’t one,’ Alice ventured to remark.", - "‘Tut, tut, child!’ said the Duchess. ‘Everything’s got a moral, if only", - "you can find it.’ And she squeezed herself up closer to Alice’s side as", - "she spoke.", - "Alice did not much like keeping so close to her: first, because the", - "Duchess was VERY ugly; and secondly, because she was exactly the", - "right height to rest her chin upon Alice’s shoulder, and it was an", - "uncomfortably sharp chin. However, she did not like to be rude, so she", - "bore it as well as she could.", - "‘The game’s going on rather better now,’ she said, by way of keeping up", - "the conversation a little.", - "‘’Tis so,’ said the Duchess: ‘and the moral of that is--“Oh, ‘tis love,", - "‘tis love, that makes the world go round!”’", - "‘Somebody said,’ Alice whispered, ‘that it’s done by everybody minding", - "their own business!’", - "‘Ah, well! It means much the same thing,’ said the Duchess, digging her", - "sharp little chin into Alice’s shoulder as she added, ‘and the moral", - "of THAT is--“Take care of the sense, and the sounds will take care of", - "themselves.”’", - "‘How fond she is of finding morals in things!’ Alice thought to herself.", - "‘I dare say you’re wondering why I don’t put my arm round your waist,’", - "the Duchess said after a pause: ‘the reason is, that I’m doubtful about", - "the temper of your flamingo. Shall I try the experiment?’", - "‘HE might bite,’ Alice cautiously replied, not feeling at all anxious to", - "have the experiment tried.", - "‘Very true,’ said the Duchess: ‘flamingoes and mustard both bite. And", - "the moral of that is--“Birds of a feather flock together.”’", - "‘Only mustard isn’t a bird,’ Alice remarked.", - "‘Right, as usual,’ said the Duchess: ‘what a clear way you have of", - "putting things!’", - "‘It’s a mineral, I THINK,’ said Alice.", - "‘Of course it is,’ said the Duchess, who seemed ready to agree to", - "everything that Alice said; ‘there’s a large mustard-mine near here. And", - "the moral of that is--“The more there is of mine, the less there is of", - "yours.”’", - "‘Oh, I know!’ exclaimed Alice, who had not attended to this last remark,", - "‘it’s a vegetable. It doesn’t look like one, but it is.’", - "‘I quite agree with you,’ said the Duchess; ‘and the moral of that", - "is--“Be what you would seem to be”--or if you’d like it put more", - "simply--“Never imagine yourself not to be otherwise than what it might", - "appear to others that what you were or might have been was not otherwise", - "than what you had been would have appeared to them to be otherwise.”’", - "‘I think I should understand that better,’ Alice said very politely, ‘if", - "I had it written down: but I can’t quite follow it as you say it.’", - "‘That’s nothing to what I could say if I chose,’ the Duchess replied, in", - "a pleased tone.", - "‘Pray don’t trouble yourself to say it any longer than that,’ said", - "Alice.", - "‘Oh, don’t talk about trouble!’ said the Duchess. ‘I make you a present", - "of everything I’ve said as yet.’", - "‘A cheap sort of present!’ thought Alice. ‘I’m glad they don’t give", - "birthday presents like that!’ But she did not venture to say it out", - "loud.", - "‘Thinking again?’ the Duchess asked, with another dig of her sharp", - "little chin.", - "‘I’ve a right to think,’ said Alice sharply, for she was beginning to", - "feel a little worried.", - "‘Just about as much right,’ said the Duchess, ‘as pigs have to fly; and", - "the m--’", - "But here, to Alice’s great surprise, the Duchess’s voice died away, even", - "in the middle of her favourite word ‘moral,’ and the arm that was linked", - "into hers began to tremble. Alice looked up, and there stood the Queen", - "in front of them, with her arms folded, frowning like a thunderstorm.", - "‘A fine day, your Majesty!’ the Duchess began in a low, weak voice.", - "‘Now, I give you fair warning,’ shouted the Queen, stamping on the", - "ground as she spoke; ‘either you or your head must be off, and that in", - "about half no time! Take your choice!’", - "The Duchess took her choice, and was gone in a moment.", - "‘Let’s go on with the game,’ the Queen said to Alice; and Alice was", - "too much frightened to say a word, but slowly followed her back to the", - "croquet-ground.", - "The other guests had taken advantage of the Queen’s absence, and were", - "resting in the shade: however, the moment they saw her, they hurried", - "back to the game, the Queen merely remarking that a moment’s delay would", - "cost them their lives.", - "All the time they were playing the Queen never left off quarrelling with", - "the other players, and shouting ‘Off with his head!’ or ‘Off with her", - "head!’ Those whom she sentenced were taken into custody by the soldiers,", - "who of course had to leave off being arches to do this, so that by", - "the end of half an hour or so there were no arches left, and all the", - "players, except the King, the Queen, and Alice, were in custody and", - "under sentence of execution.", - "Then the Queen left off, quite out of breath, and said to Alice, ‘Have", - "you seen the Mock Turtle yet?’", - "‘No,’ said Alice. ‘I don’t even know what a Mock Turtle is.’", - "‘It’s the thing Mock Turtle Soup is made from,’ said the Queen.", - "‘I never saw one, or heard of one,’ said Alice.", - "‘Come on, then,’ said the Queen, ‘and he shall tell you his history,’", - "As they walked off together, Alice heard the King say in a low voice,", - "to the company generally, ‘You are all pardoned.’ ‘Come, THAT’S a good", - "thing!’ she said to herself, for she had felt quite unhappy at the", - "number of executions the Queen had ordered.", - "They very soon came upon a Gryphon, lying fast asleep in the sun.", - "(IF you don’t know what a Gryphon is, look at the picture.) ‘Up, lazy", - "thing!’ said the Queen, ‘and take this young lady to see the Mock", - "Turtle, and to hear his history. I must go back and see after some", - "executions I have ordered’; and she walked off, leaving Alice alone with", - "the Gryphon. Alice did not quite like the look of the creature, but on", - "the whole she thought it would be quite as safe to stay with it as to go", - "after that savage Queen: so she waited.", - "The Gryphon sat up and rubbed its eyes: then it watched the Queen till", - "she was out of sight: then it chuckled. ‘What fun!’ said the Gryphon,", - "half to itself, half to Alice.", - "‘What IS the fun?’ said Alice.", - "‘Why, SHE,’ said the Gryphon. ‘It’s all her fancy, that: they never", - "executes nobody, you know. Come on!’", - "‘Everybody says “come on!” here,’ thought Alice, as she went slowly", - "after it: ‘I never was so ordered about in all my life, never!’", - "They had not gone far before they saw the Mock Turtle in the distance,", - "sitting sad and lonely on a little ledge of rock, and, as they came", - "nearer, Alice could hear him sighing as if his heart would break. She", - "pitied him deeply. ‘What is his sorrow?’ she asked the Gryphon, and the", - "Gryphon answered, very nearly in the same words as before, ‘It’s all his", - "fancy, that: he hasn’t got no sorrow, you know. Come on!’", - "So they went up to the Mock Turtle, who looked at them with large eyes", - "full of tears, but said nothing.", - "‘This here young lady,’ said the Gryphon, ‘she wants for to know your", - "history, she do.’", - "‘I’ll tell it her,’ said the Mock Turtle in a deep, hollow tone: ‘sit", - "down, both of you, and don’t speak a word till I’ve finished.’", - "So they sat down, and nobody spoke for some minutes. Alice thought to", - "herself, ‘I don’t see how he can EVEN finish, if he doesn’t begin.’ But", - "she waited patiently.", - "‘Once,’ said the Mock Turtle at last, with a deep sigh, ‘I was a real", - "Turtle.’", - "These words were followed by a very long silence, broken only by an", - "occasional exclamation of ‘Hjckrrh!’ from the Gryphon, and the constant", - "heavy sobbing of the Mock Turtle. Alice was very nearly getting up and", - "saying, ‘Thank you, sir, for your interesting story,’ but she could", - "not help thinking there MUST be more to come, so she sat still and said", - "nothing.", - "‘When we were little,’ the Mock Turtle went on at last, more calmly,", - "though still sobbing a little now and then, ‘we went to school in the", - "sea. The master was an old Turtle--we used to call him Tortoise--’", - "‘Why did you call him Tortoise, if he wasn’t one?’ Alice asked.", - "‘We called him Tortoise because he taught us,’ said the Mock Turtle", - "angrily: ‘really you are very dull!’", - "‘You ought to be ashamed of yourself for asking such a simple question,’", - "added the Gryphon; and then they both sat silent and looked at poor", - "Alice, who felt ready to sink into the earth. At last the Gryphon said", - "to the Mock Turtle, ‘Drive on, old fellow! Don’t be all day about it!’", - "and he went on in these words:", - "‘Yes, we went to school in the sea, though you mayn’t believe it--’", - "‘I never said I didn’t!’ interrupted Alice.", - "‘You did,’ said the Mock Turtle.", - "‘Hold your tongue!’ added the Gryphon, before Alice could speak again.", - "The Mock Turtle went on.", - "‘We had the best of educations--in fact, we went to school every day--’", - "‘I’VE been to a day-school, too,’ said Alice; ‘you needn’t be so proud", - "as all that.’", - "‘With extras?’ asked the Mock Turtle a little anxiously.", - "‘Yes,’ said Alice, ‘we learned French and music.’", - "‘And washing?’ said the Mock Turtle.", - "‘Certainly not!’ said Alice indignantly.", - "‘Ah! then yours wasn’t a really good school,’ said the Mock Turtle in", - "a tone of great relief. ‘Now at OURS they had at the end of the bill,", - "“French, music, AND WASHING--extra.”’", - "‘You couldn’t have wanted it much,’ said Alice; ‘living at the bottom of", - "the sea.’", - "‘I couldn’t afford to learn it.’ said the Mock Turtle with a sigh. ‘I", - "only took the regular course.’", - "‘What was that?’ inquired Alice.", - "‘Reeling and Writhing, of course, to begin with,’ the Mock Turtle", - "replied; ‘and then the different branches of Arithmetic--Ambition,", - "Distraction, Uglification, and Derision.’", - "‘I never heard of “Uglification,”’ Alice ventured to say. ‘What is it?’", - "The Gryphon lifted up both its paws in surprise. ‘What! Never heard of", - "uglifying!’ it exclaimed. ‘You know what to beautify is, I suppose?’", - "‘Yes,’ said Alice doubtfully: ‘it means--to--make--anything--prettier.’", - "‘Well, then,’ the Gryphon went on, ‘if you don’t know what to uglify is,", - "you ARE a simpleton.’", - "Alice did not feel encouraged to ask any more questions about it, so she", - "turned to the Mock Turtle, and said ‘What else had you to learn?’", - "‘Well, there was Mystery,’ the Mock Turtle replied, counting off", - "the subjects on his flappers, ‘--Mystery, ancient and modern, with", - "Seaography: then Drawling--the Drawling-master was an old conger-eel,", - "that used to come once a week: HE taught us Drawling, Stretching, and", - "Fainting in Coils.’", - "‘What was THAT like?’ said Alice.", - "‘Well, I can’t show it you myself,’ the Mock Turtle said: ‘I’m too", - "stiff. And the Gryphon never learnt it.’", - "‘Hadn’t time,’ said the Gryphon: ‘I went to the Classics master, though.", - "He was an old crab, HE was.’", - "‘I never went to him,’ the Mock Turtle said with a sigh: ‘he taught", - "Laughing and Grief, they used to say.’", - "‘So he did, so he did,’ said the Gryphon, sighing in his turn; and both", - "creatures hid their faces in their paws.", - "‘And how many hours a day did you do lessons?’ said Alice, in a hurry to", - "change the subject.", - "‘Ten hours the first day,’ said the Mock Turtle: ‘nine the next, and so", - "on.’", - "‘What a curious plan!’ exclaimed Alice.", - "‘That’s the reason they’re called lessons,’ the Gryphon remarked:", - "‘because they lessen from day to day.’", - "This was quite a new idea to Alice, and she thought it over a little", - "before she made her next remark. ‘Then the eleventh day must have been a", - "holiday?’", - "‘Of course it was,’ said the Mock Turtle.", - "‘And how did you manage on the twelfth?’ Alice went on eagerly.", - "‘That’s enough about lessons,’ the Gryphon interrupted in a very decided", - "tone: ‘tell her something about the games now.’", - "CHAPTER X. The Lobster Quadrille", - "The Mock Turtle sighed deeply, and drew the back of one flapper across", - "his eyes. He looked at Alice, and tried to speak, but for a minute or", - "two sobs choked his voice. ‘Same as if he had a bone in his throat,’", - "said the Gryphon: and it set to work shaking him and punching him in", - "the back. At last the Mock Turtle recovered his voice, and, with tears", - "running down his cheeks, he went on again:--", - "‘You may not have lived much under the sea--’ [‘I haven’t,’ said", - "Alice)--‘and perhaps you were never even introduced to a lobster--’", - "(Alice began to say ‘I once tasted--’ but checked herself hastily, and", - "said ‘No, never’) ‘--so you can have no idea what a delightful thing a", - "Lobster Quadrille is!’", - "‘No, indeed,’ said Alice. ‘What sort of a dance is it?’", - "‘Why,’ said the Gryphon, ‘you first form into a line along the", - "sea-shore--’", - "‘Two lines!’ cried the Mock Turtle. ‘Seals, turtles, salmon, and so on;", - "then, when you’ve cleared all the jelly-fish out of the way--’", - "‘THAT generally takes some time,’ interrupted the Gryphon.", - "‘--you advance twice--’", - "‘Each with a lobster as a partner!’ cried the Gryphon.", - "‘Of course,’ the Mock Turtle said: ‘advance twice, set to partners--’", - "‘--change lobsters, and retire in same order,’ continued the Gryphon.", - "‘Then, you know,’ the Mock Turtle went on, ‘you throw the--’", - "‘The lobsters!’ shouted the Gryphon, with a bound into the air.", - "‘--as far out to sea as you can--’", - "‘Swim after them!’ screamed the Gryphon.", - "‘Turn a somersault in the sea!’ cried the Mock Turtle, capering wildly", - "about.", - "‘Change lobsters again!’ yelled the Gryphon at the top of its voice.", - "‘Back to land again, and that’s all the first figure,’ said the Mock", - "Turtle, suddenly dropping his voice; and the two creatures, who had been", - "jumping about like mad things all this time, sat down again very sadly", - "and quietly, and looked at Alice.", - "‘It must be a very pretty dance,’ said Alice timidly.", - "‘Would you like to see a little of it?’ said the Mock Turtle.", - "‘Very much indeed,’ said Alice.", - "‘Come, let’s try the first figure!’ said the Mock Turtle to the Gryphon.", - "‘We can do without lobsters, you know. Which shall sing?’", - "‘Oh, YOU sing,’ said the Gryphon. ‘I’ve forgotten the words.’", - "So they began solemnly dancing round and round Alice, every now and", - "then treading on her toes when they passed too close, and waving their", - "forepaws to mark the time, while the Mock Turtle sang this, very slowly", - "and sadly:--", - " ‘“Will you walk a little faster?” said a whiting to a snail.", - " “There’s a porpoise close behind us, and he’s treading on my tail.", - " See how eagerly the lobsters and the turtles all advance!", - " They are waiting on the shingle--will you come and join the dance?", - " Will you, won’t you, will you, won’t you, will you join the dance?", - " Will you, won’t you, will you, won’t you, won’t you join the dance?", - " “You can really have no notion how delightful it will be", - " When they take us up and throw us, with the lobsters, out to sea!”", - " But the snail replied “Too far, too far!” and gave a look askance--", - " Said he thanked the whiting kindly, but he would not join the dance.", - " Would not, could not, would not, could not, would not join the dance.", - " Would not, could not, would not, could not, could not join the dance.", - " ‘“What matters it how far we go?” his scaly friend replied.", - " “There is another shore, you know, upon the other side.", - " The further off from England the nearer is to France--", - " Then turn not pale, beloved snail, but come and join the dance.", - " Will you, won’t you, will you, won’t you, will you join the dance?", - " Will you, won’t you, will you, won’t you, won’t you join the dance?”’", - "‘Thank you, it’s a very interesting dance to watch,’ said Alice, feeling", - "very glad that it was over at last: ‘and I do so like that curious song", - "about the whiting!’", - "‘Oh, as to the whiting,’ said the Mock Turtle, ‘they--you’ve seen them,", - "of course?’", - "‘Yes,’ said Alice, ‘I’ve often seen them at dinn--’ she checked herself", - "hastily.", - "‘I don’t know where Dinn may be,’ said the Mock Turtle, ‘but if you’ve", - "seen them so often, of course you know what they’re like.’", - "‘I believe so,’ Alice replied thoughtfully. ‘They have their tails in", - "their mouths--and they’re all over crumbs.’", - "‘You’re wrong about the crumbs,’ said the Mock Turtle: ‘crumbs would all", - "wash off in the sea. But they HAVE their tails in their mouths; and the", - "reason is--’ here the Mock Turtle yawned and shut his eyes.--‘Tell her", - "about the reason and all that,’ he said to the Gryphon.", - "‘The reason is,’ said the Gryphon, ‘that they WOULD go with the lobsters", - "to the dance. So they got thrown out to sea. So they had to fall a long", - "way. So they got their tails fast in their mouths. So they couldn’t get", - "them out again. That’s all.’", - "‘Thank you,’ said Alice, ‘it’s very interesting. I never knew so much", - "about a whiting before.’", - "‘I can tell you more than that, if you like,’ said the Gryphon. ‘Do you", - "know why it’s called a whiting?’", - "‘I never thought about it,’ said Alice. ‘Why?’", - "‘IT DOES THE BOOTS AND SHOES.’ the Gryphon replied very solemnly.", - "Alice was thoroughly puzzled. ‘Does the boots and shoes!’ she repeated", - "in a wondering tone.", - "‘Why, what are YOUR shoes done with?’ said the Gryphon. ‘I mean, what", - "makes them so shiny?’", - "Alice looked down at them, and considered a little before she gave her", - "answer. ‘They’re done with blacking, I believe.’", - "‘Boots and shoes under the sea,’ the Gryphon went on in a deep voice,", - "‘are done with a whiting. Now you know.’", - "‘And what are they made of?’ Alice asked in a tone of great curiosity.", - "‘Soles and eels, of course,’ the Gryphon replied rather impatiently:", - "‘any shrimp could have told you that.’", - "‘If I’d been the whiting,’ said Alice, whose thoughts were still running", - "on the song, ‘I’d have said to the porpoise, “Keep back, please: we", - "don’t want YOU with us!”’", - "‘They were obliged to have him with them,’ the Mock Turtle said: ‘no", - "wise fish would go anywhere without a porpoise.’", - "‘Wouldn’t it really?’ said Alice in a tone of great surprise.", - "‘Of course not,’ said the Mock Turtle: ‘why, if a fish came to ME, and", - "told me he was going a journey, I should say “With what porpoise?”’", - "‘Don’t you mean “purpose”?’ said Alice.", - "‘I mean what I say,’ the Mock Turtle replied in an offended tone. And", - "the Gryphon added ‘Come, let’s hear some of YOUR adventures.’", - "‘I could tell you my adventures--beginning from this morning,’ said", - "Alice a little timidly: ‘but it’s no use going back to yesterday,", - "because I was a different person then.’", - "‘Explain all that,’ said the Mock Turtle.", - "‘No, no! The adventures first,’ said the Gryphon in an impatient tone:", - "‘explanations take such a dreadful time.’", - "So Alice began telling them her adventures from the time when she first", - "saw the White Rabbit. She was a little nervous about it just at first,", - "the two creatures got so close to her, one on each side, and opened", - "their eyes and mouths so VERY wide, but she gained courage as she went", - "on. Her listeners were perfectly quiet till she got to the part about", - "her repeating ‘YOU ARE OLD, FATHER WILLIAM,’ to the Caterpillar, and the", - "words all coming different, and then the Mock Turtle drew a long breath,", - "and said ‘That’s very curious.’", - "‘It’s all about as curious as it can be,’ said the Gryphon.", - "‘It all came different!’ the Mock Turtle repeated thoughtfully. ‘I", - "should like to hear her try and repeat something now. Tell her to", - "begin.’ He looked at the Gryphon as if he thought it had some kind of", - "authority over Alice.", - "‘Stand up and repeat “‘TIS THE VOICE OF THE SLUGGARD,”’ said the", - "Gryphon.", - "‘How the creatures order one about, and make one repeat lessons!’", - "thought Alice; ‘I might as well be at school at once.’ However, she", - "got up, and began to repeat it, but her head was so full of the Lobster", - "Quadrille, that she hardly knew what she was saying, and the words came", - "very queer indeed:--", - " ‘’Tis the voice of the Lobster; I heard him declare,", - " “You have baked me too brown, I must sugar my hair.”", - " As a duck with its eyelids, so he with his nose", - " Trims his belt and his buttons, and turns out his toes.’", - " [later editions continued as follows", - " When the sands are all dry, he is gay as a lark,", - " And will talk in contemptuous tones of the Shark,", - " But, when the tide rises and sharks are around,", - " His voice has a timid and tremulous sound.]", - "‘That’s different from what I used to say when I was a child,’ said the", - "Gryphon.", - "‘Well, I never heard it before,’ said the Mock Turtle; ‘but it sounds", - "uncommon nonsense.’", - "Alice said nothing; she had sat down with her face in her hands,", - "wondering if anything would EVER happen in a natural way again.", - "‘I should like to have it explained,’ said the Mock Turtle.", - "‘She can’t explain it,’ said the Gryphon hastily. ‘Go on with the next", - "verse.’", - "‘But about his toes?’ the Mock Turtle persisted. ‘How COULD he turn them", - "out with his nose, you know?’", - "‘It’s the first position in dancing.’ Alice said; but was dreadfully", - "puzzled by the whole thing, and longed to change the subject.", - "‘Go on with the next verse,’ the Gryphon repeated impatiently: ‘it", - "begins “I passed by his garden.”’", - "Alice did not dare to disobey, though she felt sure it would all come", - "wrong, and she went on in a trembling voice:--", - " ‘I passed by his garden, and marked, with one eye,", - " How the Owl and the Panther were sharing a pie--’", - " [later editions continued as follows", - " The Panther took pie-crust, and gravy, and meat,", - " While the Owl had the dish as its share of the treat.", - " When the pie was all finished, the Owl, as a boon,", - " Was kindly permitted to pocket the spoon:", - " While the Panther received knife and fork with a growl,", - " And concluded the banquet--]", - "‘What IS the use of repeating all that stuff,’ the Mock Turtle", - "interrupted, ‘if you don’t explain it as you go on? It’s by far the most", - "confusing thing I ever heard!’", - "‘Yes, I think you’d better leave off,’ said the Gryphon: and Alice was", - "only too glad to do so.", - "‘Shall we try another figure of the Lobster Quadrille?’ the Gryphon went", - "on. ‘Or would you like the Mock Turtle to sing you a song?’", - "‘Oh, a song, please, if the Mock Turtle would be so kind,’ Alice", - "replied, so eagerly that the Gryphon said, in a rather offended tone,", - "‘Hm! No accounting for tastes! Sing her “Turtle Soup,” will you, old", - "fellow?’", - "The Mock Turtle sighed deeply, and began, in a voice sometimes choked", - "with sobs, to sing this:--", - " ‘Beautiful Soup, so rich and green,", - " Waiting in a hot tureen!", - " Who for such dainties would not stoop?", - " Soup of the evening, beautiful Soup!", - " Soup of the evening, beautiful Soup!", - " Beau--ootiful Soo--oop!", - " Beau--ootiful Soo--oop!", - " Soo--oop of the e--e--evening,", - " Beautiful, beautiful Soup!", - " ‘Beautiful Soup! Who cares for fish,", - " Game, or any other dish?", - " Who would not give all else for two", - " Pennyworth only of beautiful Soup?", - " Pennyworth only of beautiful Soup?", - " Beau--ootiful Soo--oop!", - " Beau--ootiful Soo--oop!", - " Soo--oop of the e--e--evening,", - " Beautiful, beauti--FUL SOUP!’", - "‘Chorus again!’ cried the Gryphon, and the Mock Turtle had just begun", - "to repeat it, when a cry of ‘The trial’s beginning!’ was heard in the", - "distance.", - "‘Come on!’ cried the Gryphon, and, taking Alice by the hand, it hurried", - "off, without waiting for the end of the song.", - "‘What trial is it?’ Alice panted as she ran; but the Gryphon only", - "answered ‘Come on!’ and ran the faster, while more and more faintly", - "came, carried on the breeze that followed them, the melancholy words:--", - " ‘Soo--oop of the e--e--evening,", - " Beautiful, beautiful Soup!’", - "CHAPTER XI. Who Stole the Tarts?", - "The King and Queen of Hearts were seated on their throne when they", - "arrived, with a great crowd assembled about them--all sorts of little", - "birds and beasts, as well as the whole pack of cards: the Knave was", - "standing before them, in chains, with a soldier on each side to guard", - "him; and near the King was the White Rabbit, with a trumpet in one hand,", - "and a scroll of parchment in the other. In the very middle of the court", - "was a table, with a large dish of tarts upon it: they looked so good,", - "that it made Alice quite hungry to look at them--‘I wish they’d get the", - "trial done,’ she thought, ‘and hand round the refreshments!’ But there", - "seemed to be no chance of this, so she began looking at everything about", - "her, to pass away the time.", - "Alice had never been in a court of justice before, but she had read", - "about them in books, and she was quite pleased to find that she knew", - "the name of nearly everything there. ‘That’s the judge,’ she said to", - "herself, ‘because of his great wig.’", - "The judge, by the way, was the King; and as he wore his crown over the", - "wig, (look at the frontispiece if you want to see how he did it,) he did", - "not look at all comfortable, and it was certainly not becoming.", - "‘And that’s the jury-box,’ thought Alice, ‘and those twelve creatures,’", - "(she was obliged to say ‘creatures,’ you see, because some of them were", - "animals, and some were birds,) ‘I suppose they are the jurors.’ She said", - "this last word two or three times over to herself, being rather proud of", - "it: for she thought, and rightly too, that very few little girls of her", - "age knew the meaning of it at all. However, ‘jury-men’ would have done", - "just as well.", - "The twelve jurors were all writing very busily on slates. ‘What are they", - "doing?’ Alice whispered to the Gryphon. ‘They can’t have anything to put", - "down yet, before the trial’s begun.’", - "‘They’re putting down their names,’ the Gryphon whispered in reply, ‘for", - "fear they should forget them before the end of the trial.’", - "‘Stupid things!’ Alice began in a loud, indignant voice, but she stopped", - "hastily, for the White Rabbit cried out, ‘Silence in the court!’ and the", - "King put on his spectacles and looked anxiously round, to make out who", - "was talking.", - "Alice could see, as well as if she were looking over their shoulders,", - "that all the jurors were writing down ‘stupid things!’ on their slates,", - "and she could even make out that one of them didn’t know how to spell", - "‘stupid,’ and that he had to ask his neighbour to tell him. ‘A nice", - "muddle their slates’ll be in before the trial’s over!’ thought Alice.", - "One of the jurors had a pencil that squeaked. This of course, Alice", - "could not stand, and she went round the court and got behind him, and", - "very soon found an opportunity of taking it away. She did it so quickly", - "that the poor little juror (it was Bill, the Lizard) could not make out", - "at all what had become of it; so, after hunting all about for it, he was", - "obliged to write with one finger for the rest of the day; and this was", - "of very little use, as it left no mark on the slate.", - "‘Herald, read the accusation!’ said the King.", - "On this the White Rabbit blew three blasts on the trumpet, and then", - "unrolled the parchment scroll, and read as follows:--", - " ‘The Queen of Hearts, she made some tarts,", - " All on a summer day:", - " The Knave of Hearts, he stole those tarts,", - " And took them quite away!’", - "‘Consider your verdict,’ the King said to the jury.", - "‘Not yet, not yet!’ the Rabbit hastily interrupted. ‘There’s a great", - "deal to come before that!’", - "‘Call the first witness,’ said the King; and the White Rabbit blew three", - "blasts on the trumpet, and called out, ‘First witness!’", - "The first witness was the Hatter. He came in with a teacup in one", - "hand and a piece of bread-and-butter in the other. ‘I beg pardon, your", - "Majesty,’ he began, ‘for bringing these in: but I hadn’t quite finished", - "my tea when I was sent for.’", - "‘You ought to have finished,’ said the King. ‘When did you begin?’", - "The Hatter looked at the March Hare, who had followed him into the", - "court, arm-in-arm with the Dormouse. ‘Fourteenth of March, I think it", - "was,’ he said.", - "‘Fifteenth,’ said the March Hare.", - "‘Sixteenth,’ added the Dormouse.", - "‘Write that down,’ the King said to the jury, and the jury eagerly", - "wrote down all three dates on their slates, and then added them up, and", - "reduced the answer to shillings and pence.", - "‘Take off your hat,’ the King said to the Hatter.", - "‘It isn’t mine,’ said the Hatter.", - "‘Stolen!’ the King exclaimed, turning to the jury, who instantly made a", - "memorandum of the fact.", - "‘I keep them to sell,’ the Hatter added as an explanation; ‘I’ve none of", - "my own. I’m a hatter.’", - "Here the Queen put on her spectacles, and began staring at the Hatter,", - "who turned pale and fidgeted.", - "‘Give your evidence,’ said the King; ‘and don’t be nervous, or I’ll have", - "you executed on the spot.’", - "This did not seem to encourage the witness at all: he kept shifting", - "from one foot to the other, looking uneasily at the Queen, and in", - "his confusion he bit a large piece out of his teacup instead of the", - "bread-and-butter.", - "Just at this moment Alice felt a very curious sensation, which puzzled", - "her a good deal until she made out what it was: she was beginning to", - "grow larger again, and she thought at first she would get up and leave", - "the court; but on second thoughts she decided to remain where she was as", - "long as there was room for her.", - "‘I wish you wouldn’t squeeze so.’ said the Dormouse, who was sitting", - "next to her. ‘I can hardly breathe.’", - "‘I can’t help it,’ said Alice very meekly: ‘I’m growing.’", - "‘You’ve no right to grow here,’ said the Dormouse.", - "‘Don’t talk nonsense,’ said Alice more boldly: ‘you know you’re growing", - "too.’", - "‘Yes, but I grow at a reasonable pace,’ said the Dormouse: ‘not in that", - "ridiculous fashion.’ And he got up very sulkily and crossed over to the", - "other side of the court.", - "All this time the Queen had never left off staring at the Hatter, and,", - "just as the Dormouse crossed the court, she said to one of the officers", - "of the court, ‘Bring me the list of the singers in the last concert!’ on", - "which the wretched Hatter trembled so, that he shook both his shoes off.", - "‘Give your evidence,’ the King repeated angrily, ‘or I’ll have you", - "executed, whether you’re nervous or not.’", - "‘I’m a poor man, your Majesty,’ the Hatter began, in a trembling voice,", - "‘--and I hadn’t begun my tea--not above a week or so--and what with the", - "bread-and-butter getting so thin--and the twinkling of the tea--’", - "‘The twinkling of the what?’ said the King.", - "‘It began with the tea,’ the Hatter replied.", - "‘Of course twinkling begins with a T!’ said the King sharply. ‘Do you", - "take me for a dunce? Go on!’", - "‘I’m a poor man,’ the Hatter went on, ‘and most things twinkled after", - "that--only the March Hare said--’", - "‘I didn’t!’ the March Hare interrupted in a great hurry.", - "‘You did!’ said the Hatter.", - "‘I deny it!’ said the March Hare.", - "‘He denies it,’ said the King: ‘leave out that part.’", - "‘Well, at any rate, the Dormouse said--’ the Hatter went on, looking", - "anxiously round to see if he would deny it too: but the Dormouse denied", - "nothing, being fast asleep.", - "‘After that,’ continued the Hatter, ‘I cut some more bread-and-butter--’", - "‘But what did the Dormouse say?’ one of the jury asked.", - "‘That I can’t remember,’ said the Hatter.", - "‘You MUST remember,’ remarked the King, ‘or I’ll have you executed.’", - "The miserable Hatter dropped his teacup and bread-and-butter, and went", - "down on one knee. ‘I’m a poor man, your Majesty,’ he began.", - "‘You’re a very poor speaker,’ said the King.", - "Here one of the guinea-pigs cheered, and was immediately suppressed by", - "the officers of the court. (As that is rather a hard word, I will just", - "explain to you how it was done. They had a large canvas bag, which tied", - "up at the mouth with strings: into this they slipped the guinea-pig,", - "head first, and then sat upon it.)", - "‘I’m glad I’ve seen that done,’ thought Alice. ‘I’ve so often read", - "in the newspapers, at the end of trials, “There was some attempts", - "at applause, which was immediately suppressed by the officers of the", - "court,” and I never understood what it meant till now.’", - "‘If that’s all you know about it, you may stand down,’ continued the", - "King.", - "‘I can’t go no lower,’ said the Hatter: ‘I’m on the floor, as it is.’", - "‘Then you may SIT down,’ the King replied.", - "Here the other guinea-pig cheered, and was suppressed.", - "‘Come, that finished the guinea-pigs!’ thought Alice. ‘Now we shall get", - "on better.’", - "‘I’d rather finish my tea,’ said the Hatter, with an anxious look at the", - "Queen, who was reading the list of singers.", - "‘You may go,’ said the King, and the Hatter hurriedly left the court,", - "without even waiting to put his shoes on.", - "‘--and just take his head off outside,’ the Queen added to one of the", - "officers: but the Hatter was out of sight before the officer could get", - "to the door.", - "‘Call the next witness!’ said the King.", - "The next witness was the Duchess’s cook. She carried the pepper-box in", - "her hand, and Alice guessed who it was, even before she got into the", - "court, by the way the people near the door began sneezing all at once.", - "‘Give your evidence,’ said the King.", - "‘Shan’t,’ said the cook.", - "The King looked anxiously at the White Rabbit, who said in a low voice,", - "‘Your Majesty must cross-examine THIS witness.’", - "‘Well, if I must, I must,’ the King said, with a melancholy air, and,", - "after folding his arms and frowning at the cook till his eyes were", - "nearly out of sight, he said in a deep voice, ‘What are tarts made of?’", - "‘Pepper, mostly,’ said the cook.", - "‘Treacle,’ said a sleepy voice behind her.", - "‘Collar that Dormouse,’ the Queen shrieked out. ‘Behead that Dormouse!", - "Turn that Dormouse out of court! Suppress him! Pinch him! Off with his", - "whiskers!’", - "For some minutes the whole court was in confusion, getting the Dormouse", - "turned out, and, by the time they had settled down again, the cook had", - "disappeared.", - "‘Never mind!’ said the King, with an air of great relief. ‘Call the next", - "witness.’ And he added in an undertone to the Queen, ‘Really, my dear,", - "YOU must cross-examine the next witness. It quite makes my forehead", - "ache!’", - "Alice watched the White Rabbit as he fumbled over the list, feeling very", - "curious to see what the next witness would be like, ‘--for they haven’t", - "got much evidence YET,’ she said to herself. Imagine her surprise, when", - "the White Rabbit read out, at the top of his shrill little voice, the", - "name ‘Alice!’", - "CHAPTER XII. Alice’s Evidence", - "‘Here!’ cried Alice, quite forgetting in the flurry of the moment how", - "large she had grown in the last few minutes, and she jumped up in such", - "a hurry that she tipped over the jury-box with the edge of her skirt,", - "upsetting all the jurymen on to the heads of the crowd below, and there", - "they lay sprawling about, reminding her very much of a globe of goldfish", - "she had accidentally upset the week before.", - "‘Oh, I BEG your pardon!’ she exclaimed in a tone of great dismay, and", - "began picking them up again as quickly as she could, for the accident of", - "the goldfish kept running in her head, and she had a vague sort of idea", - "that they must be collected at once and put back into the jury-box, or", - "they would die.", - "‘The trial cannot proceed,’ said the King in a very grave voice, ‘until", - "all the jurymen are back in their proper places--ALL,’ he repeated with", - "great emphasis, looking hard at Alice as he said do.", - "Alice looked at the jury-box, and saw that, in her haste, she had put", - "the Lizard in head downwards, and the poor little thing was waving its", - "tail about in a melancholy way, being quite unable to move. She soon got", - "it out again, and put it right; ‘not that it signifies much,’ she said", - "to herself; ‘I should think it would be QUITE as much use in the trial", - "one way up as the other.’", - "As soon as the jury had a little recovered from the shock of being", - "upset, and their slates and pencils had been found and handed back to", - "them, they set to work very diligently to write out a history of the", - "accident, all except the Lizard, who seemed too much overcome to do", - "anything but sit with its mouth open, gazing up into the roof of the", - "court.", - "‘What do you know about this business?’ the King said to Alice.", - "‘Nothing,’ said Alice.", - "‘Nothing WHATEVER?’ persisted the King.", - "‘Nothing whatever,’ said Alice.", - "‘That’s very important,’ the King said, turning to the jury. They were", - "just beginning to write this down on their slates, when the White Rabbit", - "interrupted: ‘UNimportant, your Majesty means, of course,’ he said in a", - "very respectful tone, but frowning and making faces at him as he spoke.", - "‘UNimportant, of course, I meant,’ the King hastily said, and went on", - "to himself in an undertone,", - "‘important--unimportant--unimportant--important--’ as if he were trying", - "which word sounded best.", - "Some of the jury wrote it down ‘important,’ and some ‘unimportant.’", - "Alice could see this, as she was near enough to look over their slates;", - "‘but it doesn’t matter a bit,’ she thought to herself.", - "At this moment the King, who had been for some time busily writing in", - "his note-book, cackled out ‘Silence!’ and read out from his book, ‘Rule", - "Forty-two. ALL PERSONS MORE THAN A MILE HIGH TO LEAVE THE COURT.’", - "Everybody looked at Alice.", - "‘I’M not a mile high,’ said Alice.", - "‘You are,’ said the King.", - "‘Nearly two miles high,’ added the Queen.", - "‘Well, I shan’t go, at any rate,’ said Alice: ‘besides, that’s not a", - "regular rule: you invented it just now.’", - "‘It’s the oldest rule in the book,’ said the King.", - "‘Then it ought to be Number One,’ said Alice.", - "The King turned pale, and shut his note-book hastily. ‘Consider your", - "verdict,’ he said to the jury, in a low, trembling voice.", - "‘There’s more evidence to come yet, please your Majesty,’ said the White", - "Rabbit, jumping up in a great hurry; ‘this paper has just been picked", - "up.’", - "‘What’s in it?’ said the Queen.", - "‘I haven’t opened it yet,’ said the White Rabbit, ‘but it seems to be a", - "letter, written by the prisoner to--to somebody.’", - "‘It must have been that,’ said the King, ‘unless it was written to", - "nobody, which isn’t usual, you know.’", - "‘Who is it directed to?’ said one of the jurymen.", - "‘It isn’t directed at all,’ said the White Rabbit; ‘in fact, there’s", - "nothing written on the OUTSIDE.’ He unfolded the paper as he spoke, and", - "added ‘It isn’t a letter, after all: it’s a set of verses.’", - "‘Are they in the prisoner’s handwriting?’ asked another of the jurymen.", - "‘No, they’re not,’ said the White Rabbit, ‘and that’s the queerest thing", - "about it.’ (The jury all looked puzzled.)", - "‘He must have imitated somebody else’s hand,’ said the King. (The jury", - "all brightened up again.)", - "‘Please your Majesty,’ said the Knave, ‘I didn’t write it, and they", - "can’t prove I did: there’s no name signed at the end.’", - "‘If you didn’t sign it,’ said the King, ‘that only makes the matter", - "worse. You MUST have meant some mischief, or else you’d have signed your", - "name like an honest man.’", - "There was a general clapping of hands at this: it was the first really", - "clever thing the King had said that day.", - "‘That PROVES his guilt,’ said the Queen.", - "‘It proves nothing of the sort!’ said Alice. ‘Why, you don’t even know", - "what they’re about!’", - "‘Read them,’ said the King.", - "The White Rabbit put on his spectacles. ‘Where shall I begin, please", - "your Majesty?’ he asked.", - "‘Begin at the beginning,’ the King said gravely, ‘and go on till you", - "come to the end: then stop.’", - "These were the verses the White Rabbit read:--", - " ‘They told me you had been to her,", - " And mentioned me to him:", - " She gave me a good character,", - " But said I could not swim.", - " He sent them word I had not gone", - " (We know it to be true):", - " If she should push the matter on,", - " What would become of you?", - " I gave her one, they gave him two,", - " You gave us three or more;", - " They all returned from him to you,", - " Though they were mine before.", - " If I or she should chance to be", - " Involved in this affair,", - " He trusts to you to set them free,", - " Exactly as we were.", - " My notion was that you had been", - " (Before she had this fit)", - " An obstacle that came between", - " Him, and ourselves, and it.", - " Don’t let him know she liked them best,", - " For this must ever be", - " A secret, kept from all the rest,", - " Between yourself and me.’", - "‘That’s the most important piece of evidence we’ve heard yet,’ said the", - "King, rubbing his hands; ‘so now let the jury--’", - "‘If any one of them can explain it,’ said Alice, (she had grown so large", - "in the last few minutes that she wasn’t a bit afraid of interrupting", - "him,) ‘I’ll give him sixpence. _I_ don’t believe there’s an atom of", - "meaning in it.’", - "The jury all wrote down on their slates, ‘SHE doesn’t believe there’s an", - "atom of meaning in it,’ but none of them attempted to explain the paper.", - "‘If there’s no meaning in it,’ said the King, ‘that saves a world of", - "trouble, you know, as we needn’t try to find any. And yet I don’t know,’", - "he went on, spreading out the verses on his knee, and looking at them", - "with one eye; ‘I seem to see some meaning in them, after all. “--SAID", - "I COULD NOT SWIM--” you can’t swim, can you?’ he added, turning to the", - "Knave.", - "The Knave shook his head sadly. ‘Do I look like it?’ he said. (Which he", - "certainly did NOT, being made entirely of cardboard.)", - "‘All right, so far,’ said the King, and he went on muttering over", - "the verses to himself: ‘“WE KNOW IT TO BE TRUE--” that’s the jury, of", - "course--“I GAVE HER ONE, THEY GAVE HIM TWO--” why, that must be what he", - "did with the tarts, you know--’", - "‘But, it goes on “THEY ALL RETURNED FROM HIM TO YOU,”’ said Alice.", - "‘Why, there they are!’ said the King triumphantly, pointing to the tarts", - "on the table. ‘Nothing can be clearer than THAT. Then again--“BEFORE SHE", - "HAD THIS FIT--” you never had fits, my dear, I think?’ he said to the", - "Queen.", - "‘Never!’ said the Queen furiously, throwing an inkstand at the Lizard", - "as she spoke. (The unfortunate little Bill had left off writing on his", - "slate with one finger, as he found it made no mark; but he now hastily", - "began again, using the ink, that was trickling down his face, as long as", - "it lasted.)", - "‘Then the words don’t FIT you,’ said the King, looking round the court", - "with a smile. There was a dead silence.", - "‘It’s a pun!’ the King added in an offended tone, and everybody laughed,", - "‘Let the jury consider their verdict,’ the King said, for about the", - "twentieth time that day.", - "‘No, no!’ said the Queen. ‘Sentence first--verdict afterwards.’", - "‘Stuff and nonsense!’ said Alice loudly. ‘The idea of having the", - "sentence first!’", - "‘Hold your tongue!’ said the Queen, turning purple.", - "‘I won’t!’ said Alice.", - "‘Off with her head!’ the Queen shouted at the top of her voice. Nobody", - "moved.", - "‘Who cares for you?’ said Alice, (she had grown to her full size by this", - "time.) ‘You’re nothing but a pack of cards!’", - "At this the whole pack rose up into the air, and came flying down upon", - "her: she gave a little scream, half of fright and half of anger, and", - "tried to beat them off, and found herself lying on the bank, with her", - "head in the lap of her sister, who was gently brushing away some dead", - "leaves that had fluttered down from the trees upon her face.", - "‘Wake up, Alice dear!’ said her sister; ‘Why, what a long sleep you’ve", - "had!’", - "‘Oh, I’ve had such a curious dream!’ said Alice, and she told her", - "sister, as well as she could remember them, all these strange Adventures", - "of hers that you have just been reading about; and when she had", - "finished, her sister kissed her, and said, ‘It WAS a curious dream,", - "dear, certainly: but now run in to your tea; it’s getting late.’ So", - "Alice got up and ran off, thinking while she ran, as well she might,", - "what a wonderful dream it had been.", - "But her sister sat still just as she left her, leaning her head on her", - "hand, watching the setting sun, and thinking of little Alice and all her", - "wonderful Adventures, till she too began dreaming after a fashion, and", - "this was her dream:--", - "First, she dreamed of little Alice herself, and once again the tiny", - "hands were clasped upon her knee, and the bright eager eyes were looking", - "up into hers--she could hear the very tones of her voice, and see that", - "queer little toss of her head to keep back the wandering hair that", - "WOULD always get into her eyes--and still as she listened, or seemed to", - "listen, the whole place around her became alive with the strange creatures", - "of her little sister’s dream.", - "The long grass rustled at her feet as the White Rabbit hurried by--the", - "frightened Mouse splashed his way through the neighbouring pool--she", - "could hear the rattle of the teacups as the March Hare and his friends", - "shared their never-ending meal, and the shrill voice of the Queen", - "ordering off her unfortunate guests to execution--once more the pig-baby", - "was sneezing on the Duchess’s knee, while plates and dishes crashed", - "around it--once more the shriek of the Gryphon, the squeaking of the", - "Lizard’s slate-pencil, and the choking of the suppressed guinea-pigs,", - "filled the air, mixed up with the distant sobs of the miserable Mock", - "Turtle.", - "So she sat on, with closed eyes, and half believed herself in", - "Wonderland, though she knew she had but to open them again, and all", - "would change to dull reality--the grass would be only rustling in the", - "wind, and the pool rippling to the waving of the reeds--the rattling", - "teacups would change to tinkling sheep-bells, and the Queen’s shrill", - "cries to the voice of the shepherd boy--and the sneeze of the baby, the", - "shriek of the Gryphon, and all the other queer noises, would change (she", - "knew) to the confused clamour of the busy farm-yard--while the lowing", - "of the cattle in the distance would take the place of the Mock Turtle’s", - "heavy sobs.", - "Lastly, she pictured to herself how this same little sister of hers", - "would, in the after-time, be herself a grown woman; and how she would", - "keep, through all her riper years, the simple and loving heart of her", - "childhood: and how she would gather about her other little children, and", - "make THEIR eyes bright and eager with many a strange tale, perhaps even", - "with the dream of Wonderland of long ago: and how she would feel with", - "all their simple sorrows, and find a pleasure in all their simple joys,", - "remembering her own child-life, and the happy summer days.", - " THE END", - "End of Project Gutenberg’s Alice’s Adventures in Wonderland, by Lewis Carroll", - "*** END OF THIS PROJECT GUTENBERG EBOOK ALICE’S ADVENTURES IN WONDERLAND ***", - "***** This file should be named 11-0.txt or 11-0.zip *****", - "This and all associated files of various formats will be found in:", - " http://www.gutenberg.org/1/11/", - "Updated editions will replace the previous one--the old editions", - "will be renamed.", - "Creating the works from public domain print editions means that no", - "one owns a United States copyright in these works, so the Foundation", - "(and you!) can copy and distribute it in the United States without", - "permission and without paying copyright royalties. Special rules,", - "set forth in the General Terms of Use part of this license, apply to", - "copying and distributing Project Gutenberg-tm electronic works to", - "protect the PROJECT GUTENBERG-tm concept and trademark. Project", - "Gutenberg is a registered trademark, and may not be used if you", - "charge for the eBooks, unless you receive specific permission. If you", - "do not charge anything for copies of this eBook, complying with the", - "rules is very easy. You may use this eBook for nearly any purpose", - "such as creation of derivative works, reports, performances and", - "research. They may be modified and printed and given away--you may do", - "practically ANYTHING with public domain eBooks. Redistribution is", - "subject to the trademark license, especially commercial", - "redistribution.", - "*** START: FULL LICENSE ***", - "THE FULL PROJECT GUTENBERG LICENSE", - "PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK", - "To protect the Project Gutenberg-tm mission of promoting the free", - "distribution of electronic works, by using or distributing this work", - "(or any other work associated in any way with the phrase “Project", - "Gutenberg”), you agree to comply with all the terms of the Full Project", - "Gutenberg-tm License (available with this file or online at", - "http://gutenberg.org/license).", - "Section 1. General Terms of Use and Redistributing Project Gutenberg-tm", - "electronic works", - "1.A. By reading or using any part of this Project Gutenberg-tm", - "electronic work, you indicate that you have read, understand, agree to", - "and accept all the terms of this license and intellectual property", - "(trademark/copyright) agreement. If you do not agree to abide by all", - "the terms of this agreement, you must cease using and return or destroy", - "all copies of Project Gutenberg-tm electronic works in your possession.", - "If you paid a fee for obtaining a copy of or access to a Project", - "Gutenberg-tm electronic work and you do not agree to be bound by the", - "terms of this agreement, you may obtain a refund from the person or", - "entity to whom you paid the fee as set forth in paragraph 1.E.8.", - "1.B. “Project Gutenberg” is a registered trademark. It may only be", - "used on or associated in any way with an electronic work by people who", - "agree to be bound by the terms of this agreement. There are a few", - "things that you can do with most Project Gutenberg-tm electronic works", - "even without complying with the full terms of this agreement. See", - "paragraph 1.C below. There are a lot of things you can do with Project", - "Gutenberg-tm electronic works if you follow the terms of this agreement", - "and help preserve free future access to Project Gutenberg-tm electronic", - "works. See paragraph 1.E below.", - "1.C. The Project Gutenberg Literary Archive Foundation (“the Foundation”", - " or PGLAF), owns a compilation copyright in the collection of Project", - "Gutenberg-tm electronic works. Nearly all the individual works in the", - "collection are in the public domain in the United States. If an", - "individual work is in the public domain in the United States and you are", - "located in the United States, we do not claim a right to prevent you from", - "copying, distributing, performing, displaying or creating derivative", - "works based on the work as long as all references to Project Gutenberg", - "are removed. Of course, we hope that you will support the Project", - "Gutenberg-tm mission of promoting free access to electronic works by", - "freely sharing Project Gutenberg-tm works in compliance with the terms of", - "this agreement for keeping the Project Gutenberg-tm name associated with", - "the work. You can easily comply with the terms of this agreement by", - "keeping this work in the same format with its attached full Project", - "Gutenberg-tm License when you share it without charge with others.", - "1.D. The copyright laws of the place where you are located also govern", - "what you can do with this work. Copyright laws in most countries are in", - "a constant state of change. If you are outside the United States, check", - "the laws of your country in addition to the terms of this agreement", - "before downloading, copying, displaying, performing, distributing or", - "creating derivative works based on this work or any other Project", - "Gutenberg-tm work. The Foundation makes no representations concerning", - "the copyright status of any work in any country outside the United", - "States.", - "1.E. Unless you have removed all references to Project Gutenberg:", - "1.E.1. The following sentence, with active links to, or other immediate", - "access to, the full Project Gutenberg-tm License must appear prominently", - "whenever any copy of a Project Gutenberg-tm work (any work on which the", - "phrase “Project Gutenberg” appears, or with which the phrase “Project", - "Gutenberg” is associated) is accessed, displayed, performed, viewed,", - "copied or distributed:", - "This eBook is for the use of anyone anywhere at no cost and with", - "almost no restrictions whatsoever. You may copy it, give it away or", - "re-use it under the terms of the Project Gutenberg License included", - "with this eBook or online at www.gutenberg.org", - "1.E.2. If an individual Project Gutenberg-tm electronic work is derived", - "from the public domain (does not contain a notice indicating that it is", - "posted with permission of the copyright holder), the work can be copied", - "and distributed to anyone in the United States without paying any fees", - "or charges. If you are redistributing or providing access to a work", - "with the phrase “Project Gutenberg” associated with or appearing on the", - "work, you must comply either with the requirements of paragraphs 1.E.1", - "through 1.E.7 or obtain permission for the use of the work and the", - "Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or", - "1.E.9.", - "1.E.3. If an individual Project Gutenberg-tm electronic work is posted", - "with the permission of the copyright holder, your use and distribution", - "must comply with both paragraphs 1.E.1 through 1.E.7 and any additional", - "terms imposed by the copyright holder. Additional terms will be linked", - "to the Project Gutenberg-tm License for all works posted with the", - "permission of the copyright holder found at the beginning of this work.", - "1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm", - "License terms from this work, or any files containing a part of this", - "work or any other work associated with Project Gutenberg-tm.", - "1.E.5. Do not copy, display, perform, distribute or redistribute this", - "electronic work, or any part of this electronic work, without", - "prominently displaying the sentence set forth in paragraph 1.E.1 with", - "active links or immediate access to the full terms of the Project", - "Gutenberg-tm License.", - "1.E.6. You may convert to and distribute this work in any binary,", - "compressed, marked up, nonproprietary or proprietary form, including any", - "word processing or hypertext form. However, if you provide access to or", - "distribute copies of a Project Gutenberg-tm work in a format other than", - "“Plain Vanilla ASCII” or other format used in the official version", - "posted on the official Project Gutenberg-tm web site (www.gutenberg.org),", - "you must, at no additional cost, fee or expense to the user, provide a", - "copy, a means of exporting a copy, or a means of obtaining a copy upon", - "request, of the work in its original “Plain Vanilla ASCII” or other", - "form. Any alternate format must include the full Project Gutenberg-tm", - "License as specified in paragraph 1.E.1.", - "1.E.7. Do not charge a fee for access to, viewing, displaying,", - "performing, copying or distributing any Project Gutenberg-tm works", - "unless you comply with paragraph 1.E.8 or 1.E.9.", - "1.E.8. You may charge a reasonable fee for copies of or providing", - "access to or distributing Project Gutenberg-tm electronic works provided", - "that", - "- You pay a royalty fee of 20% of the gross profits you derive from", - " the use of Project Gutenberg-tm works calculated using the method", - " you already use to calculate your applicable taxes. The fee is", - " owed to the owner of the Project Gutenberg-tm trademark, but he", - " has agreed to donate royalties under this paragraph to the", - " Project Gutenberg Literary Archive Foundation. Royalty payments", - " must be paid within 60 days following each date on which you", - " prepare (or are legally required to prepare) your periodic tax", - " returns. Royalty payments should be clearly marked as such and", - " sent to the Project Gutenberg Literary Archive Foundation at the", - " address specified in Section 4, “Information about donations to", - " the Project Gutenberg Literary Archive Foundation.”", - "- You provide a full refund of any money paid by a user who notifies", - " you in writing (or by e-mail) within 30 days of receipt that s/he", - " does not agree to the terms of the full Project Gutenberg-tm", - " License. You must require such a user to return or", - " destroy all copies of the works possessed in a physical medium", - " and discontinue all use of and all access to other copies of", - " Project Gutenberg-tm works.", - "- You provide, in accordance with paragraph 1.F.3, a full refund of any", - " money paid for a work or a replacement copy, if a defect in the", - " electronic work is discovered and reported to you within 90 days", - " of receipt of the work.", - "- You comply with all other terms of this agreement for free", - " distribution of Project Gutenberg-tm works.", - "1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm", - "electronic work or group of works on different terms than are set", - "forth in this agreement, you must obtain permission in writing from", - "both the Project Gutenberg Literary Archive Foundation and Michael", - "Hart, the owner of the Project Gutenberg-tm trademark. Contact the", - "Foundation as set forth in Section 3 below.", - "1.F.", - "1.F.1. Project Gutenberg volunteers and employees expend considerable", - "effort to identify, do copyright research on, transcribe and proofread", - "public domain works in creating the Project Gutenberg-tm", - "collection. Despite these efforts, Project Gutenberg-tm electronic", - "works, and the medium on which they may be stored, may contain", - "“Defects,” such as, but not limited to, incomplete, inaccurate or", - "corrupt data, transcription errors, a copyright or other intellectual", - "property infringement, a defective or damaged disk or other medium, a", - "computer virus, or computer codes that damage or cannot be read by", - "your equipment.", - "1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the “Right", - "of Replacement or Refund” described in paragraph 1.F.3, the Project", - "Gutenberg Literary Archive Foundation, the owner of the Project", - "Gutenberg-tm trademark, and any other party distributing a Project", - "Gutenberg-tm electronic work under this agreement, disclaim all", - "liability to you for damages, costs and expenses, including legal", - "fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT", - "LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE", - "PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE", - "TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE", - "LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR", - "INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH", - "DAMAGE.", - "1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a", - "defect in this electronic work within 90 days of receiving it, you can", - "receive a refund of the money (if any) you paid for it by sending a", - "written explanation to the person you received the work from. If you", - "received the work on a physical medium, you must return the medium with", - "your written explanation. The person or entity that provided you with", - "the defective work may elect to provide a replacement copy in lieu of a", - "refund. If you received the work electronically, the person or entity", - "providing it to you may choose to give you a second opportunity to", - "receive the work electronically in lieu of a refund. If the second copy", - "is also defective, you may demand a refund in writing without further", - "opportunities to fix the problem.", - "1.F.4. Except for the limited right of replacement or refund set forth", - "in paragraph 1.F.3, this work is provided to you ‘AS-IS’ WITH NO OTHER", - "WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO", - "WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE.", - "1.F.5. Some states do not allow disclaimers of certain implied", - "warranties or the exclusion or limitation of certain types of damages.", - "If any disclaimer or limitation set forth in this agreement violates the", - "law of the state applicable to this agreement, the agreement shall be", - "interpreted to make the maximum disclaimer or limitation permitted by", - "the applicable state law. The invalidity or unenforceability of any", - "provision of this agreement shall not void the remaining provisions.", - "1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the", - "trademark owner, any agent or employee of the Foundation, anyone", - "providing copies of Project Gutenberg-tm electronic works in accordance", - "with this agreement, and any volunteers associated with the production,", - "promotion and distribution of Project Gutenberg-tm electronic works,", - "harmless from all liability, costs and expenses, including legal fees,", - "that arise directly or indirectly from any of the following which you do", - "or cause to occur: (a) distribution of this or any Project Gutenberg-tm", - "work, (b) alteration, modification, or additions or deletions to any", - "Project Gutenberg-tm work, and (c) any Defect you cause.", - "Section 2. Information about the Mission of Project Gutenberg-tm", - "Project Gutenberg-tm is synonymous with the free distribution of", - "electronic works in formats readable by the widest variety of computers", - "including obsolete, old, middle-aged and new computers. It exists", - "because of the efforts of hundreds of volunteers and donations from", - "people in all walks of life.", - "Volunteers and financial support to provide volunteers with the", - "assistance they need, is critical to reaching Project Gutenberg-tm’s", - "goals and ensuring that the Project Gutenberg-tm collection will", - "remain freely available for generations to come. In 2001, the Project", - "Gutenberg Literary Archive Foundation was created to provide a secure", - "and permanent future for Project Gutenberg-tm and future generations.", - "To learn more about the Project Gutenberg Literary Archive Foundation", - "and how your efforts and donations can help, see Sections 3 and 4", - "and the Foundation web page at http://www.pglaf.org.", - "Section 3. Information about the Project Gutenberg Literary Archive", - "Foundation", - "The Project Gutenberg Literary Archive Foundation is a non profit", - "501(c)(3) educational corporation organized under the laws of the", - "state of Mississippi and granted tax exempt status by the Internal", - "Revenue Service. The Foundation’s EIN or federal tax identification", - "number is 64-6221541. Its 501(c)(3) letter is posted at", - "http://pglaf.org/fundraising. Contributions to the Project Gutenberg", - "Literary Archive Foundation are tax deductible to the full extent", - "permitted by U.S. federal laws and your state’s laws.", - "The Foundation’s principal office is located at 4557 Melan Dr. S.", - "Fairbanks, AK, 99712., but its volunteers and employees are scattered", - "throughout numerous locations. Its business office is located at", - "809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email", - "business@pglaf.org. Email contact links and up to date contact", - "information can be found at the Foundation’s web site and official", - "page at http://pglaf.org", - "For additional contact information:", - " Dr. Gregory B. Newby", - " Chief Executive and Director", - " gbnewby@pglaf.org", - "Section 4. Information about Donations to the Project Gutenberg", - "Literary Archive Foundation", - "Project Gutenberg-tm depends upon and cannot survive without wide", - "spread public support and donations to carry out its mission of", - "increasing the number of public domain and licensed works that can be", - "freely distributed in machine readable form accessible by the widest", - "array of equipment including outdated equipment. Many small donations", - "($1 to $5,000) are particularly important to maintaining tax exempt", - "status with the IRS.", - "The Foundation is committed to complying with the laws regulating", - "charities and charitable donations in all 50 states of the United", - "States. Compliance requirements are not uniform and it takes a", - "considerable effort, much paperwork and many fees to meet and keep up", - "with these requirements. We do not solicit donations in locations", - "where we have not received written confirmation of compliance. To", - "SEND DONATIONS or determine the status of compliance for any", - "particular state visit http://pglaf.org", - "While we cannot and do not solicit contributions from states where we", - "have not met the solicitation requirements, we know of no prohibition", - "against accepting unsolicited donations from donors in such states who", - "approach us with offers to donate.", - "International donations are gratefully accepted, but we cannot make", - "any statements concerning tax treatment of donations received from", - "outside the United States. U.S. laws alone swamp our small staff.", - "Please check the Project Gutenberg Web pages for current donation", - "methods and addresses. Donations are accepted in a number of other", - "ways including checks, online payments and credit card donations.", - "To donate, please visit: http://pglaf.org/donate", - "Section 5. General Information About Project Gutenberg-tm electronic", - "works.", - "Professor Michael S. Hart is the originator of the Project Gutenberg-tm", - "concept of a library of electronic works that could be freely shared", - "with anyone. For thirty years, he produced and distributed Project", - "Gutenberg-tm eBooks with only a loose network of volunteer support.", - "Project Gutenberg-tm eBooks are often created from several printed", - "editions, all of which are confirmed as Public Domain in the U.S.", - "unless a copyright notice is included. Thus, we do not necessarily", - "keep eBooks in compliance with any particular paper edition.", - "Most people start at our Web site which has the main PG search facility:", - " http://www.gutenberg.org", - "This Web site includes information about Project Gutenberg-tm,", - "including how to make donations to the Project Gutenberg Literary", - "Archive Foundation, how to help produce our new eBooks, and how to", - "subscribe to our email newsletter to hear about new eBooks.", - "" -}; diff --git a/deps/libcaption/src/avc.c b/deps/libcaption/src/avc.c deleted file mode 100644 index 4b46ef6ba..000000000 --- a/deps/libcaption/src/avc.c +++ /dev/null @@ -1,595 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ - -#include "avc.h" -#include -#include -#include -#include -//////////////////////////////////////////////////////////////////////////////// -// AVC RBSP Methods -// TODO move the to a avcutils file -static size_t _find_emulation_prevention_byte (const uint8_t* data, size_t size) -{ - size_t offset = 2; - - while (offset < size) { - if (0 == data[offset]) { - // 0 0 X 3 //; we know X is zero - offset += 1; - } else if (3 != data[offset]) { - // 0 0 X 0 0 3; we know X is not 0 and not 3 - offset += 3; - } else if (0 != data[offset-1]) { - // 0 X 0 0 3 - offset += 2; - } else if (0 != data[offset-2]) { - // X 0 0 3 - offset += 1; - } else { - // 0 0 3 - return offset; - } - } - - return size; -} - -static size_t _copy_to_rbsp (uint8_t* destData, size_t destSize, const uint8_t* sorcData, size_t sorcSize) -{ - size_t toCopy, totlSize = 0; - - for (;;) { - if (destSize >= sorcSize) { - return 0; - } - - // The following line IS correct! We want to look in sorcData up to destSize bytes - // We know destSize is smaller than sorcSize because of the previous line - toCopy = _find_emulation_prevention_byte (sorcData,destSize); - memcpy (destData, sorcData, toCopy); - totlSize += toCopy; - destData += toCopy; - destSize -= toCopy; - - if (0 == destSize) { - return totlSize; - } - - // skip the emulation prevention byte - totlSize += 1; - sorcData += toCopy + 1; - sorcSize -= toCopy + 1; - } - - return 0; -} -//////////////////////////////////////////////////////////////////////////////// -static inline size_t _find_emulated (uint8_t* data, size_t size) -{ - size_t offset = 2; - - while (offset < size) { - if (3 < data[offset]) { - // 0 0 X; we know X is not 0, 1, 2 or 3 - offset += 3; - } else if (0 != data[offset-1]) { - // 0 X 0 0 1 - offset += 2; - } else if (0 != data[offset-2]) { - // X 0 0 1 - offset += 1; - } else { - // 0 0 0, 0 0 1 - return offset; - } - } - - return size; -} - -size_t _copy_from_rbsp (uint8_t* data, uint8_t* payloadData, size_t payloadSize) -{ - size_t total = 0; - - while (payloadSize) { - size_t bytes = _find_emulated (payloadData,payloadSize); - - if (bytes > payloadSize) { - return 0; - } - - memcpy (data, payloadData, bytes); - - if (bytes == payloadSize) { - return total + bytes; - } - - data[bytes] = 3; // insert emulation prevention byte - data += bytes + 1; total += bytes + 1; - payloadData += bytes; payloadSize -= bytes; - } - - return total; -} -//////////////////////////////////////////////////////////////////////////////// -struct _sei_message_t { - size_t size; - sei_msgtype_t type; - struct _sei_message_t* next; -}; - -sei_message_t* sei_message_next (sei_message_t* msg) { return ( (struct _sei_message_t*) msg)->next; } -sei_msgtype_t sei_message_type (sei_message_t* msg) { return ( (struct _sei_message_t*) msg)->type; } -size_t sei_message_size (sei_message_t* msg) { return ( (struct _sei_message_t*) msg)->size; } -uint8_t* sei_message_data (sei_message_t* msg) { return ( (uint8_t*) msg) + sizeof (struct _sei_message_t); } -void sei_message_free (sei_message_t* msg) { if (msg) { free (msg); } } - -sei_message_t* sei_message_new (sei_msgtype_t type, uint8_t* data, size_t size) -{ - struct _sei_message_t* msg = (struct _sei_message_t*) malloc (sizeof (struct _sei_message_t) + size); - msg->next = 0; msg->type = type; msg->size = size; - - if (data) { - memcpy (sei_message_data (msg), data, size); - } else { - memset (sei_message_data (msg), 0, size); - } - - return (sei_message_t*) msg; -} -//////////////////////////////////////////////////////////////////////////////// -void sei_init (sei_t* sei) -{ - sei->dts = -1; - sei->cts = -1; - sei->head = 0; - sei->tail = 0; -} - -void sei_message_append (sei_t* sei, sei_message_t* msg) -{ - if (0 == sei->head) { - sei->head = msg; - sei->tail = msg; - } else { - sei->tail->next = msg; - sei->tail = msg; - } -} - -void sei_free (sei_t* sei) -{ - sei_message_t* tail; - - while (sei->head) { - tail = sei->head->next; - free (sei->head); - sei->head = tail; - } - - sei_init (sei); -} - -void sei_dump (sei_t* sei) -{ - fprintf (stderr,"SEI %p\n", sei); - sei_dump_messages (sei->head); -} - -void sei_dump_messages (sei_message_t* head) -{ - cea708_t cea708; - sei_message_t* msg; - cea708_init (&cea708); - - for (msg = head ; msg ; msg = sei_message_next (msg)) { - uint8_t* data = sei_message_data (msg); - size_t size = sei_message_size (msg); - fprintf (stderr,"-- Message %p\n-- Message Type: %d\n-- Message Size: %d\n", data, sei_message_type (msg), (int) size); - - while (size) { - fprintf (stderr,"%02X ", *data); - ++data; --size; - } - - fprintf (stderr,"\n"); - - if (sei_type_user_data_registered_itu_t_t35 == sei_message_type (msg)) { - cea708_parse (sei_message_data (msg), sei_message_size (msg), &cea708); - cea708_dump (&cea708); - } - - - } -} - -//////////////////////////////////////////////////////////////////////////////// -size_t sei_render_size (sei_t* sei) -{ - size_t size = 2; // nalu_type + stop bit - sei_message_t* msg; - - for (msg = sei_message_head (sei) ; msg ; msg = sei_message_next (msg)) { - size += 1 + (msg->type / 255); - size += 1 + (msg->size / 255); - size += 1 + (msg->size * 4/3); - } - - return size; -} - -// we can safely assume sei_render_size() bytes have been allocated for data -size_t sei_render (sei_t* sei, uint8_t* data) -{ - size_t escaped_size, size = 2; // nalu_type + stop bit - sei_message_t* msg; - (*data) = 6; ++data; - - for (msg = sei_message_head (sei) ; msg ; msg = sei_message_next (msg)) { - int payloadType = sei_message_type (msg); - int payloadSize = (int) sei_message_size (msg); - uint8_t* payloadData = sei_message_data (msg); - - while (255 <= payloadType) { - (*data) = 255; - ++data; ++size; - payloadType -= 255; - } - - (*data) = payloadType; - ++data; ++size; - - while (255 <= payloadSize) { - (*data) = 255; - ++data; ++size; - payloadSize -= 255; - } - - (*data) = payloadSize; - ++data; ++size; - - if (0 >= (escaped_size = _copy_from_rbsp (data,payloadData,payloadSize))) { - return 0; - } - - data += escaped_size; - size += escaped_size; - } - - // write stop bit and return - (*data) = 0x80; - return size; -} - -uint8_t* sei_render_alloc (sei_t* sei, size_t* size) -{ - size_t aloc = sei_render_size (sei); - uint8_t* data = malloc (aloc); - (*size) = sei_render (sei, data); - return data; -} - -//////////////////////////////////////////////////////////////////////////////// -int sei_parse_nalu (sei_t* sei, const uint8_t* data, size_t size, double dts, double cts) -{ - assert (0<=cts); // cant present before decode - sei->dts = dts; - sei->cts = cts; - int ret = 0; - - if (0 == data || 0 == size) { - return 0; - } - - uint8_t nal_unit_type = (*data) & 0x1F; - ++data; --size; - - if (6 != nal_unit_type) { - return 0; - } - - // SEI may contain more than one payload - while (1timestamp = sei->dts + sei->cts; - frame->duration = 0; - } - - return status; -} - -//////////////////////////////////////////////////////////////////////////////// -#define DEFAULT_CHANNEL 0 - -void sei_append_708 (sei_t* sei, cea708_t* cea708) -{ - sei_message_t* msg = sei_message_new (sei_type_user_data_registered_itu_t_t35, 0, CEA608_MAX_SIZE); - msg->size = cea708_render (cea708, sei_message_data (msg), sei_message_size (msg)); - sei_message_append (sei,msg); - // cea708_dump (cea708); - cea708_init (cea708); // will confgure using HLS compatiable defaults -} - -// This should be moved to 708.c -// This works for popon, but bad for paint on and roll up -// Please understand this function before you try to use it, setting null values have different effects than you may assume -void sei_encode_eia608 (sei_t* sei, cea708_t* cea708, uint16_t cc_data) -{ - // This one is full, flush and init a new one - // shoudl this be 32? I cant remember - if (31 == cea708->user_data.cc_count) { - sei_append_708 (sei,cea708); - } - - if (0 == cea708->user_data.cc_count) { // This is a new 708 header, but a continuation of a 608 stream - cea708_add_cc_data (cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command (eia608_control_resume_caption_loading, DEFAULT_CHANNEL)); - } - - if (0 == cc_data) { // Finished - sei_encode_eia608 (sei,cea708,eia608_control_command (eia608_control_end_of_caption, DEFAULT_CHANNEL)); - sei_append_708 (sei,cea708); - return; - } - - cea708_add_cc_data (cea708, 1, cc_type_ntsc_cc_field_1, cc_data); -} -//////////////////////////////////////////////////////////////////////////////// -// TODO use alternate charcters instead of always using space before extended charcters -// TODO rewrite this function with better logic -int sei_from_caption_frame (sei_t* sei, caption_frame_t* frame) -{ - int r,c; - cea708_t cea708; - const char* data; - uint16_t prev_cc_data; - - cea708_init (&cea708); // set up a new popon frame - cea708_add_cc_data (&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command (eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL)); - cea708_add_cc_data (&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command (eia608_control_resume_caption_loading, DEFAULT_CHANNEL)); - - for (r=0; rdts = frame->timestamp; // assumes in order frames - // sei_dump (sei); - return 1; -} -//////////////////////////////////////////////////////////////////////////////// -static int avc_is_start_code (const uint8_t* data, int size, int* len) -{ - if (3 > size) { - return -1; - } - - if (1 < data[2]) { - return 3; - } - - if (0 != data[1]) { - return 2; - } - - if (0 == data[0]) { - if (1 == data[2]) { - *len = 3; - return 0; - } - - if (4 <= size && 1 == data[3]) { - *len = 4; - return 0; - } - } - - return 1; -} - - -static int avc_find_start_code (const uint8_t* data, int size, int* len) -{ - int pos = 0; - - for (;;) { - // is pos pointing to a start code? - int isc = avc_is_start_code (data + pos, size - pos, len); - - if (0 < isc) { - pos += isc; - } else if (0 > isc) { - // No start code found - return isc; - } else { - // Start code found at pos - return pos; - } - } -} - - -static int avc_find_start_code_increnental (const uint8_t* data, int size, int prev_size, int* len) -{ - int offset = (3 <= prev_size) ? (prev_size - 3) : 0; - int pos = avc_find_start_code (data + offset, size - offset, len); - - if (0 <= pos) { - return pos + offset; - } - - return pos; -} - -void avcnalu_init (avcnalu_t* nalu) -{ - memset (nalu,0,sizeof (avcnalu_t)); -} - -int avcnalu_parse_annexb (avcnalu_t* nalu, const uint8_t** data, size_t* size) -{ - int scpos, sclen; - int new_size = (int) (nalu->size + (*size)); - - if (new_size > MAX_NALU_SIZE) { - (*size) = nalu->size = 0; - return LIBCAPTION_ERROR; - } - - memcpy (&nalu->data[nalu->size], (*data), (*size)); - scpos = avc_find_start_code_increnental (&nalu->data[0], new_size, (int) nalu->size, &sclen); - - if (0<=scpos) { - (*data) += (scpos - nalu->size) + sclen; - (*size) -= (scpos - nalu->size) + sclen; - nalu->size = scpos; - return 0 < nalu->size ? LIBCAPTION_READY : LIBCAPTION_OK; - } else { - (*size) = 0; - nalu->size = new_size; - return LIBCAPTION_OK; - } -} diff --git a/deps/libcaption/src/caption.c b/deps/libcaption/src/caption.c index 9af6a510a..734f28ff3 100644 --- a/deps/libcaption/src/caption.c +++ b/deps/libcaption/src/caption.c @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -21,78 +21,53 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ +#include "caption.h" +#include "eia608.h" #include "utf8.h" #include "xds.h" -#include "eia608.h" -#include "caption.h" #include #include //////////////////////////////////////////////////////////////////////////////// -void caption_frame_buffer_clear (caption_frame_buffer_t* buff) +void caption_frame_buffer_clear(caption_frame_buffer_t* buff) { - memset (buff,0,sizeof (caption_frame_buffer_t)); + memset(buff, 0, sizeof(caption_frame_buffer_t)); } -void caption_frame_state_clear (caption_frame_t* frame) +void caption_frame_state_clear(caption_frame_t* frame) { + frame->write = 0; frame->timestamp = -1; - frame->duration = 0; - frame->state = (caption_frame_state_t) {0,0,0,0,0,0,0}; // clear global state + frame->state = (caption_frame_state_t){ 0, 0, 0, SCREEN_ROWS - 1, 0, 0 }; // clear global state } -void caption_frame_init (caption_frame_t* frame) +void caption_frame_init(caption_frame_t* frame) { - caption_frame_state_clear (frame); - xds_init (&frame->xds); - caption_frame_buffer_clear (&frame->back); - caption_frame_buffer_clear (&frame->front); + xds_init(&frame->xds); + caption_frame_state_clear(frame); + caption_frame_buffer_clear(&frame->back); + caption_frame_buffer_clear(&frame->front); } //////////////////////////////////////////////////////////////////////////////// -#define CAPTION_CLEAR 0 -#define CAPTION_POP_ON 2 -#define CAPTION_PAINT_ON 3 -#define CAPTION_ROLL_UP 4 -//////////////////////////////////////////////////////////////////////////////// // Helpers -static caption_frame_cell_t* frame_buffer_cell (caption_frame_buffer_t* buff, int row, int col) -{ - return &buff->cell[row][col]; -} - -static caption_frame_buffer_t* frame_write_buffer (caption_frame_t* frame) +static caption_frame_cell_t* frame_buffer_cell(caption_frame_buffer_t* buff, int row, int col) { - if (CAPTION_POP_ON == frame->state.mod) { - return &frame->back; - } else if (CAPTION_PAINT_ON == frame->state.mod || CAPTION_ROLL_UP == frame->state.mod) { - return &frame->front; - } else { + if (!buff || 0 > row || SCREEN_ROWS <= row || 0 > col || SCREEN_COLS <= col) { return 0; } -} - -static caption_frame_cell_t* frame_cell (caption_frame_t* frame, int row, int col) -{ - return frame_buffer_cell (&frame->front,row,col); -} -static caption_frame_cell_t* frame_cell_get (caption_frame_t* frame) -{ - return frame_cell (frame, frame->state.row, frame->state.col); + return &buff->cell[row][col]; } -//////////////////////////////////////////////////////////////////////////////// -uint16_t _eia608_from_utf8 (const char* s); // function is in eia608.c.re2c -int caption_frame_write_char (caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const char* c) +uint16_t _eia608_from_utf8(const char* s); // function is in eia608.c.re2c +int caption_frame_write_char(caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const char* c) { - caption_frame_buffer_t* buff = frame_write_buffer (frame); - - if (!buff || ! _eia608_from_utf8 (c)) { + if (!frame->write || !_eia608_from_utf8(c)) { return 0; } - caption_frame_cell_t* cell = frame_buffer_cell (buff,row,col); + caption_frame_cell_t* cell = frame_buffer_cell(frame->write, row, col); - if (utf8_char_copy (&cell->data[0],c)) { + if (cell && utf8_char_copy(&cell->data[0], c)) { cell->uln = underline; cell->sty = style; return 1; @@ -101,9 +76,10 @@ int caption_frame_write_char (caption_frame_t* frame, int row, int col, eia608_s return 0; } -const utf8_char_t* caption_frame_read_char (caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline) +const utf8_char_t* caption_frame_read_char(caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline) { - caption_frame_cell_t* cell = frame_cell (frame, row, col); + // always read from front + caption_frame_cell_t* cell = frame_buffer_cell(&frame->front, row, col); if (!cell) { if (style) { @@ -130,57 +106,54 @@ const utf8_char_t* caption_frame_read_char (caption_frame_t* frame, int row, int //////////////////////////////////////////////////////////////////////////////// // Parsing -libcaption_stauts_t caption_frame_carriage_return (caption_frame_t* frame) +libcaption_stauts_t caption_frame_carriage_return(caption_frame_t* frame) { - caption_frame_buffer_t* buff = frame_write_buffer (frame); - - if (!buff) { - return LIBCAPTION_OK; + if (0 > frame->state.row || SCREEN_ROWS <= frame->state.row) { + return LIBCAPTION_ERROR; } - int r = frame->state.row - (frame->state.rup-1); + int r = frame->state.row - (frame->state.rup - 1); - if (0 >= r || CAPTION_ROLL_UP != frame->state.mod) { + if (0 >= r || !caption_frame_rollup(frame)) { return LIBCAPTION_OK; } for (; r < SCREEN_ROWS; ++r) { - uint8_t* dst = (uint8_t*) frame_buffer_cell (buff,r-1,0); - uint8_t* src = (uint8_t*) frame_buffer_cell (buff,r-0,0); - memcpy (dst,src,sizeof (caption_frame_cell_t) * SCREEN_COLS); + uint8_t* dst = (uint8_t*)frame_buffer_cell(frame->write, r - 1, 0); + uint8_t* src = (uint8_t*)frame_buffer_cell(frame->write, r - 0, 0); + memcpy(dst, src, sizeof(caption_frame_cell_t) * SCREEN_COLS); } - memset (frame_buffer_cell (buff,SCREEN_ROWS-1,0), 0,sizeof (caption_frame_cell_t) * SCREEN_COLS); + frame->state.col = 0; + caption_frame_cell_t* cell = frame_buffer_cell(frame->write, SCREEN_ROWS - 1, 0); + memset(cell, 0, sizeof(caption_frame_cell_t) * SCREEN_COLS); return LIBCAPTION_OK; } //////////////////////////////////////////////////////////////////////////////// -libcaption_stauts_t eia608_write_char (caption_frame_t* frame, char* c) +libcaption_stauts_t eia608_write_char(caption_frame_t* frame, char* c) { - if (0 == c || 0 == c[0] || - SCREEN_ROWS <= frame->state.row || 0 > frame->state.row || - SCREEN_COLS <= frame->state.col || 0 > frame->state.col) { + if (0 == c || 0 == c[0] || SCREEN_ROWS <= frame->state.row || 0 > frame->state.row || SCREEN_COLS <= frame->state.col || 0 > frame->state.col) { // NO-OP - } else if (caption_frame_write_char (frame,frame->state.row,frame->state.col,frame->state.sty,frame->state.uln, c)) { + } else if (caption_frame_write_char(frame, frame->state.row, frame->state.col, frame->state.sty, frame->state.uln, c)) { frame->state.col += 1; } return LIBCAPTION_OK; } -libcaption_stauts_t caption_frame_end (caption_frame_t* frame) +libcaption_stauts_t caption_frame_end(caption_frame_t* frame) { - memcpy (&frame->front,&frame->back,sizeof (caption_frame_buffer_t)); - caption_frame_state_clear (frame); - caption_frame_buffer_clear (&frame->back); + memcpy(&frame->front, &frame->back, sizeof(caption_frame_buffer_t)); + caption_frame_buffer_clear(&frame->back); // This is required return LIBCAPTION_READY; } -libcaption_stauts_t caption_frame_decode_preamble (caption_frame_t* frame, uint16_t cc_data) +libcaption_stauts_t caption_frame_decode_preamble(caption_frame_t* frame, uint16_t cc_data) { eia608_style_t sty; int row, col, chn, uln; - if (eia608_parse_preamble (cc_data, &row, &col, &sty, &chn, &uln)) { + if (eia608_parse_preamble(cc_data, &row, &col, &sty, &chn, &uln)) { frame->state.row = row; frame->state.col = col; frame->state.sty = sty; @@ -190,12 +163,12 @@ libcaption_stauts_t caption_frame_decode_preamble (caption_frame_t* frame, uint1 return LIBCAPTION_OK; } -libcaption_stauts_t caption_frame_decode_midrowchange (caption_frame_t* frame, uint16_t cc_data) +libcaption_stauts_t caption_frame_decode_midrowchange(caption_frame_t* frame, uint16_t cc_data) { eia608_style_t sty; int chn, unl; - if (eia608_parse_midrowchange (cc_data,&chn,&sty,&unl)) { + if (eia608_parse_midrowchange(cc_data, &chn, &sty, &unl)) { frame->state.sty = sty; frame->state.uln = unl; } @@ -203,75 +176,83 @@ libcaption_stauts_t caption_frame_decode_midrowchange (caption_frame_t* frame, u return LIBCAPTION_OK; } -libcaption_stauts_t caption_frame_backspace (caption_frame_t* frame) +libcaption_stauts_t caption_frame_backspace(caption_frame_t* frame) { // do not reverse wrap (tw 28:20) frame->state.col = (0 < frame->state.col) ? (frame->state.col - 1) : 0; - caption_frame_write_char (frame,frame->state.row,frame->state.col,eia608_style_white,0,EIA608_CHAR_NULL); + caption_frame_write_char(frame, frame->state.row, frame->state.col, eia608_style_white, 0, EIA608_CHAR_NULL); return LIBCAPTION_READY; } -libcaption_stauts_t caption_frame_decode_control (caption_frame_t* frame, uint16_t cc_data) +libcaption_stauts_t caption_frame_delete_to_end_of_row(caption_frame_t* frame) +{ + int c; + if (frame->write) { + for (c = frame->state.col; c < SCREEN_COLS; ++c) { + caption_frame_write_char(frame, frame->state.row, c, eia608_style_white, 0, EIA608_CHAR_NULL); + } + } + + // TODO test this and replace loop + // uint8_t* dst = (uint8_t*)frame_buffer_cell(frame->write, frame->state.row, frame->state.col); + // memset(dst,0,sizeof(caption_frame_cell_t) * (SCREEN_COLS - frame->state.col - 1)) + + return LIBCAPTION_READY; +} + +libcaption_stauts_t caption_frame_decode_control(caption_frame_t* frame, uint16_t cc_data) { int cc; - eia608_control_t cmd = eia608_parse_control (cc_data,&cc); + eia608_control_t cmd = eia608_parse_control(cc_data, &cc); switch (cmd) { // PAINT ON case eia608_control_resume_direct_captioning: frame->state.rup = 0; - frame->state.mod = CAPTION_PAINT_ON; + frame->write = &frame->front; return LIBCAPTION_OK; case eia608_control_erase_display_memory: - caption_frame_buffer_clear (&frame->front); - return LIBCAPTION_OK; + caption_frame_buffer_clear(&frame->front); + return LIBCAPTION_READY; // ROLL-UP case eia608_control_roll_up_2: frame->state.rup = 1; - frame->state.mod = CAPTION_ROLL_UP; + frame->write = &frame->front; return LIBCAPTION_OK; case eia608_control_roll_up_3: frame->state.rup = 2; - frame->state.mod = CAPTION_ROLL_UP; + frame->write = &frame->front; return LIBCAPTION_OK; case eia608_control_roll_up_4: frame->state.rup = 3; - frame->state.mod = CAPTION_ROLL_UP; + frame->write = &frame->front; return LIBCAPTION_OK; case eia608_control_carriage_return: - return caption_frame_carriage_return (frame); + return caption_frame_carriage_return(frame); // Corrections (Is this only valid as part of paint on?) case eia608_control_backspace: - return caption_frame_backspace (frame); - - case eia608_control_delete_to_end_of_row: { - int c; - - for (c = frame->state.col ; c < SCREEN_COLS ; ++c) { - caption_frame_write_char (frame,frame->state.row,c,eia608_style_white,0,EIA608_CHAR_NULL); - } - } - - return LIBCAPTION_READY; + return caption_frame_backspace(frame); + case eia608_control_delete_to_end_of_row: + return caption_frame_delete_to_end_of_row(frame); // POP ON case eia608_control_resume_caption_loading: frame->state.rup = 0; - frame->state.mod = CAPTION_POP_ON; + frame->write = &frame->back; return LIBCAPTION_OK; case eia608_control_erase_non_displayed_memory: - caption_frame_buffer_clear (&frame->back); + caption_frame_buffer_clear(&frame->back); return LIBCAPTION_OK; case eia608_control_end_of_caption: - return caption_frame_end (frame); + return caption_frame_end(frame); // cursor positioning case eia608_tab_offset_0: @@ -291,201 +272,188 @@ libcaption_stauts_t caption_frame_decode_control (caption_frame_t* frame, uint16 } } -libcaption_stauts_t caption_frame_decode_text (caption_frame_t* frame, uint16_t cc_data) +libcaption_stauts_t caption_frame_decode_text(caption_frame_t* frame, uint16_t cc_data) { int chan; char char1[5], char2[5]; - size_t chars = eia608_to_utf8 (cc_data, &chan, &char1[0], &char2[0]); + size_t chars = eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]); - if (eia608_is_westeu (cc_data)) { + if (eia608_is_westeu(cc_data)) { // Extended charcters replace the previous charcter for back compatibility - caption_frame_backspace (frame); + caption_frame_backspace(frame); } if (0 < chars) { - eia608_write_char (frame,char1); + eia608_write_char(frame, char1); } if (1 < chars) { - eia608_write_char (frame,char2); + eia608_write_char(frame, char2); } return LIBCAPTION_OK; } -libcaption_stauts_t caption_frame_decode (caption_frame_t* frame, uint16_t cc_data, double timestamp) +libcaption_stauts_t caption_frame_decode(caption_frame_t* frame, uint16_t cc_data, double timestamp) { - libcaption_stauts_t status = LIBCAPTION_OK; - - if (!eia608_parity_varify (cc_data)) { - return LIBCAPTION_ERROR; + if (!eia608_parity_varify(cc_data)) { + frame->status = LIBCAPTION_ERROR; + return frame->status; } - if (eia608_is_padding (cc_data)) { - return LIBCAPTION_OK; + if (eia608_is_padding(cc_data)) { + frame->status = LIBCAPTION_OK; + return frame->status; } - // skip duplicate controll commands. We also skip duplicate specialna to match the behaviour of iOS/vlc - if ( (eia608_is_specialna (cc_data) || eia608_is_control (cc_data)) && cc_data == frame->state.cc_data) { - return LIBCAPTION_OK; + if (0 > frame->timestamp || LIBCAPTION_READY == frame->status) { + frame->timestamp = timestamp; } - if (0 > frame->timestamp && 0 < timestamp) { - frame->timestamp = timestamp; + // skip duplicate controll commands. We also skip duplicate specialna to match the behaviour of iOS/vlc + if ((eia608_is_specialna(cc_data) || eia608_is_control(cc_data)) && cc_data == frame->state.cc_data) { + frame->status = LIBCAPTION_OK; + return frame->status; } frame->state.cc_data = cc_data; if (frame->xds.state) { - status = xds_decode (&frame->xds,cc_data); - } else if (eia608_is_xds (cc_data)) { - status = xds_decode (&frame->xds,cc_data); - } else if (eia608_is_control (cc_data)) { - status = caption_frame_decode_control (frame,cc_data); - } else if (eia608_is_basicna (cc_data) || - eia608_is_specialna (cc_data) || - eia608_is_westeu (cc_data)) { + frame->status = xds_decode(&frame->xds, cc_data); + } else if (eia608_is_xds(cc_data)) { + frame->status = xds_decode(&frame->xds, cc_data); + } else if (eia608_is_control(cc_data)) { + frame->status = caption_frame_decode_control(frame, cc_data); + } else if (eia608_is_basicna(cc_data) || eia608_is_specialna(cc_data) || eia608_is_westeu(cc_data)) { // Don't decode text if we dont know what mode we are in. - if (CAPTION_CLEAR == frame->state.mod) { - return LIBCAPTION_OK; + if (!frame->write) { + frame->status = LIBCAPTION_OK; + return frame->status; } - status = caption_frame_decode_text (frame,cc_data); + frame->status = caption_frame_decode_text(frame, cc_data); // If we are in paint on mode, display immiditally - if (1 == status && (CAPTION_PAINT_ON == frame->state.mod || CAPTION_ROLL_UP == frame->state.mod)) { - status = LIBCAPTION_READY; + if (LIBCAPTION_OK == frame->status && caption_frame_painton(frame)) { + frame->status = LIBCAPTION_READY; } - } else if (eia608_is_preamble (cc_data)) { - status = caption_frame_decode_preamble (frame,cc_data); - } else if (eia608_is_midrowchange (cc_data)) { - status = caption_frame_decode_midrowchange (frame,cc_data); + } else if (eia608_is_preamble(cc_data)) { + frame->status = caption_frame_decode_preamble(frame, cc_data); + } else if (eia608_is_midrowchange(cc_data)) { + frame->status = caption_frame_decode_midrowchange(frame, cc_data); } - return status; + return frame->status; } //////////////////////////////////////////////////////////////////////////////// -int caption_frame_from_text (caption_frame_t* frame, const utf8_char_t* data) +int caption_frame_from_text(caption_frame_t* frame, const utf8_char_t* data) { - int r, c, chan = 0; - ssize_t size = (ssize_t) strlen (data); - size_t char_count, char_length, line_length = 0, trimmed_length = 0; - caption_frame_init (frame); - frame->state.mod = CAPTION_POP_ON; - - for (r = 0 ; 0 < size && SCREEN_ROWS > r ; ++r) { - const utf8_char_t* cap_data = data; - line_length = utf8_line_length (cap_data); - trimmed_length = utf8_trimmed_length (cap_data,line_length); - char_count = utf8_char_count (cap_data,trimmed_length); - - // If char_count is greater than one line can display, split it. - if (SCREEN_COLS < char_count) { - char_count = utf8_wrap_length (cap_data,SCREEN_COLS); - line_length = utf8_string_length (cap_data,char_count+1); + ssize_t size = (ssize_t)strlen(data); + caption_frame_init(frame); + frame->write = &frame->back; + + for (size_t r = 0; (*data) && size && r < SCREEN_ROWS;) { + // skip whitespace at start of line + while (size && utf8_char_whitespace(data)) { + size_t s = utf8_char_length(data); + data += s, size -= (ssize_t)s; } - // Write the line - for (c = 0 ; c < (int) char_count ; ++c) { - caption_frame_write_char (frame,r,c,eia608_style_white,0,&cap_data[0]); - char_length = utf8_char_length (cap_data); - cap_data += char_length; + // get charcter count for wrap (or orest of line) + utf8_size_t char_count = utf8_wrap_length(data, SCREEN_COLS); + // write to caption frame + for (size_t c = 0; c < char_count; ++c) { + size_t char_length = utf8_char_length(data); + caption_frame_write_char(frame, (int)r, (int)c, eia608_style_white, 0, data); + data += char_length, size -= (ssize_t)char_length; } - data += line_length; - size -= (ssize_t) line_length; + r += char_count ? 1 : 0; // Update row num only if not blank } - caption_frame_end (frame); + caption_frame_end(frame); return 0; } //////////////////////////////////////////////////////////////////////////////// -void caption_frame_to_text (caption_frame_t* frame, utf8_char_t* data) +size_t caption_frame_to_text(caption_frame_t* frame, utf8_char_t* data) { - int r, c, x, s, uln; + int r, c, uln, crlf = 0, count = 0; + size_t s, size = 0; eia608_style_t sty; - - data[0] = 0; - - for (r = 0 ; r < SCREEN_ROWS ; ++r) { - for (c = 0, x = 0 ; c < SCREEN_COLS ; ++c) { - const char* chr = caption_frame_read_char (frame, r, c, &sty, &uln); - - if (0 < (s = (int) utf8_char_copy (data,chr))) { - ++x; data += s; + (*data) = '\0'; + + for (r = 0; r < SCREEN_ROWS; ++r) { + crlf += count, count = 0; + for (c = 0; c < SCREEN_COLS; ++c) { + const utf8_char_t* chr = caption_frame_read_char(frame, r, c, &sty, &uln); + // dont start a new line until we encounter at least one printable character + if (0 < utf8_char_length(chr) && (0 < count || !utf8_char_whitespace(chr))) { + if (0 < crlf) { + memcpy(data, "\r\n\0", 3); + data += 2, size += 2, crlf = 0; + } + + s = utf8_char_copy(data, chr); + data += s, size += s, ++count; } } - - if (x) { - strcpy ( (char*) data,"\r\n"); - data += 2; - } } + + return size; } //////////////////////////////////////////////////////////////////////////////// -size_t caption_frame_dump_buffer (caption_frame_t* frame, utf8_char_t* buf) +size_t caption_frame_dump_buffer(caption_frame_t* frame, utf8_char_t* buf) { int r, c; size_t bytes, total = 0; - bytes = sprintf (buf, " row: %d\tcol: %d\n mode: %s\troll-up: %d\n", - frame->state.row, frame->state.col, - eia608_mode_map[frame->state.mod],frame->state.rup?1+frame->state.rup:0); - total += bytes; buf += bytes; - bytes = sprintf (buf, " 00000000001111111111222222222233\n 01234567890123456789012345678901\n %s--------------------------------%s\n", - EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT); - total += bytes; buf += bytes; - - for (r = 0 ; r < SCREEN_ROWS ; ++r) { - bytes = sprintf (buf, "%02d%s", r, EIA608_CHAR_VERTICAL_LINE); - total += bytes; buf += bytes; - - for (c = 0 ; c < SCREEN_COLS ; ++c) { - caption_frame_cell_t* cell = frame_cell (frame,r,c); - bytes = utf8_char_copy (buf, (0==cell->data[0]) ?EIA608_CHAR_SPACE:&cell->data[0]); - total += bytes; buf += bytes; + bytes = sprintf(buf, " timestamp: %f\n row: %02d col: %02d roll-up: %d\n", + frame->timestamp, frame->state.row, frame->state.col, caption_frame_rollup(frame)); + total += bytes, buf += bytes; + bytes = sprintf(buf, " 00000000001111111111222222222233\t 00000000001111111111222222222233\n" + " 01234567890123456789012345678901\t 01234567890123456789012345678901\n" + " %s--------------------------------%s\t %s--------------------------------%s\n", + EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, + EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT); + total += bytes; + buf += bytes; + + for (r = 0; r < SCREEN_ROWS; ++r) { + bytes = sprintf(buf, "%02d%s", r, EIA608_CHAR_VERTICAL_LINE); + total += bytes, buf += bytes; + + // front buffer + for (c = 0; c < SCREEN_COLS; ++c) { + caption_frame_cell_t* cell = frame_buffer_cell(&frame->front, r, c); + bytes = utf8_char_copy(buf, (!cell || 0 == cell->data[0]) ? EIA608_CHAR_SPACE : &cell->data[0]); + total += bytes, buf += bytes; + } + + bytes = sprintf(buf, "%s\t%02d%s", EIA608_CHAR_VERTICAL_LINE, r, EIA608_CHAR_VERTICAL_LINE); + total += bytes, buf += bytes; + + // back buffer + for (c = 0; c < SCREEN_COLS; ++c) { + caption_frame_cell_t* cell = frame_buffer_cell(&frame->back, r, c); + bytes = utf8_char_copy(buf, (!cell || 0 == cell->data[0]) ? EIA608_CHAR_SPACE : &cell->data[0]); + total += bytes, buf += bytes; } - bytes = sprintf (buf, "%s\n", EIA608_CHAR_VERTICAL_LINE); - total += bytes; buf += bytes; + bytes = sprintf(buf, "%s\n", EIA608_CHAR_VERTICAL_LINE); + total += bytes, buf += bytes; } - bytes = sprintf (buf, " %s--------------------------------%s\n", - EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT); - total += bytes; buf += bytes; + bytes = sprintf(buf, " %s--------------------------------%s\t %s--------------------------------%s\n", + EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, + EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT); + total += bytes, buf += bytes; return total; } -void caption_frame_dump (caption_frame_t* frame) +void caption_frame_dump(caption_frame_t* frame) { utf8_char_t buff[CAPTION_FRAME_DUMP_BUF_SIZE]; - size_t size = caption_frame_dump_buffer (frame, buff); - fprintf (stderr,"%s\n", buff); -} - -size_t caption_frame_json (caption_frame_t* frame, utf8_char_t* buf) -{ - size_t bytes, total = 0; - int r,c,count = 0; - bytes = sprintf (buf, "{\"format\":\"eia608\",\"mode\":\"%s\",\"rollUp\":%d,\"data\":[", - eia608_mode_map[frame->state.mod],frame->state.rup?1+frame->state.rup:0); - total += bytes; buf += bytes; - - for (r = 0 ; r < SCREEN_ROWS ; ++r) { - for (c = 0 ; c < SCREEN_COLS ; ++c) { - caption_frame_cell_t* cell = frame_cell (frame,r,c); - - if (0 != cell->data[0]) { - const char* data = ('"' == cell->data[0]) ?"\\\"": (const char*) &cell->data[0]; //escape quote - bytes = sprintf (buf, "%s\n{\"row\":%d,\"col\":%d,\"char\":\"%s\",\"style\":\"%s\"}", - (0sty]); - total += bytes; buf += bytes; ++count; - } - } - } - - bytes = sprintf (buf, "\n]}\n"); - total += bytes; buf += bytes; - return total; + caption_frame_dump_buffer(frame, buff); + fprintf(stderr, "%s\n", buff); } diff --git a/deps/libcaption/src/cea708.c b/deps/libcaption/src/cea708.c index 8eb706861..2405c9bf1 100644 --- a/deps/libcaption/src/cea708.c +++ b/deps/libcaption/src/cea708.c @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -22,92 +22,138 @@ /* THE SOFTWARE. */ /**********************************************************************************************/ #include "cea708.h" +#include #include +#include -int cea708_cc_count (user_data_t* data) +int cea708_cc_count(user_data_t* data) { return data->cc_count; } -uint16_t cea708_cc_data (user_data_t* data, int index, int* valid, cea708_cc_type_t* type) +uint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_cc_type_t* type) { (*valid) = data->cc_data[index].cc_valid; (*type) = data->cc_data[index].cc_type; return data->cc_data[index].cc_data; } - -int cea708_init (cea708_t* cea708) +int cea708_init(cea708_t* cea708, double timestamp) { - memset (cea708,0,sizeof (cea708_t)); + memset(cea708, 0, sizeof(cea708_t)); cea708->country = country_united_states; cea708->provider = t35_provider_atsc; - cea708->user_identifier = ('G'<<24) | ('A'<<16) | ('9'<<8) | ('4'); - cea708->atsc1_data_user_data_type_code = 3; //what does 3 mean here? + cea708->user_identifier = GA94; + cea708->user_data_type_code = 3; cea708->directv_user_data_length = 0; - /////////// cea708->user_data.process_em_data_flag = 0; cea708->user_data.process_cc_data_flag = 1; cea708->user_data.additional_data_flag = 0; + cea708->user_data.em_data = 0xFF; cea708->user_data.cc_count = 0; + cea708->timestamp = timestamp; return 1; } +void cea708_parse_user_data_type_strcture(const uint8_t* data, size_t size, user_data_t* user_data) +{ + memset(user_data, 0, sizeof(user_data_t)); + user_data->process_em_data_flag = !!(data[0] & 0x80); + user_data->process_cc_data_flag = !!(data[0] & 0x40); + user_data->additional_data_flag = !!(data[0] & 0x20); + user_data->cc_count = (data[0] & 0x1F); + user_data->em_data = data[1]; + data += 2, size -= 2; + + for (int i = 0; 3 < size && i < (int)user_data->cc_count; ++i, data += 3, size -= 3) { + user_data->cc_data[i].marker_bits = data[0] >> 3; + user_data->cc_data[i].cc_valid = data[0] >> 2; + user_data->cc_data[i].cc_type = data[0] >> 0; + user_data->cc_data[i].cc_data = data[1] << 8 | data[2]; + } +} + // 00 00 00 06 C1 FF FC 34 B9 FF : onCaptionInfo. -int cea708_parse (uint8_t* data, size_t size, cea708_t* cea708) +libcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708) { - int i; - cea708->country = (itu_t_t35_country_code_t) (data[0]); - cea708->provider = (itu_t_t35_provider_code_t) ( (data[1] <<8) | data[2]); - cea708->atsc1_data_user_data_type_code = 0; + if (3 > size) { + goto error; + } + + // I think the first few bytes need to be handled in mpeg + cea708->country = (itu_t_t35_country_code_t)(data[0]); + cea708->provider = (itu_t_t35_provider_code_t)((data[1] << 8) | data[2]); cea708->user_identifier = 0; - data += 3; size -= 3; + cea708->user_data_type_code = 0; + data += 3, size -= 3; if (t35_provider_atsc == cea708->provider) { - // GA94 - cea708->user_identifier = (data[0] <<24) | (data[1] <<16) | (data[2] <<8) | data[3]; - data += 4; size -= 4; + if (4 > size) { + goto error; + } + + cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]); + data += 4, size -= 4; } - // Im not sure what this extra byt is. It sonly seesm to come up in onCaptionInfo // where country and provider are zero - if (0 == cea708->provider) { - data += 1; size -= 1; + // Im not sure what this extra byte is. It sonly seesm to come up in onCaptionInfo + // h264 spec seems to describe this + if (0 == cea708->provider && 0 == cea708->country) { + if (1 > size) { + goto error; + } + + data += 1, size -= 1; } else if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) { - cea708->atsc1_data_user_data_type_code = data[0]; - data += 1; size -= 1; + if (1 > size) { + goto error; + } + + cea708->user_data_type_code = data[0]; + data += 1, size -= 1; } if (t35_provider_direct_tv == cea708->provider) { + if (1 > size) { + goto error; + } + cea708->directv_user_data_length = data[0]; - data += 1; size -= 1; + data += 1, size -= 1; } - // TODO I believe this is condational on the above. - cea708->user_data.process_em_data_flag = !! (data[0]&0x80); - cea708->user_data.process_cc_data_flag = !! (data[0]&0x40); - cea708->user_data.additional_data_flag = !! (data[0]&0x20); - cea708->user_data.cc_count = (data[0]&0x1F); - cea708->user_data.em_data = data[1]; - data += 2; size -= 2; + if (3 == cea708->user_data_type_code && 2 <= size) { + cea708_parse_user_data_type_strcture(data, size, &cea708->user_data); + } else if (4 == cea708->user_data_type_code) { + // additional_CEA_608_data + } else if (5 == cea708->user_data_type_code) { + // luma_PAM_data + } else { + // ATSC_reserved_user_data + } - if (size < 3 * cea708->user_data.cc_count) { - cea708_init (cea708); - return 0; + return LIBCAPTION_OK; +error: + return LIBCAPTION_ERROR; +} + +libcaption_stauts_t cea708_parse_h262(const uint8_t* data, size_t size, cea708_t* cea708) +{ + if (!data || 7 > size) { + return LIBCAPTION_ERROR; } - for (i = 0 ; i < (int) cea708->user_data.cc_count ; ++i) { - cea708->user_data.cc_data[i].marker_bits = data[0]>>3; - cea708->user_data.cc_data[i].cc_valid = data[0]>>2; - cea708->user_data.cc_data[i].cc_type = data[0]>>0; - cea708->user_data.cc_data[i].cc_data = data[1]<<8|data[2]; - data += 3; size -= 3; + cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]); + cea708->user_data_type_code = data[4]; + if (3 == cea708->user_data_type_code) { + cea708_parse_user_data_type_strcture(data + 5, size - 5, &cea708->user_data); } - return 1; + return LIBCAPTION_OK; } -int cea708_add_cc_data (cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data) +int cea708_add_cc_data(cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data) { if (31 <= cea708->user_data.cc_count) { return 0; @@ -121,13 +167,16 @@ int cea708_add_cc_data (cea708_t* cea708, int valid, cea708_cc_type_t type, uint return 1; } -int cea708_render (cea708_t* cea708, uint8_t* data, size_t size) +int cea708_render(cea708_t* cea708, uint8_t* data, size_t size) { - int i; size_t total = 0; + int i; + size_t total = 0; data[0] = cea708->country; - data[1] = cea708->provider>>8; - data[2] = cea708->provider>>0; - total += 3; data += 3; size -= 3; + data[1] = cea708->provider >> 8; + data[2] = cea708->provider >> 0; + total += 3; + data += 3; + size -= 3; if (t35_provider_atsc == cea708->provider) { @@ -135,71 +184,98 @@ int cea708_render (cea708_t* cea708, uint8_t* data, size_t size) data[1] = cea708->user_identifier >> 16; data[2] = cea708->user_identifier >> 8; data[3] = cea708->user_identifier >> 0; - total += 4; data += 4; size -= 4; + total += 4; + data += 4; + size -= 4; } if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) { - data[0] = cea708->atsc1_data_user_data_type_code; - total += 1; data += 1; size -= 1; + data[0] = cea708->user_data_type_code; + total += 1; + data += 1; + size -= 1; } if (t35_provider_direct_tv == cea708->provider) { data[0] = cea708->directv_user_data_length; - total += 1; data += 1; size -= 1; + total += 1; + data += 1; + size -= 1; } data[1] = cea708->user_data.em_data; - data[0] = (cea708->user_data.process_em_data_flag?0x80:0x00) - | (cea708->user_data.process_cc_data_flag?0x40:0x00) - | (cea708->user_data.additional_data_flag?0x20:0x00) - | (cea708->user_data.cc_count & 0x1F); - - total += 2; data += 2; size -= 2; - - for (i = 0 ; i < (int) cea708->user_data.cc_count ; ++i) { - data[0] = (cea708->user_data.cc_data[i].marker_bits<<3) - | (data[0] = cea708->user_data.cc_data[i].cc_valid<<2) - | (data[0] = cea708->user_data.cc_data[i].cc_type); - data[1] = cea708->user_data.cc_data[i].cc_data>>8; - data[2] = cea708->user_data.cc_data[i].cc_data>>0; - total += 3; data += 3; size -= 3; + data[0] = (cea708->user_data.process_em_data_flag ? 0x80 : 0x00) + | (cea708->user_data.process_cc_data_flag ? 0x40 : 0x00) + | (cea708->user_data.additional_data_flag ? 0x20 : 0x00) + | (cea708->user_data.cc_count & 0x1F); + + total += 2; + data += 2; + size -= 2; + + for (i = 0; i < (int)cea708->user_data.cc_count; ++i) { + data[0] = (cea708->user_data.cc_data[i].marker_bits << 3) | (cea708->user_data.cc_data[i].cc_valid << 2) | cea708->user_data.cc_data[i].cc_type; + data[1] = cea708->user_data.cc_data[i].cc_data >> 8; + data[2] = cea708->user_data.cc_data[i].cc_data >> 0; + total += 3; + data += 3; + size -= 3; } - data[0] = 0xFF; - return (int) (total + 1); + data[0] = 0xFF; //marker bits + return (int)(total + 1); } -cc_data_t cea708_encode_cc_data (int cc_valid, cea708_cc_type_t type, uint16_t cc_data) +cc_data_t cea708_encode_cc_data(int cc_valid, cea708_cc_type_t type, uint16_t cc_data) { - cc_data_t data = { 0x1F, cc_valid,type,cc_data}; + cc_data_t data = { 0x1F, cc_valid, type, cc_data }; return data; } -void cea708_dump (cea708_t* cea708) +void cea708_dump(cea708_t* cea708) { int i; - for (i = 0 ; i < (int) cea708->user_data.cc_count ; ++i) { - cea708_cc_type_t type; int valid; - uint16_t cc_data = cea708_cc_data (&cea708->user_data, i, &valid, &type); + fprintf(stderr, "itu_t_t35_country_code_t %d\n", cea708->country); + fprintf(stderr, "itu_t_t35_provider_code_t %d\n", cea708->provider); + fprintf(stderr, "user_identifier %c%c%c%c\n", + (cea708->user_identifier >> 24) & 0xFF, (cea708->user_identifier >> 16) & 0xFF, + (cea708->user_identifier >> 8) & 0xFF, cea708->user_identifier & 0xFF); + fprintf(stderr, "user_data_type_code %d\n", cea708->user_data_type_code); + fprintf(stderr, "directv_user_data_length %d\n", cea708->directv_user_data_length); + fprintf(stderr, "user_data.process_em_data_flag %d\n", cea708->user_data.process_em_data_flag); + fprintf(stderr, "user_data.process_cc_data_flag %d\n", cea708->user_data.process_cc_data_flag); + fprintf(stderr, "user_data.additional_data_flag %d\n", cea708->user_data.additional_data_flag); + fprintf(stderr, "user_data.cc_count %d\n", cea708->user_data.cc_count); + fprintf(stderr, "user_data.em_data %d\n", cea708->user_data.em_data); + + for (i = 0; i < (int)cea708->user_data.cc_count; ++i) { + int valid; + cea708_cc_type_t type; + uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type); if (valid && cc_type_ntsc_cc_field_1 == type) { - eia608_dump (cc_data); + eia608_dump(cc_data); + } else { + fprintf(stderr, "user_data.cc_data[%d] cc_valid: %s, cc_type: %d, cc_data: %04x\n", i, cea708->user_data.cc_data[i].cc_valid ? "true" : "false", cea708->user_data.cc_data[i].cc_type, cea708->user_data.cc_data[i].cc_data); } } } -libcaption_stauts_t cea708_to_caption_frame (caption_frame_t* frame, cea708_t* cea708, double pts) +libcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708) { - int i, count = cea708_cc_count (&cea708->user_data); + int i, count = cea708_cc_count(&cea708->user_data); libcaption_stauts_t status = LIBCAPTION_OK; - for (i = 0 ; i < count ; ++i) { - cea708_cc_type_t type; int valid; - uint16_t cc_data = cea708_cc_data (&cea708->user_data, i, &valid, &type); + if (GA94 == cea708->user_identifier) { + for (i = 0; i < count; ++i) { + int valid; + cea708_cc_type_t type; + uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type); - if (valid && cc_type_ntsc_cc_field_1 == type) { - status = libcaption_status_update (status,caption_frame_decode (frame,cc_data, pts)); + if (valid && cc_type_ntsc_cc_field_1 == type) { + status = libcaption_status_update(status, caption_frame_decode(frame, cc_data, cea708->timestamp)); + } } } diff --git a/deps/libcaption/src/dvtcc.c b/deps/libcaption/src/dvtcc.c new file mode 100644 index 000000000..283bcf83c --- /dev/null +++ b/deps/libcaption/src/dvtcc.c @@ -0,0 +1,22 @@ + + +struct dtvcc_packet_t* dtvcc_packet_start(uint8_t cc_data1, uint8_t cc_data2) +{ + unsigned int packet_size = cc_data1 & 0x3F; + packet_size = (0 == packet_size) ? 64 * 8 - 1 : (packet_size * 8 - 1) + + unsigned int packet_size_bytes + = dtvcc_packet_t* dvtcc = malloc(sizeof(dtvcc_packet_t) + packet_size * 2 - 1); + dvtcc->service_number = (cc_data1 0xC0) >> 6; + dvtcc->packet_size = packet_size; + dvtcc->service_number = DVTCC_SERVICE_NUMBER_UNKNOWN; +} + +void dtvcc_packet_data(struct dtvcc_packet_t* dvtcc, uint8_t cc_data1, uint8_t cc_data2) +{ + if (dvtcc->service_number) { + if (7 == dvtcc->service_number) { + dvtcc->service_number + } + } +} diff --git a/deps/libcaption/src/eia608.c b/deps/libcaption/src/eia608.c index 26add0dcc..146b4f3a9 100644 --- a/deps/libcaption/src/eia608.c +++ b/deps/libcaption/src/eia608.c @@ -1,8 +1,7 @@ -/* Generated by re2c 0.15.3 on Tue Nov 22 15:42:35 2016 */ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,20 +22,12 @@ /* THE SOFTWARE. */ /**********************************************************************************************/ #include "eia608.h" -#include #include +#include //////////////////////////////////////////////////////////////////////////////// -int eia608_row_map[] = {10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9}; -int eia608_reverse_row_map[] = {2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0, 6, 7, 8, 9, 1}; - -const char* eia608_mode_map[] = { - "clear", - "loading", - "popOn", - "paintOn", - "rollUp", -}; +int eia608_row_map[] = { 10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9 }; +int eia608_reverse_row_map[] = { 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0, 6, 7, 8, 9, 1 }; const char* eia608_style_map[] = { "white", @@ -49,24 +40,25 @@ const char* eia608_style_map[] = { "italics", }; -static inline uint16_t eia608_row_pramble (int row, int chan, int x, int underline) +static inline uint16_t eia608_row_pramble(int row, int chan, int x, int underline) { - row = eia608_reverse_row_map[row&0x0F]; - return eia608_parity (0x1040 | (chan?0x0800:0x0000) | ( (row<<7) &0x0700) | ( (row<<5) &0x0020)) | ( (x<<1) &0x001E) | (underline?0x0001:0x0000); + row = eia608_reverse_row_map[row & 0x0F]; + return eia608_parity(0x1040 | (chan ? 0x0800 : 0x0000) | ((row << 7) & 0x0700) | ((row << 5) & 0x0020)) | ((x << 1) & 0x001E) | (underline ? 0x0001 : 0x0000); } -uint16_t eia608_row_column_pramble (int row, int col, int chan, int underline) { return eia608_row_pramble (row,chan,0x10| (col/4),underline); } -uint16_t eia608_row_style_pramble (int row, eia608_style_t style, int chan, int underline) { return eia608_row_pramble (row,chan,style,underline); } +uint16_t eia608_row_column_pramble(int row, int col, int chan, int underline) { return eia608_row_pramble(row, chan, 0x10 | (col / 4), underline); } +uint16_t eia608_row_style_pramble(int row, int chan, eia608_style_t style, int underline) { return eia608_row_pramble(row, chan, style, underline); } +uint16_t eia608_midrow_change(int chan, eia608_style_t style, int underline) { return eia608_parity(0x1120 | ((chan << 11) & 0x0800) | ((style << 1) & 0x000E) | (underline & 0x0001)); } -int eia608_parse_preamble (uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline) +int eia608_parse_preamble(uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline) { - (*row) = eia608_row_map[ ( (0x0700 & cc_data) >> 7) | ( (0x0020 & cc_data) >> 5)]; - (*chan) = !! (0x0800 & cc_data); + (*row) = eia608_row_map[((0x0700 & cc_data) >> 7) | ((0x0020 & cc_data) >> 5)]; + (*chan) = !!(0x0800 & cc_data); (*underline) = 0x0001 & cc_data; if (0x0010 & cc_data) { (*style) = eia608_style_white; - (*col) = 4* ( (0x000E & cc_data) >> 1); + (*col) = 4 * ((0x000E & cc_data) >> 1); } else { (*style) = (0x000E & cc_data) >> 1; (*col) = 0; @@ -75,9 +67,9 @@ int eia608_parse_preamble (uint16_t cc_data, int* row, int* col, eia608_style_t* return 1; } -int eia608_parse_midrowchange (uint16_t cc_data, int* chan, eia608_style_t* style, int* underline) +int eia608_parse_midrowchange(uint16_t cc_data, int* chan, eia608_style_t* style, int* underline) { - (*chan) = !! (0x0800 & cc_data); + (*chan) = !!(0x0800 & cc_data); if (0x1120 == (0x7770 & cc_data)) { (*style) = (0x000E & cc_data) >> 1; @@ -88,43 +80,44 @@ int eia608_parse_midrowchange (uint16_t cc_data, int* chan, eia608_style_t* styl } //////////////////////////////////////////////////////////////////////////////// // control command -eia608_control_t eia608_parse_control (uint16_t cc_data, int* cc) +eia608_control_t eia608_parse_control(uint16_t cc_data, int* cc) { - if (0x0200&cc_data) { - (*cc) = (cc_data&0x0800?0x01:0x00); - return (eia608_control_t) (0x177F & cc_data); + if (0x0200 & cc_data) { + (*cc) = (cc_data & 0x0800 ? 0x01 : 0x00); + return (eia608_control_t)(0x177F & cc_data); } else { - (*cc) = (cc_data&0x0800?0x01:0x00) | (cc_data&0x0100?0x02:0x00); - return (eia608_control_t) (0x167F & cc_data); + (*cc) = (cc_data & 0x0800 ? 0x01 : 0x00) | (cc_data & 0x0100 ? 0x02 : 0x00); + return (eia608_control_t)(0x167F & cc_data); } } -uint16_t eia608_control_command (eia608_control_t cmd, int cc) +uint16_t eia608_control_command(eia608_control_t cmd, int cc) { - uint16_t c = (cc&0x01) ?0x0800:0x0000; - uint16_t f = (cc&0x02) ?0x0100:0x0000; + uint16_t c = (cc & 0x01) ? 0x0800 : 0x0000; + uint16_t f = (cc & 0x02) ? 0x0100 : 0x0000; - if (eia608_tab_offset_0 == (eia608_control_t) (cmd&0xFFC0)) { - return (eia608_control_t) eia608_parity (cmd|c); + if (eia608_tab_offset_0 == (eia608_control_t)(cmd & 0xFFC0)) { + return (eia608_control_t)eia608_parity(cmd | c); } else { - return (eia608_control_t) eia608_parity (cmd|c|f); + return (eia608_control_t)eia608_parity(cmd | c | f); } } //////////////////////////////////////////////////////////////////////////////// // text -static const char* utf8_from_index (int idx) { return (0<=idx && EIA608_CHAR_COUNT>idx) ? eia608_char_map[idx] : ""; } -static int eia608_to_index (uint16_t cc_data, int* chan, int* c1, int* c2) +static const char* utf8_from_index(int idx) { return (0 <= idx && EIA608_CHAR_COUNT > idx) ? eia608_char_map[idx] : ""; } +static int eia608_to_index(uint16_t cc_data, int* chan, int* c1, int* c2) { - (*c1) = (*c2) = -1; (*chan) = 0; + (*c1) = (*c2) = -1; + (*chan) = 0; cc_data &= 0x7F7F; // strip off parity bits // Handle Basic NA BEFORE we strip the channel bit - if (eia608_is_basicna (cc_data)) { + if (eia608_is_basicna(cc_data)) { // we got first char, yes. But what about second char? - (*c1) = (cc_data>>8) - 0x20; + (*c1) = (cc_data >> 8) - 0x20; cc_data &= 0x00FF; - if (0x0020<=cc_data && 0x0080>cc_data) { + if (0x0020 <= cc_data && 0x0080 > cc_data) { (*c2) = cc_data - 0x20; return 2; } @@ -136,19 +129,19 @@ static int eia608_to_index (uint16_t cc_data, int* chan, int* c1, int* c2) (*chan) = cc_data & 0x0800; cc_data = cc_data & 0xF7FF; - if (eia608_is_specialna (cc_data)) { + if (eia608_is_specialna(cc_data)) { // Special North American character (*c1) = cc_data - 0x1130 + 0x60; return 1; } - if (0x1220<=cc_data && 0x1240>cc_data) { + if (0x1220 <= cc_data && 0x1240 > cc_data) { // Extended Western European character set, Spanish/Miscellaneous/French (*c1) = cc_data - 0x1220 + 0x70; return 1; } - if (0x1320<=cc_data && 0x1340>cc_data) { + if (0x1320 <= cc_data && 0x1340 > cc_data) { // Extended Western European character set, Portuguese/German/Danish (*c1) = cc_data - 0x1320 + 0x90; return 1; @@ -157,50 +150,49 @@ static int eia608_to_index (uint16_t cc_data, int* chan, int* c1, int* c2) return 0; } - -int eia608_to_utf8 (uint16_t c, int* chan, char* str1, char* str2) +int eia608_to_utf8(uint16_t c, int* chan, char* str1, char* str2) { int c1, c2; - size_t size = eia608_to_index (c,chan,&c1,&c2); - strncpy (str1, utf8_from_index (c1),5); - strncpy (str2, utf8_from_index (c2),5); - return (int)size; + int size = (int)eia608_to_index(c, chan, &c1, &c2); + utf8_char_copy(str1, utf8_from_index(c1)); + utf8_char_copy(str2, utf8_from_index(c2)); + return size; } -uint16_t eia608_from_basicna (uint16_t bna1, uint16_t bna2) +uint16_t eia608_from_basicna(uint16_t bna1, uint16_t bna2) { - if (! eia608_is_basicna (bna1) || ! eia608_is_basicna (bna2)) { + if (!eia608_is_basicna(bna1) || !eia608_is_basicna(bna2)) { return 0; } - return eia608_parity ( ( (0xFF00&bna1) >>0) | ( (0xFF00&bna2) >>8)); + return eia608_parity((0xFF00 & bna1) | ((0xFF00 & bna2) >> 8)); } // prototype for re2c generated function -uint16_t _eia608_from_utf8 (const utf8_char_t* s); -uint16_t eia608_from_utf8_1 (const utf8_char_t* c, int chan) +uint16_t _eia608_from_utf8(const utf8_char_t* s); +uint16_t eia608_from_utf8_1(const utf8_char_t* c, int chan) { - uint16_t cc_data = _eia608_from_utf8 (c); + uint16_t cc_data = _eia608_from_utf8(c); if (0 == cc_data) { return cc_data; } - if (chan && ! eia608_is_basicna (cc_data)) { + if (chan && !eia608_is_basicna(cc_data)) { cc_data |= 0x0800; } - return eia608_parity (cc_data); + return eia608_parity(cc_data); } -uint16_t eia608_from_utf8_2 (const utf8_char_t* c1, const utf8_char_t* c2) +uint16_t eia608_from_utf8_2(const utf8_char_t* c1, const utf8_char_t* c2) { - uint16_t cc1 = _eia608_from_utf8 (c1); - uint16_t cc2 = _eia608_from_utf8 (c2); - return eia608_from_basicna (cc1,cc2); + uint16_t cc1 = _eia608_from_utf8(c1); + uint16_t cc2 = _eia608_from_utf8(c2); + return eia608_from_basicna(cc1, cc2); } //////////////////////////////////////////////////////////////////////////////// -void eia608_dump (uint16_t cc_data) +void eia608_dump(uint16_t cc_data) { eia608_style_t style; const char* text = 0; @@ -208,547 +200,116 @@ void eia608_dump (uint16_t cc_data) char1[0] = char2[0] = 0; int row, col, chan, underline; - if (!eia608_parity_varify (cc_data)) { + if (!eia608_parity_varify(cc_data)) { text = "parity failed"; - } else if (0 == eia608_parity_strip (cc_data)) { + } else if (0 == eia608_parity_strip(cc_data)) { text = "pad"; - } else if (eia608_is_basicna (cc_data)) { + } else if (eia608_is_basicna(cc_data)) { text = "basicna"; - eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]); - } else if (eia608_is_specialna (cc_data)) { + eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]); + } else if (eia608_is_specialna(cc_data)) { text = "specialna"; - eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]); - } else if (eia608_is_westeu (cc_data)) { + eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]); + } else if (eia608_is_westeu(cc_data)) { text = "westeu"; - eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]); - } else if (eia608_is_xds (cc_data)) { + eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]); + } else if (eia608_is_xds(cc_data)) { text = "xds"; - } else if (eia608_is_midrowchange (cc_data)) { + } else if (eia608_is_midrowchange(cc_data)) { text = "midrowchange"; - } else if (eia608_is_norpak (cc_data)) { + } else if (eia608_is_norpak(cc_data)) { text = "norpak"; - } else if (eia608_is_preamble (cc_data)) { + } else if (eia608_is_preamble(cc_data)) { text = "preamble"; - eia608_parse_preamble (cc_data, &row, &col, &style, &chan, &underline); - fprintf (stderr,"preamble %d %d %d %d %d\n", row, col, style, chan, underline); + eia608_parse_preamble(cc_data, &row, &col, &style, &chan, &underline); + fprintf(stderr, "preamble %d %d %d %d %d\n", row, col, style, chan, underline); - } else if (eia608_is_control (cc_data)) { - switch (eia608_parse_control (cc_data,&chan)) { + } else if (eia608_is_control(cc_data)) { + switch (eia608_parse_control(cc_data, &chan)) { - default: text = "unknown_control"; break; + default: + text = "unknown_control"; + break; - case eia608_tab_offset_0: text = "eia608_tab_offset_0"; break; + case eia608_tab_offset_0: + text = "eia608_tab_offset_0"; + break; - case eia608_tab_offset_1: text = "eia608_tab_offset_1"; break; + case eia608_tab_offset_1: + text = "eia608_tab_offset_1"; + break; - case eia608_tab_offset_2:text = "eia608_tab_offset_2"; break; + case eia608_tab_offset_2: + text = "eia608_tab_offset_2"; + break; - case eia608_tab_offset_3: text = "eia608_tab_offset_3"; break; + case eia608_tab_offset_3: + text = "eia608_tab_offset_3"; + break; - case eia608_control_resume_caption_loading: text = "eia608_control_resume_caption_loading"; break; + case eia608_control_resume_caption_loading: + text = "eia608_control_resume_caption_loading"; + break; - case eia608_control_backspace: text = "eia608_control_backspace"; break; + case eia608_control_backspace: + text = "eia608_control_backspace"; + break; - case eia608_control_alarm_off: text = "eia608_control_alarm_off"; break; + case eia608_control_alarm_off: + text = "eia608_control_alarm_off"; + break; - case eia608_control_alarm_on: text = "eia608_control_alarm_on"; break; + case eia608_control_alarm_on: + text = "eia608_control_alarm_on"; + break; - case eia608_control_delete_to_end_of_row: text = "eia608_control_delete_to_end_of_row"; break; + case eia608_control_delete_to_end_of_row: + text = "eia608_control_delete_to_end_of_row"; + break; - case eia608_control_roll_up_2: text = "eia608_control_roll_up_2"; break; + case eia608_control_roll_up_2: + text = "eia608_control_roll_up_2"; + break; - case eia608_control_roll_up_3: text = "eia608_control_roll_up_3"; break; + case eia608_control_roll_up_3: + text = "eia608_control_roll_up_3"; + break; - case eia608_control_roll_up_4: text = "eia608_control_roll_up_4"; break; + case eia608_control_roll_up_4: + text = "eia608_control_roll_up_4"; + break; - case eia608_control_resume_direct_captioning: text = "eia608_control_resume_direct_captioning"; break; + case eia608_control_resume_direct_captioning: + text = "eia608_control_resume_direct_captioning"; + break; - case eia608_control_text_restart: text = "eia608_control_text_restart"; break; + case eia608_control_text_restart: + text = "eia608_control_text_restart"; + break; - case eia608_control_text_resume_text_display: text = "eia608_control_text_resume_text_display"; break; + case eia608_control_text_resume_text_display: + text = "eia608_control_text_resume_text_display"; + break; - case eia608_control_erase_display_memory: text = "eia608_control_erase_display_memory"; break; + case eia608_control_erase_display_memory: + text = "eia608_control_erase_display_memory"; + break; - case eia608_control_carriage_return: text = "eia608_control_carriage_return"; break; + case eia608_control_carriage_return: + text = "eia608_control_carriage_return"; + break; - case eia608_control_erase_non_displayed_memory:text = "eia608_control_erase_non_displayed_memory"; break; + case eia608_control_erase_non_displayed_memory: + text = "eia608_control_erase_non_displayed_memory"; + break; - case eia608_control_end_of_caption: text = "eia608_control_end_of_caption"; break; + case eia608_control_end_of_caption: + text = "eia608_control_end_of_caption"; + break; } } else { text = "unhandled"; } - fprintf (stderr,"cc %04X (%04X) '%s' '%s' (%s)\n", cc_data, eia608_parity_strip (cc_data), char1, char2, text); -} -//////////////////////////////////////////////////////////////////////////////// -// below this line is re2c -uint16_t _eia608_from_utf8 (const utf8_char_t* s) -{ - const unsigned char* YYMARKER; // needed by default rule - const unsigned char* YYCURSOR = (const unsigned char*) s; - - if (0==s) { return 0x0000;} - - -{ - unsigned char yych; - yych = *YYCURSOR; - if (yych <= '`') { - if (yych <= '*') { - if (yych <= '&') { - if (yych <= 0x00) goto yy2; - if (yych <= 0x1F) goto yy32; - goto yy26; - } else { - if (yych <= '\'') goto yy4; - if (yych <= ')') goto yy26; - goto yy6; - } - } else { - if (yych <= ']') { - if (yych == '\\') goto yy8; - goto yy26; - } else { - if (yych <= '^') goto yy10; - if (yych <= '_') goto yy12; - goto yy14; - } - } - } else { - if (yych <= 0x7F) { - if (yych <= '|') { - if (yych <= 'z') goto yy26; - if (yych <= '{') goto yy16; - goto yy18; - } else { - if (yych <= '}') goto yy20; - if (yych <= '~') goto yy22; - goto yy24; - } - } else { - if (yych <= 0xC3) { - if (yych <= 0xC1) goto yy32; - if (yych <= 0xC2) goto yy31; - goto yy28; - } else { - if (yych == 0xE2) goto yy30; - goto yy32; - } - } - } -yy2: - ++YYCURSOR; - { /*NULL*/ return 0x0000; } -yy4: - ++YYCURSOR; - { /*APOSTROPHE -> RIGHT_SINGLE_QUOTATION_MARK*/ return 0x1229; } -yy6: - ++YYCURSOR; - { /*ASTERISK*/ return 0x1228; } -yy8: - ++YYCURSOR; - { /*REVERSE_SOLIDUS*/ return 0x132B; } -yy10: - ++YYCURSOR; - { /*CIRCUMFLEX_ACCENT*/ return 0x132C; } -yy12: - ++YYCURSOR; - { /*LOW_LINE*/ return 0x132D; } -yy14: - ++YYCURSOR; - { /*GRAVE_ACCENT, No equivalent return 0x0000; return 1;*/ /*LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; } -yy16: - ++YYCURSOR; - { /*LEFT_CURLY_BRACKET*/ return 0x1329; } -yy18: - ++YYCURSOR; - { /*VERTICAL_LINE*/ return 0x132E; } -yy20: - ++YYCURSOR; - { /*RIGHT_CURLY_BRACKET*/ return 0x132A; } -yy22: - ++YYCURSOR; - { /*TILDE*/ return 0x132F; } -yy24: - ++YYCURSOR; - { /*DEL/BACKSPACE. Need to set bits 9 and 12! return 0x1421;*/ return 0x0000; } -yy26: - ++YYCURSOR; - { /*ASCII range*/ return (s[0]<<8) &0xFF00; } -yy28: - ++YYCURSOR; - switch ((yych = *YYCURSOR)) { - case 0x80: goto yy157; - case 0x81: goto yy169; - case 0x82: goto yy155; - case 0x83: goto yy129; - case 0x84: goto yy111; - case 0x85: goto yy101; - case 0x87: goto yy153; - case 0x88: goto yy151; - case 0x89: goto yy167; - case 0x8A: goto yy149; - case 0x8B: goto yy147; - case 0x8C: goto yy123; - case 0x8D: goto yy125; - case 0x8E: goto yy143; - case 0x8F: goto yy141; - case 0x91: goto yy187; - case 0x92: goto yy119; - case 0x93: goto yy165; - case 0x94: goto yy137; - case 0x95: goto yy115; - case 0x96: goto yy107; - case 0x98: goto yy97; - case 0x99: goto yy135; - case 0x9A: goto yy163; - case 0x9B: goto yy131; - case 0x9C: goto yy161; - case 0x9F: goto yy103; - case 0xA0: goto yy183; - case 0xA1: goto yy201; - case 0xA2: goto yy179; - case 0xA3: goto yy127; - case 0xA4: goto yy109; - case 0xA5: goto yy99; - case 0xA7: goto yy191; - case 0xA8: goto yy181; - case 0xA9: goto yy199; - case 0xAA: goto yy177; - case 0xAB: goto yy145; - case 0xAC: goto yy121; - case 0xAD: goto yy197; - case 0xAE: goto yy175; - case 0xAF: goto yy139; - case 0xB1: goto yy185; - case 0xB2: goto yy117; - case 0xB3: goto yy195; - case 0xB4: goto yy173; - case 0xB5: goto yy113; - case 0xB6: goto yy105; - case 0xB7: goto yy189; - case 0xB8: goto yy95; - case 0xB9: goto yy133; - case 0xBA: goto yy193; - case 0xBB: goto yy171; - case 0xBC: goto yy159; - default: goto yy29; - } -yy29: - { /*DEFAULT_RULE*/ return 0x0000; } -yy30: - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 0x80: goto yy63; - case 0x84: goto yy64; - case 0x94: goto yy61; - case 0x96: goto yy66; - case 0x99: goto yy65; - default: goto yy29; - } -yy31: - yych = *++YYCURSOR; - switch (yych) { - case 0xA0: goto yy47; - case 0xA1: goto yy45; - case 0xA2: goto yy51; - case 0xA3: goto yy49; - case 0xA4: goto yy35; - case 0xA5: goto yy37; - case 0xA6: goto yy33; - case 0xA9: goto yy43; - case 0xAB: goto yy41; - case 0xAE: goto yy59; - case 0xB0: goto yy57; - case 0xBB: goto yy39; - case 0xBD: goto yy55; - case 0xBF: goto yy53; - default: goto yy29; - } -yy32: - yych = *++YYCURSOR; - goto yy29; -yy33: - ++YYCURSOR; - { /*BROKEN_BAR*/ return 0x1337; } -yy35: - ++YYCURSOR; - { /*CURRENCY_SIGN*/ return 0x1336; } -yy37: - ++YYCURSOR; - { /*YEN_SIGN*/ return 0x1335; } -yy39: - ++YYCURSOR; - { /*RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123F; } -yy41: - ++YYCURSOR; - { /*LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123E; } -yy43: - ++YYCURSOR; - { /*COPYRIGHT_SIGN*/ return 0x122B; } -yy45: - ++YYCURSOR; - { /*INVERTED_EXCLAMATION_MARK*/ return 0x1227; } -yy47: - ++YYCURSOR; - { /*NO_BREAK_SPACE*/ return 0x1139; } -yy49: - ++YYCURSOR; - { /*POUND_SIGN*/ return 0x1136; } -yy51: - ++YYCURSOR; - { /*CENT_SIGN*/ return 0x1135; } -yy53: - ++YYCURSOR; - { /*INVERTED_QUESTION_MARK*/ return 0x1133; } -yy55: - ++YYCURSOR; - { /*VULGAR_FRACTION_ONE_HALF*/ return 0x1132; } -yy57: - ++YYCURSOR; - { /*DEGREE_SIGN*/ return 0x1131; } -yy59: - ++YYCURSOR; - { /*REGISTERED_SIGN*/ return 0x1130; } -yy61: - yych = *++YYCURSOR; - switch (yych) { - case 0x8C: goto yy87; - case 0x90: goto yy89; - case 0x94: goto yy91; - case 0x98: goto yy93; - default: goto yy62; - } -yy62: - YYCURSOR = YYMARKER; - goto yy29; -yy63: - yych = *++YYCURSOR; - switch (yych) { - case 0x94: goto yy79; - case 0x98: goto yy75; - case 0x99: goto yy77; - case 0x9C: goto yy83; - case 0x9D: goto yy85; - case 0xA2: goto yy81; - default: goto yy62; - } -yy64: - yych = *++YYCURSOR; - if (yych == 0xA0) goto yy73; - if (yych == 0xA2) goto yy71; - goto yy62; -yy65: - yych = *++YYCURSOR; - if (yych == 0xAA) goto yy69; - goto yy62; -yy66: - yych = *++YYCURSOR; - if (yych != 0x88) goto yy62; - ++YYCURSOR; - { /*FULL_BLOCK*/ return 0x7F00; } -yy69: - ++YYCURSOR; - { /*EIGHTH_NOTE*/ return 0x1137; } -yy71: - ++YYCURSOR; - { /*TRADE_MARK_SIGN*/ return 0x1134; } -yy73: - ++YYCURSOR; - { /*SERVICE_MARK*/ return 0x122C; } -yy75: - ++YYCURSOR; - { /*LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; } -yy77: - ++YYCURSOR; - { /*RIGHT_SINGLE_QUOTATION_MARK -> APOSTROPHE*/ return 0x2700; } -yy79: - ++YYCURSOR; - { /*EM_DASH*/ return 0x122A; } -yy81: - ++YYCURSOR; - { /*BULLET*/ return 0x122D; } -yy83: - ++YYCURSOR; - { /*LEFT_DOUBLE_QUOTATION_MARK*/ return 0x122E; } -yy85: - ++YYCURSOR; - { /*RIGHT_DOUBLE_QUOTATION_MARK*/ return 0x122F; } -yy87: - ++YYCURSOR; - { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT*/ return 0x133C; } -yy89: - ++YYCURSOR; - { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT*/ return 0x133D; } -yy91: - ++YYCURSOR; - { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT*/ return 0x133E; } -yy93: - ++YYCURSOR; - { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT*/ return 0x133F; } -yy95: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_O_WITH_STROKE*/ return 0x133B; } -yy97: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_O_WITH_STROKE*/ return 0x133A; } -yy99: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_A_WITH_RING_ABOVE*/ return 0x1339; } -yy101: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE*/ return 0x1338; } -yy103: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_SHARP_S*/ return 0x1334; } -yy105: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_O_WITH_DIAERESIS*/ return 0x1333; } -yy107: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS*/ return 0x1332; } -yy109: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_A_WITH_DIAERESIS*/ return 0x1331; } -yy111: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS*/ return 0x1330; } -yy113: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_O_WITH_TILDE*/ return 0x1328; } -yy115: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_O_WITH_TILDE*/ return 0x1327; } -yy117: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_O_WITH_GRAVE*/ return 0x1326; } -yy119: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_O_WITH_GRAVE*/ return 0x1325; } -yy121: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_I_WITH_GRAVE*/ return 0x1324; } -yy123: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_I_WITH_GRAVE*/ return 0x1323; } -yy125: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_I_WITH_ACUTE*/ return 0x1322; } -yy127: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_A_WITH_TILDE*/ return 0x1321; } -yy129: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_A_WITH_TILDE*/ return 0x1320; } -yy131: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x123D; } -yy133: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_U_WITH_GRAVE*/ return 0x123C; } -yy135: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_U_WITH_GRAVE*/ return 0x123B; } -yy137: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x123A; } -yy139: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_I_WITH_DIAERESIS*/ return 0x1239; } -yy141: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS*/ return 0x1238; } -yy143: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x1237; } -yy145: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_E_WITH_DIAERESIS*/ return 0x1236; } -yy147: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS*/ return 0x1235; } -yy149: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x1234; } -yy151: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_E_WITH_GRAVE*/ return 0x1233; } -yy153: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_C_WITH_CEDILLA*/ return 0x1232; } -yy155: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x1231; } -yy157: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_A_WITH_GRAVE*/ return 0x1230; } -yy159: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_U_WITH_DIAERESIS*/ return 0x1225; } -yy161: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS*/ return 0x1224; } -yy163: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_U_WITH_ACUTE*/ return 0x1223; } -yy165: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_O_WITH_ACUTE*/ return 0x1222; } -yy167: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_E_WITH_ACUTE*/ return 0x1221; } -yy169: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_A_WITH_ACUTE*/ return 0x1220; } -yy171: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x113F; } -yy173: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x113E; } -yy175: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x113D; } -yy177: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x113C; } -yy179: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x113B; } -yy181: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_E_WITH_GRAVE*/ return 0x113A; } -yy183: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_A_WITH_GRAVE*/ return 0x1138; } -yy185: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_N_WITH_TILDE*/ return 0x7E00; } -yy187: - ++YYCURSOR; - { /*LATIN_CAPITAL_LETTER_N_WITH_TILDE*/ return 0x7D00; } -yy189: - ++YYCURSOR; - { /*DIVISION_SIGN*/ return 0x7C00; } -yy191: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_C_WITH_CEDILLA*/ return 0x7B00; } -yy193: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_U_WITH_ACUTE*/ return 0x6000; } -yy195: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_O_WITH_ACUTE*/ return 0x5F00; } -yy197: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_I_WITH_ACUTE*/ return 0x5E00; } -yy199: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_E_WITH_ACUTE*/ return 0x5C00; } -yy201: - ++YYCURSOR; - { /*LATIN_SMALL_LETTER_A_WITH_ACUTE*/ return 0x2A00; } -} - + fprintf(stderr, "cc %04X (%04X) '%s' '%s' (%s)\n", cc_data, eia608_parity_strip(cc_data), char1, char2, text); } diff --git a/deps/libcaption/src/eia608_charmap.c b/deps/libcaption/src/eia608_charmap.c index 0c056527c..4a8258e19 100644 --- a/deps/libcaption/src/eia608_charmap.c +++ b/deps/libcaption/src/eia608_charmap.c @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -29,26 +29,180 @@ // 144 - 159: Extended Western European character set : Portuguese // 160 - 175: Extended Western European character set : German/Danish const char* eia608_char_map[] = { - EIA608_CHAR_SPACE, EIA608_CHAR_EXCLAMATION_MARK, EIA608_CHAR_QUOTATION_MARK, EIA608_CHAR_NUMBER_SIGN, EIA608_CHAR_DOLLAR_SIGN, EIA608_CHAR_PERCENT_SIGN, EIA608_CHAR_AMPERSAND, EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK, - EIA608_CHAR_LEFT_PARENTHESIS, EIA608_CHAR_RIGHT_PARENTHESIS, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE, EIA608_CHAR_PLUS_SIGN, EIA608_CHAR_COMMA, EIA608_CHAR_HYPHEN_MINUS, EIA608_CHAR_FULL_STOP, EIA608_CHAR_SOLIDUS, - EIA608_CHAR_DIGIT_ZERO, EIA608_CHAR_DIGIT_ONE, EIA608_CHAR_DIGIT_TWO, EIA608_CHAR_DIGIT_THREE, EIA608_CHAR_DIGIT_FOUR, EIA608_CHAR_DIGIT_FIVE, EIA608_CHAR_DIGIT_SIX, EIA608_CHAR_DIGIT_SEVEN, EIA608_CHAR_DIGIT_EIGHT, - EIA608_CHAR_DIGIT_NINE, EIA608_CHAR_COLON, EIA608_CHAR_SEMICOLON, EIA608_CHAR_LESS_THAN_SIGN, EIA608_CHAR_EQUALS_SIGN, EIA608_CHAR_GREATER_THAN_SIGN, EIA608_CHAR_QUESTION_MARK, EIA608_CHAR_COMMERCIAL_AT, - EIA608_CHAR_LATIN_CAPITAL_LETTER_A, EIA608_CHAR_LATIN_CAPITAL_LETTER_B, EIA608_CHAR_LATIN_CAPITAL_LETTER_C, EIA608_CHAR_LATIN_CAPITAL_LETTER_D, EIA608_CHAR_LATIN_CAPITAL_LETTER_E, EIA608_CHAR_LATIN_CAPITAL_LETTER_F, EIA608_CHAR_LATIN_CAPITAL_LETTER_G, EIA608_CHAR_LATIN_CAPITAL_LETTER_H, - EIA608_CHAR_LATIN_CAPITAL_LETTER_I, EIA608_CHAR_LATIN_CAPITAL_LETTER_J, EIA608_CHAR_LATIN_CAPITAL_LETTER_K, EIA608_CHAR_LATIN_CAPITAL_LETTER_L, EIA608_CHAR_LATIN_CAPITAL_LETTER_M, EIA608_CHAR_LATIN_CAPITAL_LETTER_N, EIA608_CHAR_LATIN_CAPITAL_LETTER_O, EIA608_CHAR_LATIN_CAPITAL_LETTER_P, - EIA608_CHAR_LATIN_CAPITAL_LETTER_Q, EIA608_CHAR_LATIN_CAPITAL_LETTER_R, EIA608_CHAR_LATIN_CAPITAL_LETTER_S, EIA608_CHAR_LATIN_CAPITAL_LETTER_T, EIA608_CHAR_LATIN_CAPITAL_LETTER_U, EIA608_CHAR_LATIN_CAPITAL_LETTER_V, EIA608_CHAR_LATIN_CAPITAL_LETTER_W, EIA608_CHAR_LATIN_CAPITAL_LETTER_X, - EIA608_CHAR_LATIN_CAPITAL_LETTER_Y, EIA608_CHAR_LATIN_CAPITAL_LETTER_Z, EIA608_CHAR_LEFT_SQUARE_BRACKET, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE, EIA608_CHAR_RIGHT_SQUARE_BRACKET, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE, - EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE, EIA608_CHAR_LATIN_SMALL_LETTER_A, EIA608_CHAR_LATIN_SMALL_LETTER_B, EIA608_CHAR_LATIN_SMALL_LETTER_C, EIA608_CHAR_LATIN_SMALL_LETTER_D, EIA608_CHAR_LATIN_SMALL_LETTER_E, EIA608_CHAR_LATIN_SMALL_LETTER_F, EIA608_CHAR_LATIN_SMALL_LETTER_G, EIA608_CHAR_LATIN_SMALL_LETTER_H, - EIA608_CHAR_LATIN_SMALL_LETTER_I, EIA608_CHAR_LATIN_SMALL_LETTER_J, EIA608_CHAR_LATIN_SMALL_LETTER_K, EIA608_CHAR_LATIN_SMALL_LETTER_L, EIA608_CHAR_LATIN_SMALL_LETTER_M, EIA608_CHAR_LATIN_SMALL_LETTER_N, EIA608_CHAR_LATIN_SMALL_LETTER_O, EIA608_CHAR_LATIN_SMALL_LETTER_P, - EIA608_CHAR_LATIN_SMALL_LETTER_Q, EIA608_CHAR_LATIN_SMALL_LETTER_R, EIA608_CHAR_LATIN_SMALL_LETTER_S, EIA608_CHAR_LATIN_SMALL_LETTER_T, EIA608_CHAR_LATIN_SMALL_LETTER_U, EIA608_CHAR_LATIN_SMALL_LETTER_V, EIA608_CHAR_LATIN_SMALL_LETTER_W, EIA608_CHAR_LATIN_SMALL_LETTER_X, - EIA608_CHAR_LATIN_SMALL_LETTER_Y, EIA608_CHAR_LATIN_SMALL_LETTER_Z, EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA, EIA608_CHAR_DIVISION_SIGN, EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE, EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE, EIA608_CHAR_FULL_BLOCK, - EIA608_CHAR_REGISTERED_SIGN, EIA608_CHAR_DEGREE_SIGN, EIA608_CHAR_VULGAR_FRACTION_ONE_HALF, EIA608_CHAR_INVERTED_QUESTION_MARK, EIA608_CHAR_TRADE_MARK_SIGN, EIA608_CHAR_CENT_SIGN, EIA608_CHAR_POUND_SIGN, EIA608_CHAR_EIGHTH_NOTE, - EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE, EIA608_CHAR_NO_BREAK_SPACE, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX, - EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS, EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK, EIA608_CHAR_INVERTED_EXCLAMATION_MARK, - EIA608_CHAR_ASTERISK, EIA608_CHAR_APOSTROPHE, EIA608_CHAR_EM_DASH, EIA608_CHAR_COPYRIGHT_SIGN, EIA608_CHAR_SERVICE_MARK, EIA608_CHAR_BULLET, EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK, EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK, - EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS, EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX, - EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX, EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, EIA608_CHAR_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, - EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE, EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE, EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE, - EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE, EIA608_CHAR_LEFT_CURLY_BRACKET, EIA608_CHAR_RIGHT_CURLY_BRACKET, EIA608_CHAR_REVERSE_SOLIDUS, EIA608_CHAR_CIRCUMFLEX_ACCENT, EIA608_CHAR_LOW_LINE, EIA608_CHAR_VERTICAL_LINE, EIA608_CHAR_TILDE, - EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS, EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S, EIA608_CHAR_YEN_SIGN, EIA608_CHAR_CURRENCY_SIGN, EIA608_CHAR_BROKEN_BAR, - EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE, EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE, EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE, EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE, EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, + EIA608_CHAR_SPACE, + EIA608_CHAR_EXCLAMATION_MARK, + EIA608_CHAR_QUOTATION_MARK, + EIA608_CHAR_NUMBER_SIGN, + EIA608_CHAR_DOLLAR_SIGN, + EIA608_CHAR_PERCENT_SIGN, + EIA608_CHAR_AMPERSAND, + EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK, + EIA608_CHAR_LEFT_PARENTHESIS, + EIA608_CHAR_RIGHT_PARENTHESIS, + EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE, + EIA608_CHAR_PLUS_SIGN, + EIA608_CHAR_COMMA, + EIA608_CHAR_HYPHEN_MINUS, + EIA608_CHAR_FULL_STOP, + EIA608_CHAR_SOLIDUS, + EIA608_CHAR_DIGIT_ZERO, + EIA608_CHAR_DIGIT_ONE, + EIA608_CHAR_DIGIT_TWO, + EIA608_CHAR_DIGIT_THREE, + EIA608_CHAR_DIGIT_FOUR, + EIA608_CHAR_DIGIT_FIVE, + EIA608_CHAR_DIGIT_SIX, + EIA608_CHAR_DIGIT_SEVEN, + EIA608_CHAR_DIGIT_EIGHT, + EIA608_CHAR_DIGIT_NINE, + EIA608_CHAR_COLON, + EIA608_CHAR_SEMICOLON, + EIA608_CHAR_LESS_THAN_SIGN, + EIA608_CHAR_EQUALS_SIGN, + EIA608_CHAR_GREATER_THAN_SIGN, + EIA608_CHAR_QUESTION_MARK, + EIA608_CHAR_COMMERCIAL_AT, + EIA608_CHAR_LATIN_CAPITAL_LETTER_A, + EIA608_CHAR_LATIN_CAPITAL_LETTER_B, + EIA608_CHAR_LATIN_CAPITAL_LETTER_C, + EIA608_CHAR_LATIN_CAPITAL_LETTER_D, + EIA608_CHAR_LATIN_CAPITAL_LETTER_E, + EIA608_CHAR_LATIN_CAPITAL_LETTER_F, + EIA608_CHAR_LATIN_CAPITAL_LETTER_G, + EIA608_CHAR_LATIN_CAPITAL_LETTER_H, + EIA608_CHAR_LATIN_CAPITAL_LETTER_I, + EIA608_CHAR_LATIN_CAPITAL_LETTER_J, + EIA608_CHAR_LATIN_CAPITAL_LETTER_K, + EIA608_CHAR_LATIN_CAPITAL_LETTER_L, + EIA608_CHAR_LATIN_CAPITAL_LETTER_M, + EIA608_CHAR_LATIN_CAPITAL_LETTER_N, + EIA608_CHAR_LATIN_CAPITAL_LETTER_O, + EIA608_CHAR_LATIN_CAPITAL_LETTER_P, + EIA608_CHAR_LATIN_CAPITAL_LETTER_Q, + EIA608_CHAR_LATIN_CAPITAL_LETTER_R, + EIA608_CHAR_LATIN_CAPITAL_LETTER_S, + EIA608_CHAR_LATIN_CAPITAL_LETTER_T, + EIA608_CHAR_LATIN_CAPITAL_LETTER_U, + EIA608_CHAR_LATIN_CAPITAL_LETTER_V, + EIA608_CHAR_LATIN_CAPITAL_LETTER_W, + EIA608_CHAR_LATIN_CAPITAL_LETTER_X, + EIA608_CHAR_LATIN_CAPITAL_LETTER_Y, + EIA608_CHAR_LATIN_CAPITAL_LETTER_Z, + EIA608_CHAR_LEFT_SQUARE_BRACKET, + EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE, + EIA608_CHAR_RIGHT_SQUARE_BRACKET, + EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE, + EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE, + EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE, + EIA608_CHAR_LATIN_SMALL_LETTER_A, + EIA608_CHAR_LATIN_SMALL_LETTER_B, + EIA608_CHAR_LATIN_SMALL_LETTER_C, + EIA608_CHAR_LATIN_SMALL_LETTER_D, + EIA608_CHAR_LATIN_SMALL_LETTER_E, + EIA608_CHAR_LATIN_SMALL_LETTER_F, + EIA608_CHAR_LATIN_SMALL_LETTER_G, + EIA608_CHAR_LATIN_SMALL_LETTER_H, + EIA608_CHAR_LATIN_SMALL_LETTER_I, + EIA608_CHAR_LATIN_SMALL_LETTER_J, + EIA608_CHAR_LATIN_SMALL_LETTER_K, + EIA608_CHAR_LATIN_SMALL_LETTER_L, + EIA608_CHAR_LATIN_SMALL_LETTER_M, + EIA608_CHAR_LATIN_SMALL_LETTER_N, + EIA608_CHAR_LATIN_SMALL_LETTER_O, + EIA608_CHAR_LATIN_SMALL_LETTER_P, + EIA608_CHAR_LATIN_SMALL_LETTER_Q, + EIA608_CHAR_LATIN_SMALL_LETTER_R, + EIA608_CHAR_LATIN_SMALL_LETTER_S, + EIA608_CHAR_LATIN_SMALL_LETTER_T, + EIA608_CHAR_LATIN_SMALL_LETTER_U, + EIA608_CHAR_LATIN_SMALL_LETTER_V, + EIA608_CHAR_LATIN_SMALL_LETTER_W, + EIA608_CHAR_LATIN_SMALL_LETTER_X, + EIA608_CHAR_LATIN_SMALL_LETTER_Y, + EIA608_CHAR_LATIN_SMALL_LETTER_Z, + EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA, + EIA608_CHAR_DIVISION_SIGN, + EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE, + EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE, + EIA608_CHAR_FULL_BLOCK, + EIA608_CHAR_REGISTERED_SIGN, + EIA608_CHAR_DEGREE_SIGN, + EIA608_CHAR_VULGAR_FRACTION_ONE_HALF, + EIA608_CHAR_INVERTED_QUESTION_MARK, + EIA608_CHAR_TRADE_MARK_SIGN, + EIA608_CHAR_CENT_SIGN, + EIA608_CHAR_POUND_SIGN, + EIA608_CHAR_EIGHTH_NOTE, + EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE, + EIA608_CHAR_NO_BREAK_SPACE, + EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE, + EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS, + EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS, + EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK, + EIA608_CHAR_INVERTED_EXCLAMATION_MARK, + EIA608_CHAR_ASTERISK, + EIA608_CHAR_APOSTROPHE, + EIA608_CHAR_EM_DASH, + EIA608_CHAR_COPYRIGHT_SIGN, + EIA608_CHAR_SERVICE_MARK, + EIA608_CHAR_BULLET, + EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK, + EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK, + EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA, + EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS, + EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS, + EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS, + EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS, + EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX, + EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE, + EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX, + EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, + EIA608_CHAR_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, + EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE, + EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE, + EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE, + EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE, + EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE, + EIA608_CHAR_LEFT_CURLY_BRACKET, + EIA608_CHAR_RIGHT_CURLY_BRACKET, + EIA608_CHAR_REVERSE_SOLIDUS, + EIA608_CHAR_CIRCUMFLEX_ACCENT, + EIA608_CHAR_LOW_LINE, + EIA608_CHAR_VERTICAL_LINE, + EIA608_CHAR_TILDE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS, + EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS, + EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS, + EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS, + EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S, + EIA608_CHAR_YEN_SIGN, + EIA608_CHAR_CURRENCY_SIGN, + EIA608_CHAR_BROKEN_BAR, + EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE, + EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE, + EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE, + EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE, + EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, + EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, + EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, + EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, }; diff --git a/deps/libcaption/src/eia608_from_utf8.c b/deps/libcaption/src/eia608_from_utf8.c new file mode 100644 index 000000000..893ca4ad6 --- /dev/null +++ b/deps/libcaption/src/eia608_from_utf8.c @@ -0,0 +1,498 @@ +/* Generated by re2c 1.0.3 on Tue Jun 19 17:18:11 2018 */ +/**********************************************************************************************/ +/* The MIT License */ +/* */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ +/* THE SOFTWARE. */ +/**********************************************************************************************/ +#include "utf8.h" +#include +#include + +uint16_t _eia608_from_utf8 (const utf8_char_t* s) +{ + const unsigned char* YYMARKER = 0; + const unsigned char* YYCURSOR = (const unsigned char*) s; + + if (0==s) { return 0x0000;} + + +{ + unsigned char yych; + yych = *YYCURSOR; + if (yych <= '`') { + if (yych <= '*') { + if (yych <= '&') { + if (yych <= 0x00) goto yy2; + if (yych <= 0x1F) goto yy4; + goto yy6; + } else { + if (yych <= '\'') goto yy8; + if (yych <= ')') goto yy6; + goto yy10; + } + } else { + if (yych <= ']') { + if (yych == '\\') goto yy12; + goto yy6; + } else { + if (yych <= '^') goto yy14; + if (yych <= '_') goto yy16; + goto yy18; + } + } + } else { + if (yych <= 0x7F) { + if (yych <= '|') { + if (yych <= 'z') goto yy6; + if (yych <= '{') goto yy20; + goto yy22; + } else { + if (yych <= '}') goto yy24; + if (yych <= '~') goto yy26; + goto yy28; + } + } else { + if (yych <= 0xC3) { + if (yych <= 0xC1) goto yy4; + if (yych <= 0xC2) goto yy30; + goto yy31; + } else { + if (yych == 0xE2) goto yy32; + goto yy4; + } + } + } +yy2: + ++YYCURSOR; + { /*NULL*/ return 0x0000; } +yy4: + ++YYCURSOR; +yy5: + { /*DEFAULT_RULE*/ return 0x0000; } +yy6: + ++YYCURSOR; + { /*ASCII range*/ return (s[0]<<8) &0xFF00; } +yy8: + ++YYCURSOR; + { /*APOSTROPHE -> RIGHT_SINGLE_QUOTATION_MARK*/ return 0x1229; } +yy10: + ++YYCURSOR; + { /*ASTERISK*/ return 0x1228; } +yy12: + ++YYCURSOR; + { /*REVERSE_SOLIDUS*/ return 0x132B; } +yy14: + ++YYCURSOR; + { /*CIRCUMFLEX_ACCENT*/ return 0x132C; } +yy16: + ++YYCURSOR; + { /*LOW_LINE*/ return 0x132D; } +yy18: + ++YYCURSOR; + { /*GRAVE_ACCENT -> LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; } +yy20: + ++YYCURSOR; + { /*LEFT_CURLY_BRACKET*/ return 0x1329; } +yy22: + ++YYCURSOR; + { /*VERTICAL_LINE*/ return 0x132E; } +yy24: + ++YYCURSOR; + { /*RIGHT_CURLY_BRACKET*/ return 0x132A; } +yy26: + ++YYCURSOR; + { /*TILDE*/ return 0x132F; } +yy28: + ++YYCURSOR; + { /*DEL/BACKSPACE. Need to set bits 9 and 12! return 0x1421;*/ return 0x0000; } +yy30: + yych = *++YYCURSOR; + switch (yych) { + case 0xA0: goto yy33; + case 0xA1: goto yy35; + case 0xA2: goto yy37; + case 0xA3: goto yy39; + case 0xA4: goto yy41; + case 0xA5: goto yy43; + case 0xA6: goto yy45; + case 0xA9: goto yy47; + case 0xAB: goto yy49; + case 0xAE: goto yy51; + case 0xB0: goto yy53; + case 0xBB: goto yy55; + case 0xBD: goto yy57; + case 0xBF: goto yy59; + default: goto yy5; + } +yy31: + yych = *++YYCURSOR; + switch (yych) { + case 0x80: goto yy61; + case 0x81: goto yy63; + case 0x82: goto yy65; + case 0x83: goto yy67; + case 0x84: goto yy69; + case 0x85: goto yy71; + case 0x87: goto yy73; + case 0x88: goto yy75; + case 0x89: goto yy77; + case 0x8A: goto yy79; + case 0x8B: goto yy81; + case 0x8C: goto yy83; + case 0x8D: goto yy85; + case 0x8E: goto yy87; + case 0x8F: goto yy89; + case 0x91: goto yy91; + case 0x92: goto yy93; + case 0x93: goto yy95; + case 0x94: goto yy97; + case 0x95: goto yy99; + case 0x96: goto yy101; + case 0x98: goto yy103; + case 0x99: goto yy105; + case 0x9A: goto yy107; + case 0x9B: goto yy109; + case 0x9C: goto yy111; + case 0x9F: goto yy113; + case 0xA0: goto yy115; + case 0xA1: goto yy117; + case 0xA2: goto yy119; + case 0xA3: goto yy121; + case 0xA4: goto yy123; + case 0xA5: goto yy125; + case 0xA7: goto yy127; + case 0xA8: goto yy129; + case 0xA9: goto yy131; + case 0xAA: goto yy133; + case 0xAB: goto yy135; + case 0xAC: goto yy137; + case 0xAD: goto yy139; + case 0xAE: goto yy141; + case 0xAF: goto yy143; + case 0xB1: goto yy145; + case 0xB2: goto yy147; + case 0xB3: goto yy149; + case 0xB4: goto yy151; + case 0xB5: goto yy153; + case 0xB6: goto yy155; + case 0xB7: goto yy157; + case 0xB8: goto yy159; + case 0xB9: goto yy161; + case 0xBA: goto yy163; + case 0xBB: goto yy165; + case 0xBC: goto yy167; + default: goto yy5; + } +yy32: + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 0x80: goto yy169; + case 0x84: goto yy171; + case 0x94: goto yy172; + case 0x96: goto yy173; + case 0x99: goto yy174; + default: goto yy5; + } +yy33: + ++YYCURSOR; + { /*NO_BREAK_SPACE*/ return 0x1139; } +yy35: + ++YYCURSOR; + { /*INVERTED_EXCLAMATION_MARK*/ return 0x1227; } +yy37: + ++YYCURSOR; + { /*CENT_SIGN*/ return 0x1135; } +yy39: + ++YYCURSOR; + { /*POUND_SIGN*/ return 0x1136; } +yy41: + ++YYCURSOR; + { /*CURRENCY_SIGN*/ return 0x1336; } +yy43: + ++YYCURSOR; + { /*YEN_SIGN*/ return 0x1335; } +yy45: + ++YYCURSOR; + { /*BROKEN_BAR*/ return 0x1337; } +yy47: + ++YYCURSOR; + { /*COPYRIGHT_SIGN*/ return 0x122B; } +yy49: + ++YYCURSOR; + { /*LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123E; } +yy51: + ++YYCURSOR; + { /*REGISTERED_SIGN*/ return 0x1130; } +yy53: + ++YYCURSOR; + { /*DEGREE_SIGN*/ return 0x1131; } +yy55: + ++YYCURSOR; + { /*RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123F; } +yy57: + ++YYCURSOR; + { /*VULGAR_FRACTION_ONE_HALF*/ return 0x1132; } +yy59: + ++YYCURSOR; + { /*INVERTED_QUESTION_MARK*/ return 0x1133; } +yy61: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_A_WITH_GRAVE*/ return 0x1230; } +yy63: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_A_WITH_ACUTE*/ return 0x1220; } +yy65: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x1231; } +yy67: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_A_WITH_TILDE*/ return 0x1320; } +yy69: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS*/ return 0x1330; } +yy71: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE*/ return 0x1338; } +yy73: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_C_WITH_CEDILLA*/ return 0x1232; } +yy75: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_E_WITH_GRAVE*/ return 0x1233; } +yy77: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_E_WITH_ACUTE*/ return 0x1221; } +yy79: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x1234; } +yy81: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS*/ return 0x1235; } +yy83: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_I_WITH_GRAVE*/ return 0x1323; } +yy85: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_I_WITH_ACUTE*/ return 0x1322; } +yy87: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x1237; } +yy89: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS*/ return 0x1238; } +yy91: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_N_WITH_TILDE*/ return 0x7D00; } +yy93: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_O_WITH_GRAVE*/ return 0x1325; } +yy95: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_O_WITH_ACUTE*/ return 0x1222; } +yy97: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x123A; } +yy99: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_O_WITH_TILDE*/ return 0x1327; } +yy101: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS*/ return 0x1332; } +yy103: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_O_WITH_STROKE*/ return 0x133A; } +yy105: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_U_WITH_GRAVE*/ return 0x123B; } +yy107: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_U_WITH_ACUTE*/ return 0x1223; } +yy109: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x123D; } +yy111: + ++YYCURSOR; + { /*LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS*/ return 0x1224; } +yy113: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_SHARP_S*/ return 0x1334; } +yy115: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_A_WITH_GRAVE*/ return 0x1138; } +yy117: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_A_WITH_ACUTE*/ return 0x2A00; } +yy119: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x113B; } +yy121: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_A_WITH_TILDE*/ return 0x1321; } +yy123: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_A_WITH_DIAERESIS*/ return 0x1331; } +yy125: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_A_WITH_RING_ABOVE*/ return 0x1339; } +yy127: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_C_WITH_CEDILLA*/ return 0x7B00; } +yy129: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_E_WITH_GRAVE*/ return 0x113A; } +yy131: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_E_WITH_ACUTE*/ return 0x5C00; } +yy133: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x113C; } +yy135: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_E_WITH_DIAERESIS*/ return 0x1236; } +yy137: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_I_WITH_GRAVE*/ return 0x1324; } +yy139: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_I_WITH_ACUTE*/ return 0x5E00; } +yy141: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x113D; } +yy143: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_I_WITH_DIAERESIS*/ return 0x1239; } +yy145: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_N_WITH_TILDE*/ return 0x7E00; } +yy147: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_O_WITH_GRAVE*/ return 0x1326; } +yy149: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_O_WITH_ACUTE*/ return 0x5F00; } +yy151: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x113E; } +yy153: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_O_WITH_TILDE*/ return 0x1328; } +yy155: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_O_WITH_DIAERESIS*/ return 0x1333; } +yy157: + ++YYCURSOR; + { /*DIVISION_SIGN*/ return 0x7C00; } +yy159: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_O_WITH_STROKE*/ return 0x133B; } +yy161: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_U_WITH_GRAVE*/ return 0x123C; } +yy163: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_U_WITH_ACUTE*/ return 0x6000; } +yy165: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x113F; } +yy167: + ++YYCURSOR; + { /*LATIN_SMALL_LETTER_U_WITH_DIAERESIS*/ return 0x1225; } +yy169: + yych = *++YYCURSOR; + switch (yych) { + case 0x94: goto yy175; + case 0x98: goto yy177; + case 0x99: goto yy179; + case 0x9C: goto yy181; + case 0x9D: goto yy183; + case 0xA2: goto yy185; + default: goto yy170; + } +yy170: + YYCURSOR = YYMARKER; + goto yy5; +yy171: + yych = *++YYCURSOR; + if (yych == 0xA0) goto yy187; + if (yych == 0xA2) goto yy189; + goto yy170; +yy172: + yych = *++YYCURSOR; + switch (yych) { + case 0x8C: goto yy191; + case 0x90: goto yy193; + case 0x94: goto yy195; + case 0x98: goto yy197; + default: goto yy170; + } +yy173: + yych = *++YYCURSOR; + if (yych == 0x88) goto yy199; + goto yy170; +yy174: + yych = *++YYCURSOR; + if (yych == 0xAA) goto yy201; + goto yy170; +yy175: + ++YYCURSOR; + { /*EM_DASH*/ return 0x122A; } +yy177: + ++YYCURSOR; + { /*LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; } +yy179: + ++YYCURSOR; + { /*RIGHT_SINGLE_QUOTATION_MARK -> APOSTROPHE*/ return 0x2700; } +yy181: + ++YYCURSOR; + { /*LEFT_DOUBLE_QUOTATION_MARK*/ return 0x122E; } +yy183: + ++YYCURSOR; + { /*RIGHT_DOUBLE_QUOTATION_MARK*/ return 0x122F; } +yy185: + ++YYCURSOR; + { /*BULLET*/ return 0x122D; } +yy187: + ++YYCURSOR; + { /*SERVICE_MARK*/ return 0x122C; } +yy189: + ++YYCURSOR; + { /*TRADE_MARK_SIGN*/ return 0x1134; } +yy191: + ++YYCURSOR; + { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT*/ return 0x133C; } +yy193: + ++YYCURSOR; + { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT*/ return 0x133D; } +yy195: + ++YYCURSOR; + { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT*/ return 0x133E; } +yy197: + ++YYCURSOR; + { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT*/ return 0x133F; } +yy199: + ++YYCURSOR; + { /*FULL_BLOCK*/ return 0x7F00; } +yy201: + ++YYCURSOR; + { /*EIGHTH_NOTE*/ return 0x1137; } +} + +} diff --git a/deps/libcaption/src/eia608.c.re2c b/deps/libcaption/src/eia608_from_utf8.re2c similarity index 51% rename from deps/libcaption/src/eia608.c.re2c rename to deps/libcaption/src/eia608_from_utf8.re2c index 633aa9d12..98db4234e 100644 --- a/deps/libcaption/src/eia608.c.re2c +++ b/deps/libcaption/src/eia608_from_utf8.re2c @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -21,270 +21,13 @@ /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ /* THE SOFTWARE. */ /**********************************************************************************************/ -#include "eia608.h" -#include -#include +#include "utf8.h" +#include +#include -//////////////////////////////////////////////////////////////////////////////// -int eia608_row_map[] = {10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9}; -int eia608_reverse_row_map[] = {2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0, 6, 7, 8, 9, 1}; - -const char* eia608_mode_map[] = { - "clear", - "loading", - "popOn", - "paintOn", - "rollUp", -}; - -const char* eia608_style_map[] = { - "white", - "green", - "blue", - "cyan", - "red", - "yellow", - "magenta", - "italics", -}; - -static inline uint16_t eia608_row_pramble (int row, int chan, int x, int underline) -{ - row = eia608_reverse_row_map[row&0x0F]; - return eia608_parity (0x1040 | (chan?0x0800:0x0000) | ( (row<<7) &0x0700) | ( (row<<5) &0x0020)) | ( (x<<1) &0x001E) | (underline?0x0001:0x0000); -} - -uint16_t eia608_row_column_pramble (int row, int col, int chan, int underline) { return eia608_row_pramble (row,chan,0x10| (col/4),underline); } -uint16_t eia608_row_style_pramble (int row, eia608_style_t style, int chan, int underline) { return eia608_row_pramble (row,chan,style,underline); } - -int eia608_parse_preamble (uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline) -{ - (*row) = eia608_row_map[ ( (0x0700 & cc_data) >> 7) | ( (0x0020 & cc_data) >> 5)]; - (*chan) = !! (0x0800 & cc_data); - (*underline) = 0x0001 & cc_data; - - if (0x0010 & cc_data) { - (*style) = eia608_style_white; - (*col) = 4* ( (0x000E & cc_data) >> 1); - } else { - (*style) = (0x000E & cc_data) >> 1; - (*col) = 0; - } - - return 1; -} - -int eia608_parse_midrowchange (uint16_t cc_data, int* chan, eia608_style_t* style, int* underline) -{ - (*chan) = !! (0x0800 & cc_data); - - if (0x1120 == (0x7770 & cc_data)) { - (*style) = (0x000E & cc_data) >> 1; - (*underline) = 0x0001 & cc_data; - } - - return 1; -} -//////////////////////////////////////////////////////////////////////////////// -// control command -eia608_control_t eia608_parse_control (uint16_t cc_data, int* cc) -{ - if (0x0200&cc_data) { - (*cc) = (cc_data&0x0800?0x01:0x00); - return (eia608_control_t) (0x177F & cc_data); - } else { - (*cc) = (cc_data&0x0800?0x01:0x00) | (cc_data&0x0100?0x02:0x00); - return (eia608_control_t) (0x167F & cc_data); - } -} - -uint16_t eia608_control_command (eia608_control_t cmd, int cc) -{ - uint16_t c = (cc&0x01) ?0x0800:0x0000; - uint16_t f = (cc&0x02) ?0x0100:0x0000; - - if (eia608_tab_offset_0 == (eia608_control_t) (cmd&0xFFC0)) { - return (eia608_control_t) eia608_parity (cmd|c); - } else { - return (eia608_control_t) eia608_parity (cmd|c|f); - } -} -//////////////////////////////////////////////////////////////////////////////// -// text -static const char* utf8_from_index (int idx) { return (0<=idx && EIA608_CHAR_COUNT>idx) ? eia608_char_map[idx] : ""; } -static int eia608_to_index (uint16_t cc_data, int* chan, int* c1, int* c2) -{ - (*c1) = (*c2) = -1; (*chan) = 0; - cc_data &= 0x7F7F; // strip off parity bits - - // Handle Basic NA BEFORE we strip the channel bit - if (eia608_is_basicna (cc_data)) { - // we got first char, yes. But what about second char? - (*c1) = (cc_data>>8) - 0x20; - cc_data &= 0x00FF; - - if (0x0020<=cc_data && 0x0080>cc_data) { - (*c2) = cc_data - 0x20; - return 2; - } - - return 1; - } - - // Check then strip second channel toggle - (*chan) = cc_data & 0x0800; - cc_data = cc_data & 0xF7FF; - - if (eia608_is_specialna (cc_data)) { - // Special North American character - (*c1) = cc_data - 0x1130 + 0x60; - return 1; - } - - if (0x1220<=cc_data && 0x1240>cc_data) { - // Extended Western European character set, Spanish/Miscellaneous/French - (*c1) = cc_data - 0x1220 + 0x70; - return 1; - } - - if (0x1320<=cc_data && 0x1340>cc_data) { - // Extended Western European character set, Portuguese/German/Danish - (*c1) = cc_data - 0x1320 + 0x90; - return 1; - } - - return 0; -} - - -int eia608_to_utf8 (uint16_t c, int* chan, char* str1, char* str2) -{ - int c1, c2; - int size = (int) eia608_to_index (c,chan,&c1,&c2); - strncpy (str1, utf8_from_index (c1),5); - strncpy (str2, utf8_from_index (c2),5); - return size; -} - -uint16_t eia608_from_basicna (uint16_t bna1, uint16_t bna2) -{ - if (! eia608_is_basicna (bna1) || ! eia608_is_basicna (bna2)) { - return 0; - } - - return eia608_parity ( ( (0xFF00&bna1) >>0) | ( (0xFF00&bna2) >>8)); -} - -// prototype for re2c generated function -uint16_t _eia608_from_utf8 (const utf8_char_t* s); -uint16_t eia608_from_utf8_1 (const utf8_char_t* c, int chan) -{ - uint16_t cc_data = _eia608_from_utf8 (c); - - if (0 == cc_data) { - return cc_data; - } - - if (chan && ! eia608_is_basicna (cc_data)) { - cc_data |= 0x0800; - } - - return eia608_parity (cc_data); -} - -uint16_t eia608_from_utf8_2 (const utf8_char_t* c1, const utf8_char_t* c2) -{ - uint16_t cc1 = _eia608_from_utf8 (c1); - uint16_t cc2 = _eia608_from_utf8 (c2); - return eia608_from_basicna (cc1,cc2); -} -//////////////////////////////////////////////////////////////////////////////// -void eia608_dump (uint16_t cc_data) -{ - eia608_style_t style; - const char* text = 0; - char char1[5], char2[5]; - char1[0] = char2[0] = 0; - int row, col, chan, underline; - - if (!eia608_parity_varify (cc_data)) { - text = "parity failed"; - } else if (0 == eia608_parity_strip (cc_data)) { - text = "pad"; - } else if (eia608_is_basicna (cc_data)) { - text = "basicna"; - eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]); - } else if (eia608_is_specialna (cc_data)) { - text = "specialna"; - eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]); - } else if (eia608_is_westeu (cc_data)) { - text = "westeu"; - eia608_to_utf8 (cc_data,&chan,&char1[0],&char2[0]); - } else if (eia608_is_xds (cc_data)) { - text = "xds"; - } else if (eia608_is_midrowchange (cc_data)) { - text = "midrowchange"; - } else if (eia608_is_norpak (cc_data)) { - text = "norpak"; - } else if (eia608_is_preamble (cc_data)) { - text = "preamble"; - eia608_parse_preamble (cc_data, &row, &col, &style, &chan, &underline); - fprintf (stderr,"preamble %d %d %d %d %d\n", row, col, style, chan, underline); - - } else if (eia608_is_control (cc_data)) { - switch (eia608_parse_control (cc_data,&chan)) { - - default: text = "unknown_control"; break; - - case eia608_tab_offset_0: text = "eia608_tab_offset_0"; break; - - case eia608_tab_offset_1: text = "eia608_tab_offset_1"; break; - - case eia608_tab_offset_2:text = "eia608_tab_offset_2"; break; - - case eia608_tab_offset_3: text = "eia608_tab_offset_3"; break; - - case eia608_control_resume_caption_loading: text = "eia608_control_resume_caption_loading"; break; - - case eia608_control_backspace: text = "eia608_control_backspace"; break; - - case eia608_control_alarm_off: text = "eia608_control_alarm_off"; break; - - case eia608_control_alarm_on: text = "eia608_control_alarm_on"; break; - - case eia608_control_delete_to_end_of_row: text = "eia608_control_delete_to_end_of_row"; break; - - case eia608_control_roll_up_2: text = "eia608_control_roll_up_2"; break; - - case eia608_control_roll_up_3: text = "eia608_control_roll_up_3"; break; - - case eia608_control_roll_up_4: text = "eia608_control_roll_up_4"; break; - - case eia608_control_resume_direct_captioning: text = "eia608_control_resume_direct_captioning"; break; - - case eia608_control_text_restart: text = "eia608_control_text_restart"; break; - - case eia608_control_text_resume_text_display: text = "eia608_control_text_resume_text_display"; break; - - case eia608_control_erase_display_memory: text = "eia608_control_erase_display_memory"; break; - - case eia608_control_carriage_return: text = "eia608_control_carriage_return"; break; - - case eia608_control_erase_non_displayed_memory:text = "eia608_control_erase_non_displayed_memory"; break; - - case eia608_control_end_of_caption: text = "eia608_control_end_of_caption"; break; - } - } else { - text = "unhandled"; - } - - fprintf (stderr,"cc %04X (%04X) '%s' '%s' (%s)\n", cc_data, eia608_parity_strip (cc_data), char1, char2, text); -} -//////////////////////////////////////////////////////////////////////////////// -// below this line is re2c uint16_t _eia608_from_utf8 (const utf8_char_t* s) { - const unsigned char* YYMARKER; // needed by default rule + const unsigned char* YYMARKER = 0; const unsigned char* YYCURSOR = (const unsigned char*) s; if (0==s) { return 0x0000;} @@ -301,13 +44,13 @@ uint16_t _eia608_from_utf8 (const utf8_char_t* s) "\x5C" { /*REVERSE_SOLIDUS*/ return 0x132B; } "\x5E" { /*CIRCUMFLEX_ACCENT*/ return 0x132C; } "\x5F" { /*LOW_LINE*/ return 0x132D; } - /*Lets Map this to a LEFT_SINGLE_QUOTATION_MARK, just so we have a cc_data for every printable ASCII value*/ - "\x60" { /*GRAVE_ACCENT, No equivalent return 0x0000; return 1;*/ /*LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; } + /*Map GRAVE_ACCENT to a LEFT_SINGLE_QUOTATION_MARK so we have a cc_data for every printable ASCII value*/ + "\x60" { /*GRAVE_ACCENT -> LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; } "\x7B" { /*LEFT_CURLY_BRACKET*/ return 0x1329; } "\x7C" { /*VERTICAL_LINE*/ return 0x132E; } "\x7D" { /*RIGHT_CURLY_BRACKET*/ return 0x132A; } "\x7E" { /*TILDE*/ return 0x132F; } - /*There is a controll equivilant. Havnt decided if we want to habcle that here, or not*/ + /*There is a control equivalent. Haven't decided if we want to handle that here or not*/ "\x7F" { /*DEL/BACKSPACE. Need to set bits 9 and 12! return 0x1421;*/ return 0x0000; } /* Rules are processed top to bottom. So All single byte chars MUST be above this line!*/ @@ -416,6 +159,6 @@ uint16_t _eia608_from_utf8 (const utf8_char_t* s) "\xE2\x94\x98" { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT*/ return 0x133F; } /*Default rule*/ - [^] { /*DEFAULT_RULE*/ return 0x0000; } + * { /*DEFAULT_RULE*/ return 0x0000; } */ } diff --git a/deps/libcaption/src/mpeg.c b/deps/libcaption/src/mpeg.c new file mode 100644 index 000000000..14fc6c2eb --- /dev/null +++ b/deps/libcaption/src/mpeg.c @@ -0,0 +1,746 @@ +/**********************************************************************************************/ +/* The MIT License */ +/* */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ +/* THE SOFTWARE. */ +/**********************************************************************************************/ + +#include "mpeg.h" +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// +// AVC RBSP Methods +// TODO move the to a avcutils file +static size_t _find_emulation_prevention_byte(const uint8_t* data, size_t size) +{ + size_t offset = 2; + + while (offset < size) { + if (0 == data[offset]) { + // 0 0 X 3 //; we know X is zero + offset += 1; + } else if (3 != data[offset]) { + // 0 0 X 0 0 3; we know X is not 0 and not 3 + offset += 3; + } else if (0 != data[offset - 1]) { + // 0 X 0 0 3 + offset += 2; + } else if (0 != data[offset - 2]) { + // X 0 0 3 + offset += 1; + } else { + // 0 0 3 + return offset; + } + } + + return size; +} + +static size_t _copy_to_rbsp(uint8_t* destData, size_t destSize, const uint8_t* sorcData, size_t sorcSize) +{ + size_t toCopy, totlSize = 0; + + for (;;) { + if (destSize >= sorcSize) { + return 0; + } + + // The following line IS correct! We want to look in sorcData up to destSize bytes + // We know destSize is smaller than sorcSize because of the previous line + toCopy = _find_emulation_prevention_byte(sorcData, destSize); + memcpy(destData, sorcData, toCopy); + totlSize += toCopy; + destData += toCopy; + destSize -= toCopy; + + if (0 == destSize) { + return totlSize; + } + + // skip the emulation prevention byte + totlSize += 1; + sorcData += toCopy + 1; + sorcSize -= toCopy + 1; + } + + return 0; +} +//////////////////////////////////////////////////////////////////////////////// +static inline size_t _find_emulated(uint8_t* data, size_t size) +{ + size_t offset = 2; + + while (offset < size) { + if (3 < data[offset]) { + // 0 0 X; we know X is not 0, 1, 2 or 3 + offset += 3; + } else if (0 != data[offset - 1]) { + // 0 X 0 0 1 + offset += 2; + } else if (0 != data[offset - 2]) { + // X 0 0 1 + offset += 1; + } else { + // 0 0 0, 0 0 1 + return offset; + } + } + + return size; +} + +size_t _copy_from_rbsp(uint8_t* data, uint8_t* payloadData, size_t payloadSize) +{ + size_t total = 0; + + while (payloadSize) { + size_t bytes = _find_emulated(payloadData, payloadSize); + + if (bytes > payloadSize) { + return 0; + } + + memcpy(data, payloadData, bytes); + + if (bytes == payloadSize) { + return total + bytes; + } + + data[bytes] = 3; // insert emulation prevention byte + data += bytes + 1; + total += bytes + 1; + payloadData += bytes; + payloadSize -= bytes; + } + + return total; +} +//////////////////////////////////////////////////////////////////////////////// +sei_message_t* sei_message_next(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->next; } +sei_msgtype_t sei_message_type(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->type; } +size_t sei_message_size(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->size; } +uint8_t* sei_message_data(sei_message_t* msg) { return ((uint8_t*)msg) + sizeof(struct _sei_message_t); } +void sei_message_free(sei_message_t* msg) +{ + if (msg) { + free(msg); + } +} + +sei_message_t* sei_message_new(sei_msgtype_t type, uint8_t* data, size_t size) +{ + struct _sei_message_t* msg = (struct _sei_message_t*)malloc(sizeof(struct _sei_message_t) + size); + msg->next = 0; + msg->type = type; + msg->size = size; + + if (data) { + memcpy(sei_message_data(msg), data, size); + } else { + memset(sei_message_data(msg), 0, size); + } + + return (sei_message_t*)msg; +} +//////////////////////////////////////////////////////////////////////////////// +void sei_init(sei_t* sei, double timestamp) +{ + sei->head = 0; + sei->tail = 0; + sei->timestamp = timestamp; +} + +void sei_message_append(sei_t* sei, sei_message_t* msg) +{ + if (0 == sei->head) { + sei->head = msg; + sei->tail = msg; + } else { + sei->tail->next = msg; + sei->tail = msg; + } +} + +void sei_cat(sei_t* to, sei_t* from, int itu_t_t35) +{ + if (!to || !from) { + return; + } + + sei_message_t* msg = NULL; + for (msg = sei_message_head(from); msg; msg = sei_message_next(msg)) { + if (itu_t_t35 || sei_type_user_data_registered_itu_t_t35 != msg->type) { + sei_message_append(to, sei_message_copy(msg)); + } + } +} + +void sei_free(sei_t* sei) +{ + sei_message_t* tail; + + while (sei->head) { + tail = sei->head->next; + free(sei->head); + sei->head = tail; + } + + sei_init(sei, 0); +} + +void sei_dump(sei_t* sei) +{ + fprintf(stderr, "SEI %p\n", sei); + sei_dump_messages(sei->head, sei->timestamp); +} + +void sei_dump_messages(sei_message_t* head, double timestamp) +{ + cea708_t cea708; + sei_message_t* msg; + cea708_init(&cea708, timestamp); + + for (msg = head; msg; msg = sei_message_next(msg)) { + uint8_t* data = sei_message_data(msg); + size_t size = sei_message_size(msg); + fprintf(stderr, "-- Message %p\n-- Message Type: %d\n-- Message Size: %d\n", data, sei_message_type(msg), (int)size); + + while (size) { + fprintf(stderr, "%02X ", *data); + ++data; + --size; + } + + fprintf(stderr, "\n"); + + if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) { + if (LIBCAPTION_OK != cea708_parse_h262(sei_message_data(msg), sei_message_size(msg), &cea708)) { + fprintf(stderr, "cea708_parse error\n"); + } else { + cea708_dump(&cea708); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +size_t sei_render_size(sei_t* sei) +{ + if (!sei || !sei->head) { + return 0; + } + + size_t size = 2; // nalu_type + stop bit + sei_message_t* msg; + for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) { + size += 1 + (msg->type / 255); + size += 1 + (msg->size / 255); + size += 1 + (msg->size * 4 / 3); + } + + return size; +} + +// we can safely assume sei_render_size() bytes have been allocated for data +size_t sei_render(sei_t* sei, uint8_t* data) +{ + if (!sei || !sei->head) { + return 0; + } + + size_t escaped_size, size = 2; // nalu_type + stop bit + sei_message_t* msg; + (*data) = 6; + ++data; + + for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) { + int payloadType = sei_message_type(msg); + int payloadSize = (int)sei_message_size(msg); + uint8_t* payloadData = sei_message_data(msg); + + while (255 <= payloadType) { + (*data) = 255; + ++data; + ++size; + payloadType -= 255; + } + + (*data) = payloadType; + ++data; + ++size; + + while (255 <= payloadSize) { + (*data) = 255; + ++data; + ++size; + payloadSize -= 255; + } + + (*data) = payloadSize; + ++data; + ++size; + + if (0 >= (escaped_size = _copy_from_rbsp(data, payloadData, payloadSize))) { + return 0; + } + + data += escaped_size; + size += escaped_size; + } + + // write stop bit and return + (*data) = 0x80; + return size; +} + +uint8_t* sei_render_alloc(sei_t* sei, size_t* size) +{ + size_t aloc = sei_render_size(sei); + uint8_t* data = malloc(aloc); + (*size) = sei_render(sei, data); + return data; +} + +//////////////////////////////////////////////////////////////////////////////// +libcaption_stauts_t sei_parse(sei_t* sei, const uint8_t* data, size_t size, double timestamp) +{ + sei_init(sei, timestamp); + int ret = 0; + + // SEI may contain more than one payload + while (1 < size) { + size_t payloadType = 0; + size_t payloadSize = 0; + + while (0 < size && 255 == (*data)) { + payloadType += 255; + ++data, --size; + } + + if (0 == size) { + return LIBCAPTION_ERROR; + } + + payloadType += (*data); + ++data, --size; + + while (0 < size && 255 == (*data)) { + payloadSize += 255; + ++data, --size; + } + + if (0 == size) { + return LIBCAPTION_ERROR; + } + + payloadSize += (*data); + ++data, --size; + + if (payloadSize) { + sei_message_t* msg = sei_message_new((sei_msgtype_t)payloadType, 0, payloadSize); + uint8_t* payloadData = sei_message_data(msg); + size_t bytes = _copy_to_rbsp(payloadData, payloadSize, data, size); + sei_message_append(sei, msg); + + if (bytes < payloadSize) { + return LIBCAPTION_ERROR; + } + + data += bytes; + size -= bytes; + ++ret; + } + } + + // There should be one trailing byte, 0x80. But really, we can just ignore that fact. + return LIBCAPTION_OK; +} +//////////////////////////////////////////////////////////////////////////////// +libcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame) +{ + cea708_t cea708; + sei_message_t* msg; + libcaption_stauts_t status = LIBCAPTION_OK; + + cea708_init(&cea708, frame->timestamp); + + for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) { + if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) { + cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), &cea708); + status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708)); + } + } + + if (LIBCAPTION_READY == status) { + frame->timestamp = sei->timestamp; + } + + return status; +} +//////////////////////////////////////////////////////////////////////////////// +#define DEFAULT_CHANNEL 0 + +void sei_append_708(sei_t* sei, cea708_t* cea708) +{ + sei_message_t* msg = sei_message_new(sei_type_user_data_registered_itu_t_t35, 0, CEA608_MAX_SIZE); + msg->size = cea708_render(cea708, sei_message_data(msg), sei_message_size(msg)); + sei_message_append(sei, msg); + cea708_init(cea708, sei->timestamp); // will confgure using HLS compatiable defaults +} + +// This should be moved to 708.c +// This works for popon, but bad for paint on and roll up +// Please understand this function before you try to use it, setting null values have different effects than you may assume +void sei_encode_eia608(sei_t* sei, cea708_t* cea708, uint16_t cc_data) +{ + // This one is full, flush and init a new one + // shoudl this be 32? I cant remember + if (31 == cea708->user_data.cc_count) { + sei_append_708(sei, cea708); + } + + if (0 == cea708->user_data.cc_count) { // This is a new 708 header, but a continuation of a 608 stream + cea708_add_cc_data(cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL)); + cea708_add_cc_data(cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL)); + } + + if (0 == cc_data) { // Finished + sei_encode_eia608(sei, cea708, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL)); + sei_encode_eia608(sei, cea708, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL)); + sei_append_708(sei, cea708); + return; + } + + cea708_add_cc_data(cea708, 1, cc_type_ntsc_cc_field_1, cc_data); +} +//////////////////////////////////////////////////////////////////////////////// +// TODO move this out of sei +libcaption_stauts_t sei_from_caption_frame(sei_t* sei, caption_frame_t* frame) +{ + int r, c; + int unl, prev_unl; + cea708_t cea708; + const char* data; + uint16_t prev_cc_data; + eia608_style_t styl, prev_styl; + + sei_init(sei, frame->timestamp); + cea708_init(&cea708, frame->timestamp); // set up a new popon frame + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL)); + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL)); + + for (r = 0; r < SCREEN_ROWS; ++r) { + prev_unl = 0, prev_styl = eia608_style_white; + // Calculate preamble + for (c = 0; c < SCREEN_COLS && 0 == *caption_frame_read_char(frame, r, c, &styl, &unl); ++c) { + } + + // This row is blank + if (SCREEN_COLS == c) { + continue; + } + + // Write preamble + if (0 < c || (0 == unl && eia608_style_white == styl)) { + int tab = c % 4; + sei_encode_eia608(sei, &cea708, eia608_row_column_pramble(r, c, DEFAULT_CHANNEL, 0)); + if (tab) { + sei_encode_eia608(sei, &cea708, eia608_tab(tab, DEFAULT_CHANNEL)); + } + } else { + sei_encode_eia608(sei, &cea708, eia608_row_style_pramble(r, DEFAULT_CHANNEL, styl, unl)); + prev_unl = unl, prev_styl = styl; + } + + // Write the row + for (prev_cc_data = 0, data = caption_frame_read_char(frame, r, c, 0, 0); + (*data) && c < SCREEN_COLS; ++c, data = caption_frame_read_char(frame, r, c, &styl, &unl)) { + uint16_t cc_data = eia608_from_utf8_1(data, DEFAULT_CHANNEL); + + if (unl != prev_unl || styl != prev_styl) { + sei_encode_eia608(sei, &cea708, eia608_midrow_change(DEFAULT_CHANNEL, styl, unl)); + prev_unl = unl, prev_styl = styl; + } + + if (!cc_data) { + // We do't want to write bad data, so just ignore it. + } else if (eia608_is_basicna(prev_cc_data)) { + if (eia608_is_basicna(cc_data)) { + // previous and current chars are both basicna, combine them into current + sei_encode_eia608(sei, &cea708, eia608_from_basicna(prev_cc_data, cc_data)); + } else if (eia608_is_westeu(cc_data)) { + // extended charcters overwrite the previous charcter, so insert a dummy char thren write the extended char + sei_encode_eia608(sei, &cea708, eia608_from_basicna(prev_cc_data, eia608_from_utf8_1(EIA608_CHAR_SPACE, DEFAULT_CHANNEL))); + sei_encode_eia608(sei, &cea708, cc_data); + } else { + // previous was basic na, but current isnt; write previous and current + sei_encode_eia608(sei, &cea708, prev_cc_data); + sei_encode_eia608(sei, &cea708, cc_data); + } + + prev_cc_data = 0; // previous is handled, we can forget it now + } else if (eia608_is_westeu(cc_data)) { + // extended chars overwrite the previous chars, so insert a dummy char + // TODO create a map of alternamt chars for eia608_is_westeu instead of using space + sei_encode_eia608(sei, &cea708, eia608_from_utf8_1(EIA608_CHAR_SPACE, DEFAULT_CHANNEL)); + sei_encode_eia608(sei, &cea708, cc_data); + } else if (eia608_is_basicna(cc_data)) { + prev_cc_data = cc_data; + } else { + sei_encode_eia608(sei, &cea708, cc_data); + } + + if (eia608_is_specialna(cc_data)) { + // specialna are treated as control charcters. Duplicated control charcters are discarded + // So we write a resume after a specialna as a noop to break repetition detection + // TODO only do this if the same charcter is repeated + sei_encode_eia608(sei, &cea708, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL)); + } + } + + if (0 != prev_cc_data) { + sei_encode_eia608(sei, &cea708, prev_cc_data); + } + } + + sei_encode_eia608(sei, &cea708, 0); // flush + sei->timestamp = frame->timestamp; // assumes in order frames + // sei_dump (sei); + return LIBCAPTION_OK; +} + +libcaption_stauts_t sei_from_scc(sei_t* sei, const scc_t* scc) +{ + unsigned int i; + cea708_t cea708; + cea708_init(&cea708, sei->timestamp); // set up a new popon frame + + for (i = 0; i < scc->cc_size; ++i) { + if (31 == cea708.user_data.cc_count) { + sei_append_708(sei, &cea708); + } + + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, scc->cc_data[i]); + } + + if (0 != cea708.user_data.cc_count) { + sei_append_708(sei, &cea708); + } + + return LIBCAPTION_OK; +} + +libcaption_stauts_t sei_from_caption_clear(sei_t* sei) +{ + cea708_t cea708; + cea708_init(&cea708, sei->timestamp); // set up a new popon frame + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL)); + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL)); + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL)); + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL)); + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_display_memory, DEFAULT_CHANNEL)); + cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_display_memory, DEFAULT_CHANNEL)); + sei_append_708(sei, &cea708); + return LIBCAPTION_OK; +} +//////////////////////////////////////////////////////////////////////////////// +// bitstream +void mpeg_bitstream_init(mpeg_bitstream_t* packet) +{ + packet->dts = 0; + packet->cts = 0; + packet->size = 0; + packet->front = 0; + packet->latent = 0; + packet->status = LIBCAPTION_OK; +} + +uint8_t mpeg_bitstream_packet_type(mpeg_bitstream_t* packet, unsigned stream_type) +{ + if (4 > packet->size) { + return 0; + } + switch (stream_type) { + case STREAM_TYPE_H262: + return packet->data[3]; + case STREAM_TYPE_H264: + return packet->data[3] & 0x1F; + case STREAM_TYPE_H265: + return (packet->data[3] >> 1) & 0x3F; + default: + return 0; + } +} + +// TODO optomize +// static size_t find_start_code_increnental(const uint8_t* data, size_t size, size_t prev_size) +// { +// // prev_size MUST be at least 4 +// assert(3 < prev_size); +// uint32_t start_code = 0xffffffff; +// for (size_t i = prev_size - 3; i < size; ++i) { +// start_code = (start_code << 8) | data[i]; +// if (0x00000100 == (start_code & 0xffffff00)) { +// return i - 3; +// } +// } +// return 0; +// } + +static size_t find_start_code(const uint8_t* data, size_t size) +{ + uint32_t start_code = 0xffffffff; + for (size_t i = 1; i < size; ++i) { + start_code = (start_code << 8) | data[i]; + if (0x00000100 == (start_code & 0xffffff00)) { + return i - 3; + } + } + return 0; +} + +// WILL wrap around if larger than MAX_REFRENCE_FRAMES for memory saftey +cea708_t* _mpeg_bitstream_cea708_at(mpeg_bitstream_t* packet, size_t pos) { return &packet->cea708[(packet->front + pos) % MAX_REFRENCE_FRAMES]; } +cea708_t* _mpeg_bitstream_cea708_front(mpeg_bitstream_t* packet) { return _mpeg_bitstream_cea708_at(packet, 0); } +cea708_t* _mpeg_bitstream_cea708_back(mpeg_bitstream_t* packet) { return _mpeg_bitstream_cea708_at(packet, packet->latent - 1); } +cea708_t* _mpeg_bitstream_cea708_emplace_back(mpeg_bitstream_t* packet, double timestamp) +{ + ++packet->latent; + cea708_t* cea708 = _mpeg_bitstream_cea708_back(packet); + cea708_init(cea708, timestamp); + return cea708; +} + +void _mpeg_bitstream_cea708_sort(mpeg_bitstream_t* packet) +{ + // TODO better sort? (for small nearly sorted lists bubble is difficult to beat) + // This must be stable, decending sort +again: + for (size_t i = 1; i < packet->latent; ++i) { + cea708_t c; + cea708_t* a = _mpeg_bitstream_cea708_at(packet, i - 1); + cea708_t* b = _mpeg_bitstream_cea708_at(packet, i); + if (a->timestamp > b->timestamp) { + memcpy(&c, a, sizeof(cea708_t)); + memcpy(a, b, sizeof(cea708_t)); + memcpy(b, &c, sizeof(cea708_t)); + goto again; + } + } +} + +// Removes items from front +size_t mpeg_bitstream_flush(mpeg_bitstream_t* packet, caption_frame_t* frame) +{ + if (packet->latent) { + cea708_t* cea708 = _mpeg_bitstream_cea708_front(packet); + packet->status = libcaption_status_update(LIBCAPTION_OK, cea708_to_caption_frame(frame, cea708)); + packet->front = (packet->front + 1) % MAX_REFRENCE_FRAMES; + --packet->latent; + } + + return packet->latent; +} + +void _mpeg_bitstream_cea708_sort_flush(mpeg_bitstream_t* packet, caption_frame_t* frame, double dts) +{ + _mpeg_bitstream_cea708_sort(packet); + // Loop will terminate on LIBCAPTION_READY + while (packet->latent && packet->status == LIBCAPTION_OK && _mpeg_bitstream_cea708_front(packet)->timestamp < dts) { + mpeg_bitstream_flush(packet, frame); + } +} + +size_t mpeg_bitstream_parse(mpeg_bitstream_t* packet, caption_frame_t* frame, const uint8_t* data, size_t size, unsigned stream_type, double dts, double cts) +{ + if (MAX_NALU_SIZE <= packet->size) { + packet->status = LIBCAPTION_ERROR; + // fprintf(stderr, "LIBCAPTION_ERROR\n"); + return 0; + } + + // consume upto MAX_NALU_SIZE bytes + if (MAX_NALU_SIZE <= packet->size + size) { + size = MAX_NALU_SIZE - packet->size; + } + + sei_t sei; + size_t header_size, scpos; + packet->status = LIBCAPTION_OK; + memcpy(&packet->data[packet->size], data, size); + packet->size += size; + + while (packet->status == LIBCAPTION_OK && 0 < (scpos = find_start_code(&packet->data[0], packet->size))) { + switch (mpeg_bitstream_packet_type(packet, stream_type)) { + default: + break; + case H262_SEI_PACKET: + header_size = 4; + if (STREAM_TYPE_H262 == stream_type && scpos > header_size) { + cea708_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts); + packet->status = libcaption_status_update(packet->status, cea708_parse_h262(&packet->data[header_size], scpos - header_size, cea708)); + _mpeg_bitstream_cea708_sort_flush(packet, frame, dts); + } + break; + case H264_SEI_PACKET: + case H265_SEI_PACKET: + header_size = STREAM_TYPE_H264 == stream_type ? 4 : STREAM_TYPE_H265 == stream_type ? 5 : 0; + if (header_size && scpos > header_size) { + packet->status = libcaption_status_update(packet->status, sei_parse(&sei, &packet->data[header_size], scpos - header_size, dts + cts)); + for (sei_message_t* msg = sei_message_head(&sei); msg; msg = sei_message_next(msg)) { + if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) { + cea708_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts); + packet->status = libcaption_status_update(packet->status, cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), cea708)); + _mpeg_bitstream_cea708_sort_flush(packet, frame, dts); + } + } + } + break; + } + + packet->size -= scpos; + memmove(&packet->data[0], &packet->data[scpos], packet->size); + } + + return size; +} +//////////////////////////////////////////////////////////////////////////////// +// // h262 +// libcaption_stauts_t h262_user_data_to_caption_frame(caption_frame_t* frame, mpeg_bitstream_t* packet, double dts, double cts) +// { +// cea708_t cea708; +// libcaption_stauts_t status = LIBCAPTION_OK; + +// cea708_init(&cea708); +// size_t size = mpeg_bitstream_size(packet, STREAM_TYPE_H262); +// const uint8_t* data = mpeg_bitstream_data(packet, STREAM_TYPE_H262); +// status = cea708_parse_h262(data, size, &cea708); +// // cea708_dump(&cea708); +// status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708, dts + cts)); + +// if (LIBCAPTION_READY == status) { +// frame->timestamp = dts + cts; +// } + +// return status; +// } diff --git a/deps/libcaption/src/scc.c b/deps/libcaption/src/scc.c index 7b42487c1..643120e00 100644 --- a/deps/libcaption/src/scc.c +++ b/deps/libcaption/src/scc.c @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -24,31 +24,90 @@ #include "scc.h" #include "utf8.h" #include -#include #include +#include -#define FRAME_RATE (1000.0/30) -#define SCCTIME2MS(HH,MM,SS,FF) (((HH*3600.0 + MM*60.0 + SS) * 1000.0) + ( FF * FRAME_RATE )) +static scc_t* scc_relloc(scc_t* scc, unsigned int cc_count) +{ + if (0 == scc || scc->cc_aloc < cc_count) { + // alloc 1.5 time what is asked for. + scc = (scc_t*)realloc(scc, sizeof(scc_t) + ((cc_count * 15 / 10) * sizeof(uint16_t))); + scc->cc_aloc = cc_count; + } -// 00:00:25:16 9420 9440 aeae ae79 ef75 2068 6176 e520 79ef 75f2 20f2 ef62 eff4 e9e3 732c 2061 6e64 2049 94fe 9723 ea75 73f4 20f7 616e f420 f4ef 2062 e520 61f7 e573 ef6d e520 e96e 2073 7061 e3e5 ae80 942c 8080 8080 942f + return scc; +} + +scc_t* scc_new(int cc_count) +{ + scc_t* scc = scc_relloc(0, cc_count); + scc->timestamp = 0.0; + scc->cc_size = 0; + return scc; +} + +scc_t* scc_free(scc_t* scc) +{ + free(scc); + return NULL; +} + +double scc_time_to_timestamp(int hh, int mm, int ss, int ff) +{ + return (hh * 3600.0) + (mm * 60.0) + ss + (ff / 29.97); +} -int scc_to_608 (const char* line, double* pts, uint16_t* cc, int cc_max) +// 00:00:25:16 9420 9440 aeae ae79 ef75 2068 6176 e520 79ef 75f2 20f2 ef62 eff4 e9e3 732c 2061 6e64 2049 94fe 9723 ea75 73f4 20f7 616e f420 f4ef 2062 e520 61f7 e573 ef6d e520 e96e 2073 7061 e3e5 ae80 942c 8080 8080 942f +size_t scc_to_608(scc_t** scc, const utf8_char_t* data) { - int cc_count = 0, cc_data = 0, hh = 0, mm = 0, ss = 0, ff = 0; + size_t llen, size = 0; + int v1 = 0, v2 = 0, hh = 0, mm = 0, ss = 0, ff = 0, cc_data = 0; + + if (0 == data) { + return 0; + } + + if ((*scc)) { + (*scc)->cc_size = 0; + } + + // skip 'Scenarist_SCC V1.0' header + if (2 == sscanf(data, "Scenarist_SCC V%1d.%1d", &v1, &v2)) { + data += 18, size += 18; - // TODO if ';' use 29.79 fps, if ':' use 30 fls - if (4 == sscanf (line, "%2d:%2d:%2d%*1[:;]%2d", &hh, &mm, &ss, &ff)) { - (*pts) = SCCTIME2MS (hh,mm,ss,ff); // scc files start at one hour for some reason - line += 12; + if (1 != v1 || 0 != v2) { + return 0; + } + } + + // Skip blank lines + for (;;) { + llen = utf8_line_length(data); + + if (0 == llen || 0 != utf8_trimmed_length(data, llen)) { + break; + } + + data += llen; + size += llen; + } - while (1 == sscanf (line, "%04x ", &cc_data)) { - line += 5; cc[cc_count] = cc_data; ++cc_count; + if (4 == sscanf(data, "%2d:%2d:%2d%*1[:;]%2d", &hh, &mm, &ss, &ff)) { + data += 12, size += 12; + // Get length of the remaining charcters + llen = utf8_line_length(data); + llen = utf8_trimmed_length(data, llen); + unsigned int max_cc_count = 1 + ((unsigned int)llen / 5); + (*scc) = scc_relloc((*scc), max_cc_count * 15 / 10); + (*scc)->timestamp = scc_time_to_timestamp(hh, mm, ss, ff); + (*scc)->cc_size = 0; - if (cc_count >= cc_max) { - break; - } + while ((*scc)->cc_size < max_cc_count && 1 == sscanf(data, "%04x", &cc_data)) { + (*scc)->cc_data[(*scc)->cc_size] = (uint16_t)cc_data; + (*scc)->cc_size += 1; + data += 5, size += 5; } } - return cc_count; + return size; } diff --git a/deps/libcaption/src/srt.c b/deps/libcaption/src/srt.c index badf5e436..48891ade1 100644 --- a/deps/libcaption/src/srt.c +++ b/deps/libcaption/src/srt.c @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -23,172 +23,37 @@ /**********************************************************************************************/ #include "srt.h" #include "utf8.h" +#include "vtt.h" #include -#include #include +#include - - -srt_t* srt_new (const utf8_char_t* data, size_t size, double timestamp, srt_t* prev, srt_t** head) -{ - srt_t* srt = malloc (sizeof (srt_t)+size+1); - srt->next = 0; - srt->duration = 0; - srt->aloc = size; - srt->timestamp = timestamp; - utf8_char_t* dest = (utf8_char_t*) srt_data (srt); - - if (prev) { - prev->next = srt; - prev->duration = timestamp - prev->timestamp; - } - - if (head && 0 == (*head)) { - (*head) = srt; - } - - if (data) { - memcpy (dest, data, size); - } else { - memset (dest, 0, size); - } - - dest[size] = '\0'; - return srt; -} - -srt_t* srt_free_head (srt_t* head) -{ - srt_t* next = head->next; - free (head); - return next; -} - -void srt_free (srt_t* srt) -{ - while (srt) { - srt = srt_free_head (srt); - } -} - -#define SRTTIME2SECONDS(HH,MM,SS,MS) ((HH*3600.0) + (MM*60.0) + SS + (MS/1000.0)) -srt_t* srt_parse (const utf8_char_t* data, size_t size) -{ - int counter; - srt_t* head = 0, *prev = 0; - double str_pts = 0, end_pts = 0; - size_t line_length = 0, trimmed_length = 0; - int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2; - - for (;;) { - line_length = 0; - - do { - data += line_length; - size -= line_length; - line_length = utf8_line_length (data); - trimmed_length = utf8_trimmed_length (data,line_length); - // Skip empty lines - } while (0 < line_length && 0 == trimmed_length); - - // linelength cant be zero before EOF - if (0 == line_length) { - break; - } - - counter = atoi (data); - // printf ("counter (%d): '%.*s'\n", line_length, (int) line_length, data); - data += line_length; - size -= line_length; - - line_length = utf8_line_length (data); - // printf ("time (%d): '%.*s'\n", line_length, (int) line_length, data); - - { - if (8 == sscanf (data, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d", &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2)) { - str_pts = SRTTIME2SECONDS (hh1, mm1, ss1, ms1); - end_pts = SRTTIME2SECONDS (hh2, mm2, ss2, ms2); - } - - data += line_length; - size -= line_length; - } - - // Caption text starts here - const utf8_char_t* text = data; - size_t text_size = 0; - // printf ("time: '(%f --> %f)\n",srt.srt_time, srt.end_time); - - do { - line_length = utf8_line_length (data); - trimmed_length = utf8_trimmed_length (data,line_length); - // printf ("cap (%d): '%.*s'\n", line_length, (int) trimmed_length, data); - data += line_length; - size -= line_length; - text_size += line_length; - } while (trimmed_length); - - // should we trim here? - srt_t* srt = srt_new (text,text_size,str_pts,prev,&head); - srt->duration = end_pts - str_pts; - prev = srt; - } - - return head; -} - -int srt_to_caption_frame (srt_t* srt, caption_frame_t* frame) +srt_t* srt_new() { - const char* data = srt_data (srt); - return caption_frame_from_text (frame,data); + return vtt_new(); } -srt_t* srt_from_caption_frame (caption_frame_t* frame, srt_t* prev, srt_t** head) +void srt_free(srt_t* srt) { - // CRLF per row, plus an extra at the end - srt_t* srt = srt_new (0, 2+CAPTION_FRAME_TEXT_BYTES, frame->timestamp, prev, head); - utf8_char_t* data = srt_data (srt); - - caption_frame_to_text (frame,data); - // srt requires an extra new line - strcat ( (char*) data,"\r\n"); - - return srt; + vtt_free(srt); } -static inline void _crack_time (double tt, int* hh, int* mm, int* ss, int* ms) +vtt_t* srt_parse(const utf8_char_t* data, size_t size) { - (*ms) = (int) ((int64_t) (tt * 1000.0) % 1000); - (*ss) = (int) ((int64_t) (tt) % 60); - (*mm) = (int) ((int64_t) (tt / (60.0)) % 60); - (*hh) = (int) ((int64_t) (tt / (60.0*60.0))); + return _vtt_parse(data, size, 1); } -static void _dump (srt_t* head, char type) +void srt_dump(srt_t* srt) { int i; - srt_t* srt; - - if ('v' == type) { - printf ("WEBVTT\r\n"); - } + vtt_block_t* block; - for (srt = head, i = 1; srt; srt=srt_next (srt), ++i) { + for (block = srt->cue_head, i = 1; block; block = block->next, ++i) { int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2; - _crack_time (srt->timestamp, &hh1, &mm1, &ss1, &ms1); - _crack_time (srt->timestamp + srt->duration, &hh2, &mm2, &ss2, &ms2); + vtt_crack_time(block->timestamp, &hh1, &mm1, &ss1, &ms1); + vtt_crack_time(block->timestamp + block->duration, &hh2, &mm2, &ss2, &ms2); - if ('s' == type) { - printf ("%02d\r\n%d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\r\n%s\r\n", i, - hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, srt_data (srt)); - } - - else if ('v' == type) { - printf ("%d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d\r\n%s\r\n", - hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, srt_data (srt)); - } + printf("%02d\r\n%d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\r\n%s\r\n", i, + hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, vtt_block_data(block)); } } - -void srt_dump (srt_t* head) { _dump (head,'s'); } -void vtt_dump (srt_t* head) { _dump (head,'v'); } diff --git a/deps/libcaption/src/utf8.c b/deps/libcaption/src/utf8.c index d237eb221..49c941980 100644 --- a/deps/libcaption/src/utf8.c +++ b/deps/libcaption/src/utf8.c @@ -1,7 +1,7 @@ /**********************************************************************************************/ /* The MIT License */ /* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining a copy */ /* of this software and associated documentation files (the "Software"), to deal */ @@ -22,47 +22,59 @@ /* THE SOFTWARE. */ /**********************************************************************************************/ - - #include "utf8.h" +#include +#include #include -const utf8_char_t* utf8_char_next (const char* s) +const utf8_char_t* utf8_char_next(const utf8_char_t* c) { - if (0x80 == (s[0]&0xC0)) { ++s; } - - return s; + const utf8_char_t* n = c + utf8_char_length(c); + return n == c ? 0 : n; } // returnes the length of the char in bytes -size_t utf8_char_length (const utf8_char_t* c) +size_t utf8_char_length(const utf8_char_t* c) { // count null term as zero size - if (0x00 == c[0]) { return 0; } + if (!c || 0x00 == c[0]) { + return 0; + } - if (0x00 == (c[0]&0x80)) { return 1; } + static const size_t _utf8_char_length[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 + }; - if (0xC0 == (c[0]&0xE0) && 0x80 == (c[1]&0xC0)) { return 2; } + return _utf8_char_length[(c[0] >> 3) & 0x1F]; +} - if (0xE0 == (c[0]&0xF0) && 0x80 == (c[1]&0xC0) && 0x80 == (c[2]&0xC0)) { return 3; } +int utf8_char_whitespace(const utf8_char_t* c) +{ + // 0x7F is DEL + if (!c || (c[0] >= 0 && c[0] <= ' ') || c[0] == 0x7F) { + return 1; + } - if (0xF0 == (c[0]&0xF8) && 0x80 == (c[1]&0xC0) && 0x80 == (c[2]&0xC0) && 0x80 == (c[3]&0xC0)) { return 4; } + // EIA608_CHAR_NO_BREAK_SPACE TODO other utf8 spaces + if (0xC2 == (unsigned char)c[0] && 0xA0 == (unsigned char)c[1]) { + return 1; + } return 0; } // returns length of the string in bytes // size is number of charcter to count (0 to count until NULL term) -size_t utf8_string_length (const utf8_char_t* data, utf8_size_t size) +size_t utf8_string_length(const utf8_char_t* data, utf8_size_t size) { size_t char_length, byts = 0; if (0 == size) { - size = utf8_char_count (data,0); + size = utf8_char_count(data, 0); } - for (; 0 < size ; --size) { - if (0 == (char_length = utf8_char_length (data))) { + for (; 0 < size; --size) { + if (0 == (char_length = utf8_char_length(data))) { break; } @@ -73,12 +85,12 @@ size_t utf8_string_length (const utf8_char_t* data, utf8_size_t size) return byts; } -size_t utf8_char_copy (utf8_char_t* dst, const utf8_char_t* src) +size_t utf8_char_copy(utf8_char_t* dst, const utf8_char_t* src) { - size_t bytes = utf8_char_length (src); + size_t bytes = utf8_char_length(src); - if (bytes&&dst) { - memcpy (dst,src,bytes); + if (bytes && dst) { + memcpy(dst, src, bytes); dst[bytes] = '\0'; } @@ -87,17 +99,17 @@ size_t utf8_char_copy (utf8_char_t* dst, const utf8_char_t* src) // returnes the number of utf8 charcters in a string given the number of bytes // to count until the a null terminator, pass 0 for size -utf8_size_t utf8_char_count (const char* data, size_t size) +utf8_size_t utf8_char_count(const char* data, size_t size) { size_t i, bytes = 0; utf8_size_t count = 0; if (0 == size) { - size = strlen (data); + size = strlen(data); } - for (i = 0 ; i < size ; ++count, i += bytes) { - if (0 == (bytes = utf8_char_length (&data[i]))) { + for (i = 0; i < size; ++count, i += bytes) { + if (0 == (bytes = utf8_char_length(&data[i]))) { break; } } @@ -106,32 +118,39 @@ utf8_size_t utf8_char_count (const char* data, size_t size) } // returnes the length of the line in bytes triming not printable charcters at the end -size_t utf8_trimmed_length (const char* data, size_t size) +size_t utf8_trimmed_length(const utf8_char_t* data, utf8_size_t charcters) { - for (; 0 < size && ' ' >= (uint8_t) data[size-1] ; --size) { } + size_t l, t = 0, split_at = 0; + for (size_t c = 0; (*data) && c < charcters; ++c) { + l = utf8_char_length(data); + t += l, data += l; + if (!utf8_char_whitespace(data)) { + split_at = t; + } + } - return size; + return split_at; } +size_t _utf8_newline(const utf8_char_t* data) +{ + if ('\r' == data[0]) { + return '\n' == data[1] ? 2 : 1; // windows/unix + } else if ('\n' == data[0]) { + return '\r' == data[1] ? 2 : 1; // riscos/macos + } else { + return 0; + } +} // returns the length in bytes of the line including the new line charcter(s) // auto detects between windows(CRLF), unix(LF), mac(CR) and riscos (LFCR) line endings -size_t utf8_line_length (const char* data) +size_t utf8_line_length(const utf8_char_t* data) { - size_t len = 0; + size_t n, len = 0; for (len = 0; 0 != data[len]; ++len) { - if ('\r' == data[len]) { - if ('\n' == data[len+1]) { - return len + 2; // windows - } else { - return len + 1; // unix - } - } else if ('\n' == data[len]) { - if ('\r' == data[len+1]) { - return len + 2; // riscos - } else { - return len + 1; // macos - } + if (0 < (n = _utf8_newline(data))) { + return len + n; } } @@ -139,32 +158,88 @@ size_t utf8_line_length (const char* data) } // returns number of chars to include before split -utf8_size_t utf8_wrap_length (const utf8_char_t* data, utf8_size_t size) +utf8_size_t utf8_wrap_length(const utf8_char_t* data, utf8_size_t size) { // Set split_at to size, so if a split point cna not be found, retuns the size passed in size_t char_length, char_count, split_at = size; - for (char_count = 0 ; char_count <= size ; ++char_count) { - if (' ' >= (*data)) { + for (char_count = 0; char_count <= size; ++char_count) { + if (_utf8_newline(data)) { + return char_count; + } else if (utf8_char_whitespace(data)) { split_at = char_count; } - char_length = utf8_char_length (data); + char_length = utf8_char_length(data); data += char_length; } return split_at; } -int utf8_line_count (const utf8_char_t* data) +int utf8_line_count(const utf8_char_t* data) { size_t len = 0; int count = 0; do { - len = utf8_line_length (data); - data += len; ++count; - } while (0= length2) { + len--; + if (!memcmp(string1, string2, length2)) + return (char*)string1; + string1++; + } + return NULL; } +#endif \ No newline at end of file diff --git a/deps/libcaption/src/vtt.c b/deps/libcaption/src/vtt.c new file mode 100644 index 000000000..5189224a8 --- /dev/null +++ b/deps/libcaption/src/vtt.c @@ -0,0 +1,357 @@ +/**********************************************************************************************/ +/* The MIT License */ +/* */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ +/* THE SOFTWARE. */ +/**********************************************************************************************/ +#include "vtt.h" +#include "utf8.h" +#include +#include +#include + +vtt_block_t* vtt_block_free_head(vtt_block_t* head); + +vtt_t* vtt_new() +{ + vtt_t* vtt = malloc(sizeof(vtt_t)); + memset(vtt, 0, sizeof(vtt_t)); + return vtt; +} + +void vtt_free(vtt_t* vtt) +{ + while (vtt->region_head != NULL) { + vtt->region_head = vtt_block_free_head(vtt->region_head); + } + while (vtt->style_head != NULL) { + vtt->style_head = vtt_block_free_head(vtt->style_head); + } + while (vtt->cue_head != NULL) { + vtt->cue_head = vtt_block_free_head(vtt->cue_head); + } + free(vtt); +} + +vtt_block_t* vtt_block_new(vtt_t* vtt, const utf8_char_t* data, size_t size, enum VTT_BLOCK_TYPE type) +{ + vtt_block_t* block = malloc(sizeof(vtt_block_t) + size + 1); + block->next = NULL; + block->type = type; + block->timestamp = 0.0; + block->duration = 0.0; + block->cue_settings = NULL; + block->cue_id = NULL; + block->text_size = size; + + switch (type) { + case VTT_REGION: + if (vtt->region_head == NULL) { + vtt->region_head = block; + } else { + vtt->region_tail->next = block; + } + vtt->region_tail = block; + break; + case VTT_STYLE: + if (vtt->style_head == NULL) { + vtt->style_head = block; + } else { + vtt->style_tail->next = block; + } + vtt->style_tail = block; + break; + case VTT_CUE: + if (vtt->cue_head == NULL) { + vtt->cue_head = block; + } else { + vtt->cue_tail->next = block; + } + vtt->cue_tail = block; + break; + case VTT_NOTE: + break; + } + + utf8_char_t* dest = (utf8_char_t*)vtt_block_data(block); + if (data) { + memcpy(dest, data, size); + } else { + memset(dest, 0, size); + } + + dest[size] = '\0'; + return block; +} + +vtt_block_t* vtt_block_free_head(vtt_block_t* head) +{ + vtt_block_t* next = head->next; + if (head->cue_id != NULL) { + free(head->cue_id); + } + if (head->cue_settings != NULL) { + free(head->cue_settings); + } + free(head); + return next; +} + +void vtt_cue_free_head(vtt_t* vtt) +{ + vtt->cue_head = vtt_block_free_head(vtt->cue_head); + if (vtt->cue_head == NULL) { + vtt->cue_tail = NULL; + } +} + +void vtt_style_free_head(vtt_t* vtt) +{ + vtt->style_head = vtt_block_free_head(vtt->style_head); + if (vtt->style_head == NULL) { + vtt->style_tail = NULL; + } +} + +void vtt_region_free_head(vtt_t* vtt) +{ + vtt->region_head = vtt_block_free_head(vtt->region_head); + if (vtt->region_head == NULL) { + vtt->region_tail = NULL; + } +} + +#define VTTTIME2SECONDS(HH, MM, SS, MS) ((HH * 3600.0) + (MM * 60.0) + SS + (MS / 1000.0)) +double parse_timestamp(const utf8_char_t* line) +{ + int hh, mm, ss, ms; + if (sscanf(line, "%d:%2d:%2d%*1[,.]%3d", &hh, &mm, &ss, &ms) == 4) { + return VTTTIME2SECONDS(hh, mm, ss, ms); + } + if (sscanf(line, "%2d:%2d%*1[,.]%3d", &mm, &ss, &ms) == 3) { + return VTTTIME2SECONDS(0.0, mm, ss, ms); + } + return -1.0; +} + +void parse_timestamps(const utf8_char_t* line, double* start_pts, double* end_pts, char** cue_settings) +{ + char start_str[32]; + char end_str[32]; + char cue_str[1024]; + + int matches = sscanf(line, " %31s --> %31s%1023[^\n\r]", start_str, end_str, cue_str); + *start_pts = -1; + *cue_settings = NULL; + + printf("Matches: %d\n", matches); + + if (matches >= 1) { + *start_pts = parse_timestamp(start_str); + printf("Start pts: %f\n", *start_pts); + } + if (matches >= 2) { + *end_pts = parse_timestamp(end_str); + } + if ((matches == 3) && (strlen(cue_str) > 0)) { + int cue_size = strlen(cue_str); + *cue_settings = malloc(cue_size + 1); + strncpy(*cue_settings, cue_str, cue_size); + (*cue_settings)[cue_size] = '\0'; + } +} + +vtt_t* vtt_parse(const utf8_char_t* data, size_t size) +{ + return _vtt_parse(data, size, 0); +} + +vtt_t* _vtt_parse(const utf8_char_t* data, size_t size, int srt_mode) +{ + vtt_t* vtt = NULL; + double str_pts = 0, end_pts = 0; + size_t line_length = 0, trimmed_length = 0; + char* cue_settings; + enum VTT_BLOCK_TYPE block_type; + size_t cue_id_length = 0; + const utf8_char_t* cue_id = NULL; + + if (!data || !size || size < 6) { + return NULL; + } + + // TODO: Support UTF-8 BOM? + if (!srt_mode && strncmp(data, "WEBVTT", 6) != 0) { + // WebVTT files must start with WEBVTT + fprintf(stderr, "Invalid webvtt header: %.*s\n", 6, data); + return NULL; + } + + data += 6; + size -= 6; + + vtt = vtt_new(); + + for (;;) { + line_length = 0; + + do { + data += line_length; + size -= line_length; + line_length = utf8_line_length(data); // Line length + trimmed_length = utf8_trimmed_length(data, line_length); + // Skip empty lines + } while (0 < line_length && 0 == trimmed_length); + + // line length only zero at EOF + if (0 == line_length) { + break; + } + + if (strnstr(data, "REGION", line_length) != NULL) { + block_type = VTT_REGION; + } else if (strnstr(data, "STYLE", line_length) != NULL) { + block_type = VTT_STYLE; + } else if (strnstr(data, "NOTE", line_length) != NULL) { + block_type = VTT_NOTE; + } else if (strnstr(data, "-->", line_length) != NULL) { + block_type = VTT_CUE; + parse_timestamps(data, &str_pts, &end_pts, &cue_settings); + if (str_pts == -1) { + // Failed to parse timestamps + fprintf(stderr, "Bad timestamp: %.*s\n", (int)line_length, data); + return NULL; + } + } else { + if (cue_id != NULL) { + // Invalid text found + fprintf(stderr, "ERR: Unrecognized block\n"); + return NULL; + } + + cue_id = data; + cue_id_length = line_length; + + data += line_length; + size -= line_length; + continue; + } + + data += line_length; + size -= line_length; + + // Caption text starts here + const utf8_char_t* text = data; + size_t text_size = 0; + + line_length = 0; + + do { + text_size += line_length; + line_length = utf8_line_length(data); + trimmed_length = utf8_trimmed_length(data, line_length); + // printf ("cap (%d): '%.*s'\n", line_length, (int) trimmed_length, data); + data += line_length; + size -= line_length; + } while (trimmed_length); + + // should we trim here? + + vtt_block_t* block = vtt_block_new(vtt, text, text_size, block_type); + + if (block_type == VTT_CUE) { + block->timestamp = str_pts; + block->duration = end_pts - str_pts; + block->cue_settings = cue_settings; + if (cue_id != NULL) { + block->cue_id = malloc(cue_id_length + 1); + memcpy(block->cue_id, cue_id, cue_id_length); + block->cue_id[cue_id_length] = '\0'; + } + } + + cue_id = NULL; + } + + return vtt; +} + +int vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame) +{ + const char* data = vtt_block_data(cue); + return caption_frame_from_text(frame, data); +} + +vtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt) +{ + if (vtt->cue_tail && 0 >= vtt->cue_tail->duration) { + vtt->cue_tail->duration = frame->timestamp - vtt->cue_tail->timestamp; + } + + // CRLF per row, plus an extra at the end + vtt_block_t* cue = vtt_block_new(vtt, NULL, 2 + CAPTION_FRAME_TEXT_BYTES, VTT_CUE); + utf8_char_t* data = vtt_block_data(cue); + + caption_frame_to_text(frame, data); + cue->timestamp = frame->timestamp; + // vtt requires an extra new line + strcat((char*)data, "\r\n"); + return cue; +} + +static void _dump(vtt_t* vtt) +{ + vtt_block_t* block; + printf("WEBVTT\r\n\r\n"); + + block = vtt->region_head; + while (block != NULL) { + printf("REGION\r\n%s\r\n", vtt_block_data(block)); + block = block->next; + } + + block = vtt->style_head; + while (block != NULL) { + printf("STYLE\r\n%s\r\n", vtt_block_data(block)); + block = block->next; + } + + block = vtt->cue_head; + while (block != NULL) { + int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2; + vtt_crack_time(block->timestamp, &hh1, &mm1, &ss1, &ms1); + vtt_crack_time(block->timestamp + block->duration, &hh2, &mm2, &ss2, &ms2); + + if (block->cue_id != NULL) { + printf("%s\n", block->cue_id); + } + + printf("%02d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d", + hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2); + + if (block->cue_settings != NULL) { + printf(" %s", block->cue_settings); + } + + printf("\r\n%s\r\n", vtt_block_data(block)); + } +} + +void vtt_dump(vtt_t* head) { _dump(head); } diff --git a/deps/libcaption/src/xds.c b/deps/libcaption/src/xds.c index 1250dac9f..35c01d07a 100644 --- a/deps/libcaption/src/xds.c +++ b/deps/libcaption/src/xds.c @@ -1,46 +1,57 @@ /**********************************************************************************************/ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ +/* The MIT License */ /* */ -/* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file */ -/* except in compliance with the License. A copy of the License is located at */ +/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ /* */ -/* http://aws.amazon.com/apache2.0/ */ +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ /* */ -/* or in the "license" file accompanying this file. This file is distributed on an "AS IS" */ -/* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the */ -/* License for the specific language governing permissions and limitations under the License. */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ +/* THE SOFTWARE. */ /**********************************************************************************************/ // http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/CC_XDS.HTML#PR #include "xds.h" #include "caption.h" #include -void xds_init (xds_t* xds) +void xds_init(xds_t* xds) { - memset (xds,0,sizeof (xds_t)); + memset(xds, 0, sizeof(xds_t)); } -int xds_decode (xds_t* xds, uint16_t cc) +int xds_decode(xds_t* xds, uint16_t cc) { switch (xds->state) { default: case 0: - xds_init (xds); - xds->class = (cc&0x0F00) >>8; - xds->type = (cc&0x000F); + xds_init(xds); + xds->class_code = (cc & 0x0F00) >> 8; + xds->type = (cc & 0x000F); xds->state = 1; return LIBCAPTION_OK; case 1: - if (0x8F00 == (cc&0xFF00)) { - xds->checksum = (cc&0x007F); + if (0x8F00 == (cc & 0xFF00)) { + xds->checksum = (cc & 0x007F); xds->state = 0; return LIBCAPTION_READY; } if (xds->size < 32) { - xds->content[xds->size+0] = (cc&0x7F00) >>8; - xds->content[xds->size+1] = (cc&0x007F); + xds->content[xds->size + 0] = (cc & 0x7F00) >> 8; + xds->content[xds->size + 1] = (cc & 0x007F); xds->size += 2; return LIBCAPTION_OK; } diff --git a/deps/libcaption/unit_tests/eia608_test.c b/deps/libcaption/unit_tests/eia608_test.c deleted file mode 100644 index 9712107a6..000000000 --- a/deps/libcaption/unit_tests/eia608_test.c +++ /dev/null @@ -1,280 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ - -#include "eia608.h" -#include -#include -#include -#include -// all possible utf8 valies, including invalid ones -void encode_utf8ish (int32_t in, char out[7]) -{ - if (0 > in) { - out[0] = 0; - return; - } - - // 0xxxxxxx, 7 bits - if (0x80 > in) { - out[0] = in; - out[1] = 0; - return; - } - - // 110xxxxx 10xxxxxx, 11 bits - if (0x800 > in) { - out[0] = 0xC0 | ( (in >> (6*1)) & 0x1F); - out[1] = 0x80 | ( (in >> (6*0)) & 0x3F); - out[2] = 0; - return; - } - - // 1110xxxx 10xxxxxx 10xxxxxx, 16 bits - if (0x10000 > in) { - out[0] = 0xE0 | ( (in >> (6*2)) & 0x0F); - out[1] = 0x80 | ( (in >> (6*1)) & 0x3F); - out[2] = 0x80 | ( (in >> (6*0)) & 0x3F); - out[3] = 0; - return; - } - - // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx, 21 bits - if (0x200000 > in) { - out[0] = 0xF0 | ( (in >> (6*3)) & 0x07); - out[1] = 0x80 | ( (in >> (6*2)) & 0x3F); - out[2] = 0x80 | ( (in >> (6*1)) & 0x3F); - out[3] = 0x80 | ( (in >> (6*0)) & 0x3F); - out[4] = 0; - return; - } - - // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx, 26 bits - if (0x4000000 > in) { - out[0] = 0xF8 | ( (in >> (6*4)) & 0x03); - out[1] = 0x80 | ( (in >> (6*3)) & 0x3F); - out[2] = 0x80 | ( (in >> (6*2)) & 0x3F); - out[3] = 0x80 | ( (in >> (6*1)) & 0x3F); - out[4] = 0x80 | ( (in >> (6*0)) & 0x3F); - out[5] = 0; - return; - } - - // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx, 31 bits - if (0x80000000 > in) { - out[0] = 0xFC | ( (in >> (6*5)) & 0x01); - out[1] = 0x80 | ( (in >> (6*4)) & 0x3F); - out[2] = 0x80 | ( (in >> (6*3)) & 0x3F); - out[3] = 0x80 | ( (in >> (6*2)) & 0x3F); - out[4] = 0x80 | ( (in >> (6*1)) & 0x3F); - out[5] = 0x80 | ( (in >> (6*0)) & 0x3F); - out[6] = 0; - return; - } -} - -void test_all_utf8() -{ - char s[7]; size_t size, count = 0; uint16_t code1, code2; - - for (int i = 0 ; i < 0x80000000 ; ++i) { - encode_utf8ish (i, &s[0]); - code1 = eia608_from_utf8 ( (const char*) &s[0], 0, &size); - - // code2 = eia608_from_utf8 ( (const char*) &s[0], 1, &size); - if (code1) { - ++count; - printf ("%d: string: '%s' code: %04X\n",count, &s[0],code1); - } - } - - // Count must be 177 - // 176 charcters, pile we have two mapping for left quote mark -} - -#define BIN "%d%d%d%d%d%d%d%d %d%d%d%d%d%d%d%d" -#define BIND(D) ((D)>>15)&0x01, ((D)>>14)&0x01,((D)>>13)&0x01,((D)>>12)&0x01,((D)>>11)&0x01,((D)>>10)&0x01,((D)>>9)&0x01,((D)>>8)&0x01,((D)>>7)&0x01,((D)>>6)&0x01,((D)>>5)&0x01,((D)>>4)&0x01,((D)>>3)&0x01,((D)>>2)&0x01,((D)>>1)&0x01,((D)>>0)&0x01 - - -void print_bin (int n) -{ - int mask = 0x80; - - for (int mask = 0x80 ; mask ; mask >>= 1) { - printf ("%s", n & mask ? "1" : "0"); - } - - printf ("\n"); -} - -void void_test_all_possible_code_words() -{ - for (int i = 0 ; i <= 0x3FFF ; ++i) { - int16_t code = eia608_parity ( ( (i<<1) &0x7F00) | (i&0x7F)); - - int count =eia608_cc_data_is_extended_data_service (code)+ - eia608_cc_data_is_basic_north_american_character (code) + - eia608_cc_data_is_special_north_american_character (code) + - eia608_cc_data_is_extended_western_european_character (code) + - eia608_cc_data_is_nonwestern_norpak_character (code) + - eia608_cc_data_is_row_preamble (code) + - eia608_cc_data_is_control_command (code); - - if (1 < count) { - printf ("code 0x%04X matched >1\n",code&0x7F7F); - } - - // if (0 == count) { - // printf ("code 0x%04X not matched %d\n",eia608_strip_parity_bits (code), i); - // } - } -} - -void print_charmap() -{ - for (int i = 0 ; i < EIA608_CHAR_COUNT ; ++i) { - printf ("%s", eia608_char_map[i]); - } - - printf ("\n"); -} - -void dance() -{ - for (int i = 0 ; i < 100 ; ++i) { - const char* l = 0 == rand() % 2 ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT; - const char* r = 0 == rand() % 2 ? EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT : EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT; - printf ("%s %s%s%s%s%s%s%s %s ", EIA608_CHAR_EIGHTH_NOTE, l, EIA608_CHAR_LEFT_PARENTHESIS, EIA608_CHAR_EM_DASH, EIA608_CHAR_LOW_LINE, EIA608_CHAR_EM_DASH, - EIA608_CHAR_RIGHT_PARENTHESIS, r, EIA608_CHAR_EIGHTH_NOTE); - } - -} - - -int main (int argc, const char** arg) -{ - // print_charmap(); - // // return 0; - // srand (time (0)); - // // test_all_utf8(); - // // void_test_all_possible_code_words(); - // // return 0; - // // print_charmap(); - // dance(); - // return 0; - for (int i = 0 ; i <= 0x3FFF ; ++i) { - uint16_t code1 = eia608_parity ( ( (i<<1) &0x7F00) | (i&0x7F)); - - switch (eia608_cc_data_type (code1)) { - default: - case EIA608_CC_DATA_UNKNOWN: - // printf ("Unknown code %04X\n",code); - break; - - case EIA608_CC_DATA_CONTROL_COMMAND: { - int cc; - eia608_control_t cmd = eia608_parse_control (code1, &cc); - uint16_t code2 = eia608_control_command (cmd,cc); - - if (code1 != code2) { - printf (BIN " != " BIN " (0x%04x != 0x%04x) cc: %d\n", BIND (code1), BIND (code2),code1,code2,cc); - } - } break; - - - case EIA608_CC_DATA_BASIC_NORTH_AMERICAN_CHARACTER: { - char char1[5], char2[5]; int chan; size_t size; - - if (eia608_to_utf8 (code1, &chan, &char1[0], &char2[0])) { - uint16_t code2 = eia608_from_utf8_2 (&char1[0], &char2[0]); - - // if the second char is invalid, mask it off, we will accept the first - if (0x80 < (code1 &0x007F) || 0x20 > (code1 &0x007F)) { - code1 = (code1&0xFF00) |0x0080; - } - - if (code1 == code2) { - // printf ("%s " BIN " == " BIN " (0x%04x == 0x%04x)\n", &char1[0], BIND (code1), BIND (code2),code1,code2); - } else { - printf ("%s %s " BIN " != " BIN " (0x%04x != 0x%04x)\n", &char1[0], &char2[0], BIND (code1), BIND (code2),code1,code2); - } - } - - } break; - - case EIA608_CC_DATA_SPECIAL_NORTH_AMERICAN_CHARACTER: - case EIA608_CC_DATA_EXTENDED_WESTERN_EUROPEAN_CHARACTER: { - char char1[5], char2[5]; int chan; size_t size; - - if (eia608_to_utf8 (code1, &chan, &char1[0], &char2[0])) { - uint16_t code2 = eia608_from_utf8 (&char1[0], chan, &size); - - if (code1 == code2) { - // printf ("%s " BIN " == " BIN " (0x%04x == 0x%04x)\n", &char1[0], BIND (code1), BIND (code2),code1,code2); - } else { - printf ("%s " BIN " != " BIN " (0x%04x != 0x%04x)\n", &char1[0], BIND (code1), BIND (code2),code1,code2); - } - } - } break; - - // #define EIA608_CODE_ROW_PREAMBLE 4 - // #define EIA608_CODE_EXTENDED_DATA_SERVICE 5 - // #define EIA608_CODE_CONTROL_COMMAND 6 - } - } - - return 0; -} - - - - -// for (uint16_t i = 0 ; i < 0x4000; ++i) { -// int chan; -// char str[7]; -// uint16_t code = ( (i<<1) &0x7F00) | (i & 0x007F); -// -// if (eia608_to_utf8 (code,&chan,str)) { -// printf ("code: 0x%04X str: '%s'\n", code,str); -// } -// } -// -// // for(int i = 0 ; i < cie608_char_count ; ++i) -// // { -// // cie608_char_map[i] -// // -// // } -// -// -// for (int i = 0 ; i < 128 ; ++i) { -// // print_bin( B7( i ) ); -// // print_bin( eia608_parity_table[i] ); -// printf ("%d %d %d\n", i, 0x7F & eia608_parity_table[i], eia608_parity_table[i]); -// // if ( i != eia608_parity_table[i] ) -// // { -// // printf( "ERROR\n" ); -// // -// // } -// } -// -// } diff --git a/deps/libcaption/unit_tests/test_sei.c b/deps/libcaption/unit_tests/test_sei.c deleted file mode 100644 index 0e321b3b8..000000000 --- a/deps/libcaption/unit_tests/test_sei.c +++ /dev/null @@ -1,148 +0,0 @@ -/**********************************************************************************************/ -/* The MIT License */ -/* */ -/* Copyright 2016-2016 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining a copy */ -/* of this software and associated documentation files (the "Software"), to deal */ -/* in the Software without restriction, including without limitation the rights */ -/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ -/* copies of the Software, and to permit persons to whom the Software is */ -/* furnished to do so, subject to the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be included in */ -/* all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ -/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ -/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ -/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ -/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ -/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */ -/* THE SOFTWARE. */ -/**********************************************************************************************/ - -#include "avcsei.h" -#include -#include - -uint8_t sei1[] = { 0x06, 0x04, 0x68, 0xB5, 0x00, 0x31, 0x47, 0x41, 0x39, 0x34, 0x03, 0xDF, 0xFF, 0xFC, 0xEC, 0xE5, - 0xFC, 0xAE, 0x80, 0xFC, 0x94, 0x52, 0xFC, 0x97, 0xA1, 0xFC, 0x2A, 0x20, 0xFC, 0xDC, 0x20, 0xFC, - 0x5E, 0x20, 0xFC, 0xDF, 0x20, 0xFC, 0xE0, 0x20, 0xFC, 0x91, 0x38, 0xFC, 0x20, 0x80, 0xFC, 0x91, - 0xBA, 0xFC, 0x20, 0xE9, 0xFC, 0x13, 0xA4, 0xFC, 0x20, 0xEF, 0xFC, 0x13, 0x26, 0xFC, 0x20, 0x75, - 0xFC, 0x92, 0xBC, 0xFC, 0x94, 0xF2, 0xFC, 0x97, 0xA1, 0xFC, 0x61, 0x80, 0xFC, 0x13, 0x31, 0xFC, - 0x20, 0xE5, 0xFC, 0x92, 0xB6, 0xFC, 0x20, 0xE9, 0xFC, 0x92, 0xB9, 0xFC, 0x20, 0xEF, 0xFC, 0x13, - 0xB3, 0xFC, 0x20, 0x75, 0xFC, 0x92, 0x25, 0xFC, 0x20, 0x80, 0xFF, 0x80, - }; - -uint8_t sei2[] = { 0x06, 0x04, 0x35, 0xB5, 0x00, 0x31, 0x47, 0x41, 0x39, 0x34, 0x03, 0xCE, 0xFF, 0xFC, 0x94, 0x26, - 0xFC, 0x94, 0xAD, 0xFC, 0x94, 0xF2, 0xFC, 0x43, 0xC1, 0xFC, 0xD0, 0x54, 0xFC, 0x49, 0x4F, 0xFC, - 0xCE, 0x20, 0xFC, 0x4C, 0x49, 0xFC, 0xCE, 0x45, 0xFC, 0xD3, 0x20, 0xFC, 0x52, 0x4F, 0xFC, 0x4C, - 0x4C, 0xFC, 0x20, 0xD5, 0xFC, 0xD0, 0x80, 0xFF, 0x80, - }; - - -uint8_t sei3[] = { 0x06, - 0x04, 0x68, 0xB5, 0x00, 0x31, 0x47, 0x41, 0x39, 0x34, 0x03, 0xDF, 0xFF, 0xFC, 0xEC, 0xE5, 0xFC, - 0xAE, 0x80, 0xFC, 0x94, 0x52, 0xFC, 0x97, 0xA1, 0x00, 0x00, 0x03, 0x00, 0xFC, 0xDC, 0x20, 0xFC, - 0x5E, 0x20, 0xFC, 0xDF, 0x20, 0xFC, 0xE0, 0x20, 0xFC, 0x91, 0x38, 0xFC, 0x20, 0x80, 0xFC, 0x91, - 0xBA, 0xFC, 0x20, 0xE9, 0xFC, 0x13, 0xA4, 0xFC, 0x20, 0xEF, 0xFC, 0x13, 0x26, 0xFC, 0x20, 0x75, - 0xFC, 0x92, 0xBC, 0xFC, 0x94, 0xF2, 0xFC, 0x97, 0xA1, 0xFC, 0x61, 0x80, 0xFC, 0x13, 0x31, 0xFC, - 0x20, 0xE5, 0xFC, 0x92, 0xB6, 0xFC, 0x20, 0xE9, 0xFC, 0x92, 0xB9, 0xFC, 0x20, 0xEF, 0xFC, 0x13, - 0xB3, 0xFC, 0x20, 0x75, 0xFC, 0x92, 0x25, 0xFC, 0x20, 0x80, 0xFF, - - 0x04, 0x68, 0xB5, 0x00, 0x31, 0x47, 0x41, 0x39, 0x34, 0x03, 0xDF, 0xFF, 0xFC, 0xEC, 0xE5, - 0xFC, 0xAE, 0x80, 0xFC, 0x94, 0x52, 0xFC, 0x97, 0xA1, 0x00, 0x00, 0x03, 0x00, 0xFC, 0xDC, 0x20, - 0xFC, 0x5E, 0x20, 0xFC, 0xDF, 0x20, 0xFC, 0xE0, 0x20, 0xFC, 0x91, 0x38, 0xFC, 0x20, 0x80, 0xFC, - 0x91, 0xBA, 0xFC, 0x20, 0xE9, 0xFC, 0x13, 0xA4, 0xFC, 0x20, 0xEF, 0xFC, 0x13, 0x26, 0xFC, 0x20, - 0x75, 0xFC, 0x92, 0xBC, 0xFC, 0x94, 0xF2, 0xFC, 0x97, 0xA1, 0xFC, 0x61, 0x80, 0xFC, 0x13, 0x31, - 0xFC, 0x20, 0xE5, 0xFC, 0x92, 0xB6, 0xFC, 0x20, 0xE9, 0xFC, 0x92, 0xB9, 0xFC, 0x20, 0xEF, 0xFC, - 0x13, 0xB3, 0xFC, 0x20, 0x75, 0xFC, 0x92, 0x25, 0xFC, 0x20, 0x80, 0xFF, - 0x80, - }; - - -// TODO make SEI with multiple messages -// TODO make SEI with emupation prevention - - -uint8_t* read_file (const char* file, size_t* size) -{ - FILE* f = fopen (file, "rb"); - - if (! f) { - return 0; - } - - fseek (f,0,SEEK_END); - (*size) = ftell (f); - fseek (f,0,SEEK_SET); - uint8_t* data = (uint8_t*) malloc (*size); - - for (int i = 0 ; i < (*size) ;) { - i += fread (&data[i], 1, *size, f); - } - - fclose (f); - return data; -} - -int test_emulation_byte() -{ - avcsei_t sei; - avcsei_init (&sei); - avcsei_parse (&sei,sei3,sizeof (sei3)); - avcsei_dump (&sei); - avcsei_free (&sei); -} - - -int main (int argc, const char** argv) -{ - // test_emulation_byte(); - // return 0; - - size_t size; - avcsei_t sei; - cea708_t cea708; - eia608_screen_t screen; - char screen_buf[EIA608_SCREEN_DUMP_BUF_SIZE]; - char json_buf[EIA608_SCREEN_JSON_BUF_SIZE]; - - // uint8_t* data = - for (int i = 1 ; i < argc ; ++i) { - avcsei_init (&sei); - eia608_screen_init (&screen); - uint8_t* data = read_file (argv[i],&size); - avcsei_parse (&sei,data,size); - free (data); - - // avcsei_parse (&sei,sei1,sizeof (sei1)); - - for (avcsei_message_t* msg = avcsei_message_head (&sei) ; msg ; msg = avcsei_message_next (msg)) { - if (sei_type_user_data_registered_itu_t_t35 == avcsei_message_type (msg)) { - // avcsei_dump (&sei); - avcsei_decode_cea708 (msg,&cea708); - int count = cea708_cc_count (&cea708.user_data); - - for (int i = 0 ; i < count ; ++i) { - cea708_cc_type_t type; int valid; - uint16_t cc_data = cea708_cc_data (&cea708.user_data, i, &valid, &type); - - if (valid && (cc_type_ntsc_cc_field_1 == type || cc_type_ntsc_cc_field_2 == type)) { - eia608_screen_decode (&screen,cc_data); - } - } - - // eia608_screen_dump (&screen, &screen_buf[0]); - // printf ("screen:\n%s\n",&screen_buf[0]); - - eia608_screen_json (&screen, &json_buf[0]); - printf ("json:\n%s\n",&json_buf[0]); - - } - } - - avcsei_free (&sei); - } -} diff --git a/deps/libcaption/unit_tests/tos.scc b/deps/libcaption/unit_tests/tos.scc deleted file mode 100644 index 70ce99df3..000000000 --- a/deps/libcaption/unit_tests/tos.scc +++ /dev/null @@ -1,154 +0,0 @@ -Scenarist_SCC V1.0 - -00:00:22:10 9420 94f2 97a2 d9ef 75a7 f2e5 2061 20ea e5f2 6b2c 2054 68ef 6dae 942c 8080 8080 942f - -00:00:23:28 9420 947c 97a2 4cef ef6b 2043 e5ec e961 2c20 f7e5 2068 6176 e520 f4ef 20e6 efec ecef f720 ef75 f220 7061 7373 e9ef 6e73 3b80 942c 8080 8080 942f - -00:00:25:16 9420 9440 aeae ae79 ef75 2068 6176 e520 79ef 75f2 20f2 ef62 eff4 e9e3 732c 2061 6e64 2049 94fe 9723 ea75 73f4 20f7 616e f420 f4ef 2062 e520 61f7 e573 ef6d e520 e96e 2073 7061 e3e5 ae80 942c 8080 8080 942f - -00:00:29:09 9420 9440 97a1 5768 7920 64ef 6ea7 f420 79ef 7520 ea75 73f4 2061 646d e9f4 20f4 6861 f480 94fe 97a2 79ef 75a7 f2e5 20e6 f2e5 616b e564 20ef 75f4 2062 7920 6d79 20f2 ef62 eff4 2068 616e 64bf 942c 8080 8080 942f - -00:00:33:19 9420 94e0 49a7 6d20 6eef f420 e6f2 e561 6be5 6420 ef75 f420 6279 ad20 e9f4 a773 aeae ae80 942c 8080 8080 942f - -00:00:36:10 9420 94f2 9723 aeae ae61 ecf2 e967 68f4 a120 46e9 6ee5 a180 942c 8080 8080 942f - -00:00:36:29 9420 945e 9723 49a7 6d20 e6f2 e561 6be5 6420 ef75 f4a1 2049 2068 6176 e520 6ee9 6768 f46d 61f2 e573 94f2 f468 61f4 2049 a76d 2062 e5e9 6e67 20e3 6861 73e5 64ae aeae 942c 8080 8080 942f - -00:00:39:27 9420 947c 97a2 aeae ae62 7920 f468 e573 e520 67e9 616e f420 f2ef 62ef f4e9 e320 e3ec 61f7 7320 efe6 2064 e561 f468 aeae ae80 942c 8080 8080 942f - -00:00:40:29 9420 9452 97a2 a246 ef75 f2f4 7920 79e5 61f2 7320 ec61 f4e5 f2a2 94e0 97a2 5768 61f4 e576 e5f2 2c20 5468 ef6d ae20 57e5 a7f2 e520 64ef 6ee5 ae80 942c 8080 8080 942f - -00:00:49:02 9420 94fe 9723 52ef 62ef f4a7 7320 6de5 6def f279 2073 796e e3e5 6420 616e 6420 ecef e36b e564 a180 942c 8080 8080 942f - -00:01:00:08 9420 94f2 97a1 5468 e973 20e9 7320 70f2 e5f4 f479 20e6 f2e5 616b 79ae 942c 8080 8080 942f - -00:01:56:03 9420 94e0 97a2 d368 ef75 ec64 6ea7 f420 79ef 7520 62e5 2064 eff7 6e20 f468 e5f2 e5bf 942c 8080 8080 942f - -00:02:09:29 9420 94fe 97a2 4920 68e5 61f2 6420 79ef 7520 6775 7973 20f4 61ec 6be9 6e67 20ec 6173 f420 6ee9 6768 f4ae 942c 8080 8080 942f - -00:02:15:02 9420 94e0 97a2 49f4 a773 206e eff4 206d 7920 e661 75ec f42c 2079 ef75 206b 6eef f7ae 942c 8080 8080 942f - -00:03:10:08 9420 94f4 97a1 c1f2 e520 79ef 7520 f2e5 6164 79bf 942c 8080 8080 942f - -00:03:11:24 9420 947c 9723 4fe6 20e3 ef75 f273 e520 79ef 75a7 f2e5 20f2 e561 6479 2c20 79ef 75a7 f2e5 2061 20f2 efe3 6b73 f461 f2ae 942c 8080 8080 942f - -00:03:16:02 9420 94e0 9723 c8ef f7a7 7320 e9f4 20ec efef 6be9 6e67 2c20 c261 f2ec e579 bf80 942c 8080 8080 942f - -00:03:17:27 9420 94fe 97a2 57e5 2073 68ef 75ec 6420 6861 76e5 2061 62ef 75f4 20f4 e56e 206d e96e 75f4 e573 aeae ae80 942c 8080 8080 942f - -00:03:20:19 9420 94f2 97a2 57e5 ecec 20f4 6861 f4a7 7320 70e5 f2e6 e5e3 f4ae 942c 8080 8080 942f - -00:03:22:05 9420 9440 57e5 a7f2 e520 ef6e 20e9 6e20 ef6e e5a1 20c1 ecec 2073 7973 f4e5 6d73 2067 efa1 94f4 97a1 d9e5 6168 2079 ef75 2c20 67ef a180 942c 8080 8080 942f - -00:03:26:29 9420 94e0 97a1 c7ef a120 cdef 76e5 2079 ef75 f220 6173 73e5 73a1 20c7 ef20 67ef 2067 efa1 942c 8080 8080 942f - -00:03:32:03 9420 94f2 4920 ecef 76e5 20e9 f4a1 2043 ef6d e520 ef6e 2c20 67ef a180 942c 8080 8080 942f - -00:03:42:08 9420 94f4 97a2 5468 61f4 a773 206e e9e3 e5ae 942c 8080 8080 942f - -00:03:43:18 9420 94f2 ceef f468 e96e 6720 f4ef 20f7 eff2 f279 2061 62ef 75f4 ae80 942c 8080 8080 942f - -00:03:45:11 9420 9476 97a1 5468 ef6d ae80 942c 8080 8080 942f - -00:03:50:07 9420 94f4 97a1 5468 e5f2 e520 7368 e520 e973 ae80 942c 8080 8080 942f - -00:03:52:27 9420 9476 97a2 ceef f7ae 942c 8080 8080 942f - -00:03:54:06 9420 94f4 97a1 d9ef 7520 ecef 76e5 2068 e5f2 ae80 942c 8080 8080 942f - -00:03:56:03 9420 94f2 97a2 d368 e520 e973 2079 ef75 f220 7061 7373 e9ef 6ea1 942c 8080 8080 942f - -00:03:59:04 9420 94f2 9723 c2e5 20f4 e56e 64e5 f220 f4ef 2068 e5f2 ae80 942c 8080 8080 942f - -00:04:00:15 9420 94e0 9723 c2e5 2068 ef6e e573 f4a1 2045 68ad 2062 e520 f4e5 6e64 e5f2 ae80 942c 8080 8080 942f - -00:04:04:02 9420 94f2 52e5 6de9 6e64 2068 e5f2 20f7 6861 f420 ecef 76e5 20e9 73ae 942c 8080 8080 942f - -00:04:51:05 9420 94f4 aeae ae61 6e64 2c20 61e3 f4e9 ef6e a180 942c 8080 8080 942f - -00:04:55:12 9420 94e0 97a1 cde5 6def f279 20ef 76e5 f2f7 f2e9 f4e5 20e9 6e20 70f2 ef67 f2e5 7373 a180 942c 8080 8080 942f - -00:04:58:02 9420 94f2 97a2 d9ef 75a7 f2e5 2061 20ea e5f2 6b2c 2054 68ef 6da1 942c 8080 8080 942f - -00:05:01:17 9420 94f2 9723 4f68 70ad 20d3 eff2 f279 a120 d3ef f2f2 79ae 942c 8080 8080 942f - -00:05:15:05 9420 94f4 97a2 4cef ef6b 2043 e5ec e961 ae80 942c 8080 8080 942f - -00:05:16:26 9420 94e0 57e5 2068 6176 e520 f4ef 20e6 efec ecef f720 ef75 f220 7061 7373 e9ef 6e73 ae80 942c 8080 8080 942f - -00:05:19:28 9420 94e0 9723 d9ef 7520 6861 76e5 2079 ef75 f220 f2ef 62ef f4e9 e373 aeae ae80 942c 8080 8080 942f - -00:05:23:20 9420 947c 9723 aeae ae61 6e64 2049 20ea 7573 f420 f761 6ef4 20f4 ef20 62e5 2061 f7e5 73ef 6de5 20e9 6e20 7370 61e3 e5ae 942c 8080 8080 942f - -00:05:37:21 9420 94fe 4f6b 6179 2c20 f468 e579 a7f2 e520 e3ef 6de9 6e67 ae20 54f7 ef20 6de9 6e75 f4e5 7320 ece5 e6f4 a180 942c 8080 8080 942f - -00:05:42:01 9420 94f2 9723 d370 e5e5 6420 e9f4 2075 702c 2054 68ef 6da1 942c 8080 8080 942f - -00:05:44:04 9420 94f4 97a2 d6e9 7661 e3e9 7373 e96d efa1 942c 8080 8080 942f - -00:05:45:05 9420 945e 97a2 5768 7920 64ef 6ea7 f420 79ef 7520 ea75 73f4 2061 646d e9f4 20f4 6861 f420 79ef 75a7 f2e5 94e0 97a1 e6f2 e561 6be5 6420 ef75 f420 6279 206d 7920 f2ef 62ef f420 6861 6e64 bf80 942c 8080 8080 942f - -00:05:54:26 9420 94e0 97a2 4ce9 73f4 e56e 2043 e5ec e961 2c20 4920 f761 7320 79ef 756e 67ae aeae 942c 8080 8080 942f - -00:05:58:18 9420 94f4 97a1 aeae ae61 6e64 2061 2064 e9e3 6bae 942c 8080 8080 942f - -00:05:59:18 9420 9452 c275 f420 f468 61f4 a773 206e ef20 f2e5 6173 ef6e 20f4 ef80 94f2 9723 64e5 73f4 f2ef 7920 f468 e520 f7ef f2ec 64ae 942c 8080 8080 942f - -00:06:04:00 9420 94f2 97a2 5768 7920 64ef e573 2068 e520 64ef 20f4 68e9 73bf 942c 8080 8080 942f - -00:06:05:12 9420 94e0 9723 57e5 2061 ecf2 e561 6479 20f4 f2e9 e564 20f4 6861 f420 ef6e e5a1 942c 8080 8080 942f - -00:06:10:21 9420 9476 97a1 c162 eff2 f4a1 942c 8080 8080 942f - -00:06:12:07 9420 9476 97a2 4375 f4a1 942c 8080 8080 942f - -00:06:13:06 9420 9476 5768 ef61 6161 a180 942c 8080 8080 942f - -00:06:18:06 9420 9476 5768 ef61 6161 a180 942c 8080 8080 942f - -00:06:20:06 9420 9476 ceef efef efef a180 942c 8080 8080 942f - -00:06:21:29 9420 94f2 97a2 d9ef 7520 62f2 ef6b e520 6d79 2068 e561 f2f4 ae80 942c 8080 8080 942f - -00:06:25:06 9420 9476 4920 6b6e eff7 ae80 942c 8080 8080 942f - -00:06:25:28 9420 94f2 d9ef 7520 e6ef f267 eff4 206d e520 ef6e 20e5 61f2 f468 ae80 942c 8080 8080 942f - -00:06:28:06 9420 9476 4920 6b6e eff7 ae80 942c 8080 8080 942f - -00:06:29:13 9420 94f2 4920 7368 ef75 ec64 20ea 7573 f420 e3f2 7573 6820 79ef 75ae 942c 8080 8080 942f - -00:06:35:07 9420 9476 97a2 49a7 6dad 942c 8080 8080 942f - -00:06:46:03 9420 94f4 9723 49a7 6d20 73ef f2f2 79ae 942c 8080 8080 942f - -00:06:49:02 9420 94f4 97a2 c7ef ef64 2061 64ad ece9 62ae 942c 8080 8080 942f - -00:06:58:01 9420 94f4 97a1 ceef f420 6d79 20e6 6175 ecf4 a180 942c 8080 8080 942f - -00:07:01:03 9420 94f4 9723 5468 e973 20f4 e96d e5ae 942c 8080 8080 942f - -00:07:07:01 9420 94f4 97a1 5175 e9e5 f420 ef6e 2073 e5f4 a180 942c 8080 8080 942f - -00:07:08:29 9420 94f2 9723 57e5 a7f2 e520 ef75 f420 efe6 20f4 e96d e5a1 942c 8080 8080 942f - -00:08:12:03 9420 9476 43ef 6de5 20ef 6ea1 942c 8080 8080 942f - -00:08:20:29 9420 94f4 97a2 52c1 c1c1 c1c1 c1c1 c1c8 a180 942c 8080 8080 942f - -00:08:22:24 9420 94f2 97a1 cde5 6def f279 20ef 76e5 f2f7 f2e9 f4e5 2c20 b9b0 25ae 942c 8080 8080 942f - -00:08:24:22 9420 94e0 9723 4361 70f4 61e9 6ea1 2057 e520 6861 76e5 20f4 ef20 6162 eff2 f4a1 942c 8080 8080 942f - -00:08:36:20 9420 94e0 97a1 5468 e520 f7ef f2ec 64a7 7320 e368 616e 67e5 642c 2043 e5ec e961 aeae ae80 942c 8080 8080 942f - -00:08:52:24 9420 94f2 97a2 aeae ae6d 6179 62e5 20f7 e520 e361 6e20 f4ef efae 942c 8080 8080 942f - -00:08:56:21 9420 94e0 9723 cde5 6def f279 20ef 76e5 f2f7 f2e9 f4e5 20e3 ef6d 70ec e5f4 e5a1 942c 8080 8080 942f - -00:09:17:29 9420 94f4 9723 d9ef 7520 6b6e eff7 ae80 942c 8080 8080 942f - -00:09:20:13 9420 947c 9723 5468 e5f2 e5a7 7320 6120 ece5 7373 ef6e 20f4 ef20 62e5 20ec e561 f26e e564 20e6 f2ef 6d20 f468 e973 ae80 942c 8080 8080 942f - -00:09:24:24 9420 94f2 97a2 43ef 75ec 64a7 6120 67ef 6ee5 20f7 eff2 73e5 ae80 942c 8080 8080 942f - diff --git a/deps/lzma/CMakeLists.txt b/deps/lzma/CMakeLists.txt index cb84fe7f9..6908cd23e 100644 --- a/deps/lzma/CMakeLists.txt +++ b/deps/lzma/CMakeLists.txt @@ -30,7 +30,7 @@ add_definitions( if(WIN32) if(MSVC) - add_compile_options("$<$:/MT>") + add_compile_options($,/MTd,/MT> /Zl) add_compile_options("/wd4244") add_compile_options("/wd4267") endif() diff --git a/deps/obs-scripting/CMakeLists.txt b/deps/obs-scripting/CMakeLists.txt index 835c1b305..31087398e 100644 --- a/deps/obs-scripting/CMakeLists.txt +++ b/deps/obs-scripting/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 2.8) if(NOT ENABLE_SCRIPTING) + message(STATUS "Scripting plugin disabled") return() endif() @@ -11,13 +12,54 @@ if(MSVC) w32-pthreads) endif() -find_package(Luajit QUIET) -find_package(PythonDeps QUIET) -find_package(SwigDeps QUIET 2) +option(DISABLE_LUA "Disable Lua scripting support" OFF) +option(DISABLE_PYTHON "Disable Python scripting support" OFF) set(COMPILE_PYTHON FALSE CACHE BOOL "" FORCE) set(COMPILE_LUA FALSE CACHE BOOL "" FORCE) +if(NOT DISABLE_LUA) + find_package(Luajit QUIET) + + if(NOT DISABLE_LUA AND NOT LUAJIT_FOUND) + message(STATUS "Luajit support not found.") + set(LUAJIT_FOUND FALSE) + else() + message(STATUS "Scripting: Luajit supported") + set(COMPILE_LUA TRUE CACHE BOOL "" FORCE) + endif() +else() + message(STATUS "Scripting: Luajit support disabled") + set(LUAJIT_FOUND FALSE) +endif() + +if(NOT DISABLE_PYTHON) + find_package(PythonDeps QUIET) + + if(NOT DISABLE_PYTHON AND NOT PYTHONLIBS_FOUND) + message(STATUS "Python support not found.") + set(PYTHON_FOUND FALSE) + set(PYTHONLIBS_FOUND FALSE) + else() + message(STATUS "Scripting: Python 3 supported") + set(PYTHON_FOUND TRUE) + set(COMPILE_PYTHON TRUE CACHE BOOL "" FORCE) + + get_filename_component(PYTHON_LIB "${PYTHON_LIBRARIES}" NAME) + string(REGEX REPLACE "\\.[^.]*$" "" PYTHON_LIB ${PYTHON_LIB}) + + if(WIN32) + string(REGEX REPLACE "_d" "" PYTHON_LIB "${PYTHON_LIB}") + endif() + endif() +else() + message(STATUS "Scripting: Python 3 support disabled") + set(PYTHON_FOUND FALSE) + set(PYTHONLIBS_FOUND FALSE) +endif() + +find_package(SwigDeps QUIET 2) + if(NOT SWIG_FOUND) message(STATUS "Scripting: SWIG not found; scripting disabled") return() @@ -28,29 +70,6 @@ if(NOT PYTHONLIBS_FOUND AND NOT LUAJIT_FOUND) return() endif() -if(NOT LUAJIT_FOUND) - message(STATUS "Scripting: Luajit not found; Luajit support disabled") -else() - message(STATUS "Scripting: Luajit supported") - set(COMPILE_LUA TRUE CACHE BOOL "" FORCE) -endif() - -if(NOT PYTHONLIBS_FOUND) - message(STATUS "Scripting: Python 3 not found; Python support disabled") - set(PYTHON_FOUND FALSE) - set(PYTHONLIBS_FOUND FALSE) -else() - message(STATUS "Scripting: Python 3 supported") - set(PYTHON_FOUND TRUE) - set(COMPILE_PYTHON TRUE CACHE BOOL "" FORCE) - - get_filename_component(PYTHON_LIB "${PYTHON_LIBRARIES}" NAME) - string(REGEX REPLACE "\\.[^.]*$" "" PYTHON_LIB ${PYTHON_LIB}) - if(WIN32) - string(REGEX REPLACE "_d" "" PYTHON_LIB "${PYTHON_LIB}") - endif() -endif() - set(SCRIPTING_ENABLED ON CACHE BOOL "Interal global cmake variable" FORCE) if(UI_ENABLED) diff --git a/deps/obs-scripting/obs-scripting-lua.c b/deps/obs-scripting/obs-scripting-lua.c index 965e7e132..5743e0f94 100644 --- a/deps/obs-scripting/obs-scripting-lua.c +++ b/deps/obs-scripting/obs-scripting-lua.c @@ -377,7 +377,7 @@ static void obs_lua_tick_callback(void *priv, float seconds) lock_callback(); lua_pushnumber(script, (lua_Number)seconds); - call_func(obs_lua_tick_callback, 2, 0); + call_func(obs_lua_tick_callback, 1, 0); unlock_callback(); } @@ -579,6 +579,32 @@ static int enum_sources(lua_State *script) return 1; } +/* -------------------------------------------- */ + +static bool source_enum_filters_proc(obs_source_t *source, obs_source_t *filter, void *param) +{ + lua_State *script = param; + + obs_source_get_ref(filter); + ls_push_libobs_obj(obs_source_t, filter, false); + + size_t idx = lua_rawlen(script, -2); + lua_rawseti(script, -2, (int)idx + 1); + return true; +} + +static int source_enum_filters(lua_State *script) +{ + obs_source_t *source; + if (!ls_get_libobs_obj(obs_source_t, 1, &source)) + return 0; + + lua_newtable(script); + obs_source_enum_filters(source, source_enum_filters_proc, script); + return 1; +} + + /* -------------------------------------------- */ static bool enum_items_proc(obs_scene_t *scene, obs_sceneitem_t *item, @@ -972,6 +998,7 @@ static void add_hook_functions(lua_State *script) add_func("timer_remove", timer_remove); add_func("timer_add", timer_add); add_func("obs_enum_sources", enum_sources); + add_func("obs_source_enum_filters", source_enum_filters); add_func("obs_scene_enum_items", scene_enum_items); add_func("source_list_release", source_list_release); add_func("sceneitem_list_release", sceneitem_list_release); diff --git a/deps/obs-scripting/obs-scripting-python.c b/deps/obs-scripting/obs-scripting-python.c index 766aa92af..fc71d06d6 100644 --- a/deps/obs-scripting/obs-scripting-python.c +++ b/deps/obs-scripting/obs-scripting-python.c @@ -1595,6 +1595,8 @@ void obs_python_load(void) extern void add_python_frontend_funcs(PyObject *module); +static bool python_loaded_at_all = false; + bool obs_scripting_load_python(const char *python_path) { if (python_loaded) @@ -1696,6 +1698,8 @@ bool obs_scripting_load_python(const char *python_path) obs_python_unload(); } + python_loaded_at_all = success; + if (python_loaded) obs_add_tick_callback(python_tick, NULL); @@ -1704,6 +1708,9 @@ bool obs_scripting_load_python(const char *python_path) void obs_python_unload(void) { + if (!python_loaded_at_all) + return; + if (python_loaded && Py_IsInitialized()) { PyGILState_Ensure(); @@ -1722,4 +1729,6 @@ void obs_python_unload(void) pthread_mutex_destroy(&tick_mutex); pthread_mutex_destroy(&timer_mutex); dstr_free(&cur_py_log_chunk); + + python_loaded_at_all = false; } diff --git a/deps/obs-scripting/obs-scripting.c b/deps/obs-scripting/obs-scripting.c index d9fc06800..bed24fb2e 100644 --- a/deps/obs-scripting/obs-scripting.c +++ b/deps/obs-scripting/obs-scripting.c @@ -213,6 +213,8 @@ void obs_scripting_unload(void) pthread_mutex_destroy(&defer_call_mutex); os_sem_destroy(defer_call_semaphore); + + scripting_loaded = false; } const char **obs_scripting_supported_formats(void) diff --git a/deps/obs-scripting/obslua/obslua.i b/deps/obs-scripting/obslua/obslua.i index b71f2acb4..98058b8a3 100644 --- a/deps/obs-scripting/obslua/obslua.i +++ b/deps/obs-scripting/obslua/obslua.i @@ -59,6 +59,7 @@ static inline void wrap_blog(int log_level, const char *message) %ignore obs_add_main_render_callback; %ignore obs_remove_main_render_callback; %ignore obs_enum_sources; +%ignore obs_source_enum_filters; %ignore obs_properties_add_button; %ignore obs_property_set_modified_callback; %ignore signal_handler_connect; diff --git a/docs/sphinx/Makefile b/docs/sphinx/Makefile new file mode 100644 index 000000000..6a2b50199 --- /dev/null +++ b/docs/sphinx/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = OBSStudio +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/plugins/obs-outputs/ftl-sdk/ftl_app/cavlc.c b/docs/sphinx/_build/.gitignore similarity index 100% rename from plugins/obs-outputs/ftl-sdk/ftl_app/cavlc.c rename to docs/sphinx/_build/.gitignore diff --git a/plugins/obs-outputs/ftl-sdk/ftl_app/cavlc.h b/docs/sphinx/_static/.gitignore similarity index 100% rename from plugins/obs-outputs/ftl-sdk/ftl_app/cavlc.h rename to docs/sphinx/_static/.gitignore diff --git a/docs/sphinx/_templates/.gitignore b/docs/sphinx/_templates/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/docs/sphinx/backend-design.rst b/docs/sphinx/backend-design.rst new file mode 100644 index 000000000..8602bede1 --- /dev/null +++ b/docs/sphinx/backend-design.rst @@ -0,0 +1,194 @@ +OBS Studio Backend Design +========================= +The OBS Studio backend is powered by the library libobs. Libobs +provides the main pipeline, the video/audio subsystems, and the general +framework for all plugins. + + +Libobs Plugin Objects +--------------------- +Libobs is designed to be modular, where adding modules will add custom +functionality. There are four libobs objects that you can make plugins +for: + +- :ref:`plugins_sources` -- Sources are used to render video and/or + audio on stream. Things such as capturing displays/games/audio, + playing a video, showing an image, or playing audio. Sources can also + be used to implement audio and video filters. + +- :ref:`plugins_outputs` -- Outputs allow the ability to output the + currently rendering audio/video. Streaming and recording are two + common examples of outputs, but not the only types of outputs. + Outputs can receive the raw data or receive encoded data. + +- :ref:`plugins_encoders` -- Encoders are OBS-specific implementations + of video/audio encoders, which are used with outputs that use + encoders. x264, NVENC, Quicksync are examples of encoder + implementations. + +- :ref:`plugins_services` -- Services are custom implementations of + streaming services, which are used with outputs that stream. For + example, you could have a custom implementation for streaming to + Twitch, and another for YouTube to allow the ability to log in and use + their APIs to do things such as get the RTMP servers or control the + channel. + +*(Author's note: the service API is incomplete as of this writing)* + + +Libobs Threads +-------------- +There are three primary threads spawned by libobs on initialization: + +- The obs_graphics_thread_ function used exclusively for rendering in + `libobs/obs-video.c`_ + +- The video_thread_ function used exclusively for video encoding/output + in `libobs/media-io/video-io.c`_ + +- The audio_thread_ function used for all audio + processing/encoding/output in `libobs/media-io/audio-io.c`_ + +*(Author's note: obs_graphics_thread was originally named +obs_video_thread; it was renamed as of this writing to prevent confusion +with video_thread)* + + +.. _output_channels: + +Output Channels +--------------- +Rendering video or audio starts from output channels. You assign a +source to an output channel via the :c:func:`obs_set_output_source()` +function. The *channel* parameter can be any number from +0..(MAX_CHANNELS_-1). You may initially think that this is how you +display multiple sources at once; however, sources are hierarchical. +Sources such as scenes or transitions can have multiple sub-sources, and +those sub-sources in turn can have sub-sources and so on (see +:ref:`displaying_sources` for more information). Typically, you would +use scenes to draw multiple sources as a group with specific transforms +for each source, as a scene is just another type of source. The +"channel" design allows for highly complex video presentation setups. +The OBS Studio front-end has yet to even fully utilize this back-end +design for its rendering, and currently only uses one output channel to +render one scene at a time. It does however utilize additional channels +for things such as global audio sources which are set in audio settings. + +*(Author's note: "Output channels" are not to be confused with output +objects or audio channels. Output channels are used to set the sources +you want to output, and output objects are used for actually +streaming/recording/etc.)* + + +General Video Pipeline Overview +------------------------------- +The video graphics pipeline is run from two threads: a dedicated +graphics thread that renders preview displays as well as the final mix +(the obs_graphics_thread_ function in `libobs/obs-video.c`_), and a +dedicated thread specific to video encoding/output (the video_thread_ +function in `libobs/media-io/video-io.c`_). + +Sources assigned to output channels will be drawn from channels +0..(MAX_CHANNELS_-1). They are drawn on to the final texture which will +be used for output `[1]`_. Once all sources are drawn, the final +texture is converted to whatever format that libobs is set to (typically +a YUV format). After being converted to the back-end video format, it's +then sent along with its timestamp to the current video handler, +`obs_core_video::video`_. + +It then puts that raw frame in a queue of MAX_CACHE_SIZE_ in the `video +output handler`_. A semaphore is posted, then the video-io thread will +process frames as it's able. If the video frame queue is full, it will +duplicate the last frame in the queue in an attempt to reduce video +encoding complexity (and thus CPU usage) `[2]`_. This is why you may +see frame skipping when the encoder can't keep up. Frames are sent to +any raw outputs or video encoders that are currently active `[3]`_. + +If it's sent to a video encoder object (`libobs/obs-encoder.c`_), it +encodes the frame and sends the encoded packet off to the outputs that +encoder is connected to (which can be multiple). If the output takes +both encoded video/audio, it puts the packets in an interleave queue to +ensure encoded packets are sent in monotonic timestamp order `[4]`_. + +The encoded packet or raw frame is then sent to the output. + + +General Audio Pipeline Overview +------------------------------- +The audio pipeline is run from a dedicated audio thread in the audio +handler (the `audio_thread`_ function in `libobs/media-io/audio-io.c`_); +assuming that AUDIO_OUTPUT_FRAMES_ is set to 1024, the audio thread +"ticks" (processes audio data) once every 1024 audio samples (around +every 21 millisecond intervals at 48khz), and calls the audio_callback_ +function in `libobs/obs-audio.c`_ where most of the audio processing is +accomplished. + +A source with audio will output its audio via the +obs_source_output_audio_ function, and that audio data will be appended +or inserted in to the circular buffer `obs_source::audio_input_buf`_. +If the sample rate or channel count does not match what the back-end is +set to, the audio is automatically remixed/resampled via swresample +`[5]`_. Before insertion, audio data is also run through any audio +filters attached to the source `[6]`_. + +Each audio tick, the audio thread takes a reference snapshot of the +audio source tree (stores references of all sources that output/process +audio) `[7]`_. On each audio leaf (audio source), it takes the closest +audio (relative to the current audio thread timestamp) stored in the +circular buffer `obs_source::audio_input_buf`_, and puts it in +`obs_source::audio_output_buf`_. + +Then, the audio samples stored in `obs_source::audio_output_buf`_ of the +leaves get sent through their parents in the source tree snapshot for +mixing or processing at each source node in the hierarchy `[8]`_. +Sources with multiple children such as scenes or transitions will +mix/process their children's audio themselves via the +`obs_source_info::audio_render`_ callback. This allows, for example, +transitions to fade in the audio of one source and fade in the audio of +a new source when they're transitioning between two sources. The mix or +processed audio data is then stored in `obs_source::audio_output_buf`_ +of that node similarly, and the process is repeated until the audio +reaches the root nodes of the tree. + +Finally, when the audio has reached the base of the snapshot tree, the +audio of all the sources in each output channel are mixed together for a +final mix `[9]`_. That final mix is then sent to any raw outputs or +audio encoders that are currently active `[10]`_. + +If it's sent to an audio encoder object (`libobs/obs-encoder.c`_), it +encodes the audio data and sends the encoded packet off to the outputs +that encoder is connected to (which can be multiple). If the output +takes both encoded video/audio, it puts the packets in an interleave +queue to ensure encoded packets are sent in monotonic timestamp order +`[4]`_. + +The encoded packet or raw audio data is then sent to the output. + +.. _obs_graphics_thread: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-video.c#L588-L651 +.. _libobs/obs-audio.c: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-audio.c +.. _libobs/obs-video.c: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-video.c +.. _video_thread: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L169-L195 +.. _libobs/media-io/video-io.c: https://github.com/jp9000/obs-studio/blob/master/libobs/media-io/video-io.c +.. _video output handler: https://github.com/jp9000/obs-studio/blob/master/libobs/media-io/video-io.c +.. _audio_thread: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/audio-io.c#L241-L282 +.. _libobs/media-io/audio-io.c: https://github.com/jp9000/obs-studio/blob/master/libobs/media-io/audio-io.c +.. _MAX_CHANNELS: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-defs.h#L20-L21 +.. _[1]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-video.c#L99-L129 +.. _obs_core_video::video: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-internal.h#L250 +.. _MAX_CACHE_SIZE: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L34 +.. _[2]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L431-L434 +.. _[3]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L115-L167 +.. _libobs/obs-encoder.c: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-encoder.c +.. _[4]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-output.c#L1382-L1439 +.. _AUDIO_OUTPUT_FRAMES: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/audio-io.h#L30 +.. _audio_callback: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L367-L485 +.. _obs_source_output_audio: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L2578-L2608 +.. _obs_source::audio_input_buf: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L1280-L1283 +.. _[5]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L2561-L2563 +.. _[6]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L2591 +.. _[7]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L393-L415 +.. _obs_source::audio_output_buf: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-internal.h#L580 +.. _[8]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L417-L423 +.. _obs_source_info::audio_render: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.h#L410-L412 +.. _[9]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L436-L453 +.. _[10]: https://github.com/jp9000/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/audio-io.c#L144-L165 diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py new file mode 100644 index 000000000..47ee2ec37 --- /dev/null +++ b/docs/sphinx/conf.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# OBS Studio documentation build configuration file, created by +# sphinx-quickstart on Wed Oct 25 00:03:21 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +primary_domain = 'c' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'OBS Studio' +copyright = '2017, Hugh Bailey' +author = 'Hugh Bailey' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '20.1.0' +# The full version, including alpha/beta/rc tags. +release = '20.1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'bizstyle' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'relations.html', # needs 'show_related': True theme option to display + 'searchbox.html', + ] +} + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'OBSStudiodoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'OBSStudio.tex', 'OBS Studio Documentation', + 'Hugh Bailey', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'obsstudio', 'OBS Studio Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'OBSStudio', 'OBS Studio Documentation', + author, 'OBSStudio', 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/docs/sphinx/frontends.rst b/docs/sphinx/frontends.rst new file mode 100644 index 000000000..b375a2e29 --- /dev/null +++ b/docs/sphinx/frontends.rst @@ -0,0 +1,252 @@ +Frontends +========= + +Initialization and Shutdown +--------------------------- + +To initialize libobs, you must call :c:func:`obs_startup()`, +:c:func:`obs_reset_video()`, and then :c:func:`obs_reset_audio()`. +After that, modules typically should be loaded. + +You can load individual modules manually by calling +:c:func:`obs_open_module()`. After loading, the +:c:func:`obs_open_module()` function, you must then call +:c:func:`obs_init_module()` to initialize the module. + +You can load modules automatically via two functions: +:c:func:`obs_add_module_path()` and :c:func:`obs_load_all_modules()`. + +After all plugin modules have been loaded, call +:c:func:`obs_post_load_modules()`. + +Certain modules may optionally use a configuration storage directory, +which is set as a parameter to :c:func:`obs_startup()`. + +When it's time to shut down the frontend, make sure to release all +references to any objects, free any data, and then call +:c:func:`obs_shutdown()`. If for some reason any libobs objects have +not been released, they will be destroyed automatically and a warning +will be logged. + +To detect if any general memory allocations have not been freed, call +the :c:func:`bnum_allocs()` to get the number of allocations remaining. +If the number remaining is above 0, there are memory leaks. + +See :ref:`obs_init_shutdown_reference` for more information. + + +Reconfiguring Video +------------------- + +Any time after initialization, video settings can be reconfigured by +calling :c:func:`obs_reset_video()` as long as no outputs are active. +Audio was originally intended to have this capability as well, but +currently is not able to be reset once initialized; libobs must be fully +shutdown in order to reconfigure audio settings. + + +Displays +-------- + +Displays as the name implies are used for display/preview panes. To use +displays, you must have a native window handle or identifier to draw on. + +First you must call :c:func:`obs_display_create()` to initialize the +display, then you must assign a draw callback with +:c:func:`obs_display_add_draw_callback()`. If you need to remove a draw +callback, call :c:func:`obs_display_remove_draw_callback()` similarly. + +When drawing, to draw the main preview window (if any), call +:c:func:`obs_render_main_texture()`. If you need to render a specific +source on a secondary display, you can increment its "showing" state +with :c:func:`obs_source_inc_showing()` while it's showing in the +secondary display, draw it with :c:func:`obs_source_video_render()` in +the draw callback, then when it's no longer showing in the secondary +display, call :c:func:`obs_source_dec_showing()`. + +If the display needs to be resized, call :c:func:`obs_display_resize()`. + +If the display needs a custom background color other than black, call +:c:func:`obs_display_set_background_color()`. + +If the display needs to be temporarily disabled, call +:c:func:`obs_display_set_enabled()` to disable, and +:c:func:`obs_display_enabled()` to get its enabled/disabled state. + +Then call :c:func:`obs_display_destroy()` to destroy the display when +it's no longer needed. + +*(Important note: do not use more than one display widget within the +hierarchy of the same base window; this will cause presentation stalls +on Macs.)* + +For an example of how displays are used with Qt, see +`UI/qt-display.hpp`_ and `UI/qt-display.cpp`_. + +See :ref:`display_reference` for more information. + + +Saving/Loading Objects and Object Management +-------------------------------------------- + +The frontend is generally expected to manage its own objects, however +for sources, there are some helper functions to allow easier +saving/loading all sources: :c:func:`obs_save_sources()` and +:c:func:`obs_load_sources()`. With those functions, all sources that +aren't private will automatically be saved and loaded. You can also +save/load individual sources manually by using +:c:func:`obs_save_source()` and :c:func:`obs_load_source()`. + +*(Author's note: I should not have written those helper functions; the +downside is I had to add "private" sources that aren't savable via the* +:c:func:`obs_source_create_private()` *function. Just one of the many +minor design flaws that can occur during long-term development.)* + +For outputs, encoders, and services, there are no helper functions, so +usually you'd get their settings individually and save them as json. +(See :c:func:`obs_output_get_settings()`). You don't have to save each +object to different files individually; you'd save multiple objects +together in a bigger :c:type:`obs_data_t` object, then save that via +:c:func:`obs_data_save_json_safe()`, then load everything again via +:c:func:`obs_data_create_from_json_file_safe()`. + + +Signals +------- + +The core, as well as scenes and sources, have a set of standard signals +that are used to determine when something happens or changes. + +Typically the most important signals are the +:ref:`output_signal_handler_reference`: the **start**, **stop**, +**starting**, **stopping**, **reconnect**, **reconnect_success** +signals in particular. + +Most other signals for scenes/sources are optional if you are the only +thing controlling their state. However, it's generally recommended to +watch most signals when possible for consistency. See +:ref:`source_signal_handler_reference` and :ref:`scene_signal_reference` +for more information. + +For example, let's say you wanted to connect a callback to the **stop** +signal of an output. The **stop** signal has two parameters: *output* +and *code*. A callback for this signal would typically look something +like this: + +.. code:: cpp + + static void output_stopped(void *my_data, calldata_t *cd) + { + obs_output_t *output = calldata_ptr(cd, "output"); + int code = calldata_int(cd, "code"); + + [...] + } + +*(Note that callbacks are not thread-safe.)* + +Then to connect it to the **stop** signal, you use the +:c:func:`signal_handler_connect()` with the callback. In this case for +example: + +.. code:: cpp + + signal_handler_t *handler = obs_output_get_signal_handler(output); + signal_handler_connect(handler, "stop", output_stopped); + + +.. _displaying_sources: + +Displaying Sources +------------------ + +Sources are displayed on stream/recording via :ref:`output_channels` +with the :c:func:`obs_set_output_source()` function. There are 64 +channels that you can assign sources to, which will draw on top of each +other in ascending index order. Typically, a normal source shouldn't be +directly assigned with this function; you would use a scene or a +transition containing scenes. + +To draw one or more sources together with a specific transform applied +to them, scenes are used. To create a scene, you call +:c:func:`obs_scene_create()`. Child sources are referenced using scene +items, and then specific transforms are applied to those scene items. +Scene items are not sources but containers for sources; the same source +can be referenced by multiple scene items within the same scene, or can +be referenced in multiple scenes. To create a scene item that +references a source, you call :c:func:`obs_scene_add()`, which returns a +new reference to a scene item. + +To change the transform of a scene item, you typically would call a +function like :c:func:`obs_sceneitem_set_pos()` to change its position, +:c:func:`obs_sceneitem_set_rot()` to change its rotation, or +:c:func:`obs_sceneitem_set_scale()` to change its scaling. Scene items +can also force scaling in to a custom size constraint referred to as a +"bounding box"; a bounding box will force the source to be drawn at a +specific size and with specific scaling constraint within that size. To +use a bounding box, you call the +:c:func:`obs_sceneitem_set_bounds_type()`, +:c:func:`obs_sceneitem_set_bounds()`, and +:c:func:`obs_sceneitem_set_bounds_alignment()`. Though the easiest way +to handle everything related to transforms is to use the +:c:func:`obs_sceneitem_set_info()` and +:c:func:`obs_sceneitem_get_info()` functions. See +:ref:`scene_item_reference` for all the functions related to scene +items. + +Usually, a smooth transition between multiple scenes is required. To do +this, transitions are used. To create a transition, you use +:c:func:`obs_source_create()` or :c:func:`obs_source_create_private()` +like any other source. Then, to activate a transition, you call +:c:func:`obs_transition_start()`. When the transition is not active and +is only displaying one source, it performs a pass-through to the current +displaying source. See :ref:`transitions` for more functions related to +using transitions. + +The recommended way to set up your structure is to have a transition as +the source that is used as the main output source, then your scene as a +child of the transition, then your sources as children in the scene. +When you need to switch to a new scene, simply call +:c:func:`obs_transition_start()`. + + +Outputs, Encoders, and Services +------------------------------- + +Outputs, encoders, and services are all used together, and managed a bit +differently than sources. There currently is no global function to +save/load them, that must be accomplished manually for now via their +settings if needed. + +Encoders are used with outputs that expect encoded data (which is almost +all typical outputs), such as standard file recording or streaming. + +Services are used with outputs to a stream; the `RTMP output`_ is the +quintessential example of this. + +Here's an example of how an output would be used with encoders and +services: + +.. code:: cpp + + obs_encoder_set_video(my_h264_encoder, obs_get_video()); + obs_encoder_set_audio(my_aac_encoder, obs_get_audio()); + obs_output_set_video_encoder(my_output, my_h264_encoder); + obs_output_set_audio_encoder(my_output, my_aac_encoder); + obs_output_set_service(my_output, my_service); /* if a stream */ + obs_output_start(my_output); + +Once the output has started successfully, it automatically starts +capturing the video and/or audio from the current video/audio output +(i.e. any sources that are assigned to the :ref:`output_channels`). + +If the output fails to start up, it will send the **stop** signal with +an error code in the *code* parameter, possibly accompanied by a +translated error message stored that can be obtained via the +:c:func:`obs_output_get_last_error()` function. + +.. -------------------------------------------------------------------- + +.. _RTMP Output: https://github.com/jp9000/obs-studio/blob/master/plugins/obs-outputs/rtmp-stream.c +.. _UI/qt-display.hpp: https://github.com/jp9000/obs-studio/blob/master/UI/qt-display.hpp +.. _UI/qt-display.cpp: https://github.com/jp9000/obs-studio/blob/master/UI/qt-display.cpp diff --git a/docs/sphinx/graphics.rst b/docs/sphinx/graphics.rst new file mode 100644 index 000000000..c2c91849a --- /dev/null +++ b/docs/sphinx/graphics.rst @@ -0,0 +1,364 @@ +Rendering Graphics +================== + +Libobs has a custom-made programmable graphics subsystem that wraps both +Direct3D 11 and OpenGL. The reason why it was designed with a custom +graphics subsystem was to accommodate custom capture features only +available on specific operating systems. + +*(Author's note: In retrospect, I probably should have used something +like ANGLE, but I would have to modify it to accommodate my specific +use-cases.)* + +Most rendering is dependent upon effects. Effects are used by all video +objects in libobs; they're used to easily bundle related vertex/pixel +shaders in to one file. + +An effect file has a nearly identical syntax to Direct3D 11 HLSL effect +files. The only differences are as follows: + +- Sampler states are named "sampler_state" +- Position semantic is called "POSITION" rather than "SV_Position" +- Target semantic is called "TARGET" rather than "SV_Target" + +*(Author's note: I'm probably missing a few exceptions here, if I am +please let me know)* + + +The Graphics Context +-------------------- + +Using graphics functions isn't possible unless the current thread has +entered a graphics context, and the graphics context can only be used by +one thread at a time. To enter the graphics context, use +:c:func:`obs_enter_graphics()`, and to leave the graphics context, use +:c:func:`obs_leave_graphics()`. + +Certain callback will automatically be within the graphics context: +:c:member:`obs_source_info.video_render`, and the draw callback +parameter of :c:func:`obs_display_add_draw_callback()`, and +:c:func:`obs_add_main_render_callback()`. + + +Creating Effects +---------------- + +Effect Parameters +^^^^^^^^^^^^^^^^^ + +To create an effect, it's recommended to start with the uniforms +(parameters) of the effect. + +There are a number of different types of uniforms: + ++------------------+---------------+------------------+------------+------------+ +| Floating points: | **float** | **float2** | **float3** | **float4** | ++------------------+---------------+------------------+------------+------------+ +| Matrices: | **float3x3** | **float4x4** | | | ++------------------+---------------+------------------+------------+------------+ +| Integers: | **int** | **int2** | **int3** | **int4** | ++------------------+---------------+------------------+------------+------------+ +| Booleans: | **bool** | | | | ++------------------+---------------+------------------+------------+------------+ +| Textures: | **texture2d** | **texture_cube** | | | ++------------------+---------------+------------------+------------+------------+ + +To get the effect uniform parameters, you use +:c:func:`gs_effect_get_param_by_name()` or +:c:func:`gs_effect_get_param_by_idx()`. + +Then the uniforms are set through the following functions: + +- :c:func:`gs_effect_set_bool()` +- :c:func:`gs_effect_set_float()` +- :c:func:`gs_effect_set_int()` +- :c:func:`gs_effect_set_matrix4()` +- :c:func:`gs_effect_set_vec2()` +- :c:func:`gs_effect_set_vec3()` +- :c:func:`gs_effect_set_vec4()` +- :c:func:`gs_effect_set_texture()` + +There are two "universal" effect parameters that may be expected of +effects: **ViewProj**, and **image**. The **ViewProj** parameter +(which is a float4x4) is used for the primary view/projection matrix +combination. The **image** parameter (which is a texture2d) is a +commonly used parameter for the main texture; this parameter will be +used with the functions :c:func:`obs_source_draw()`, +:c:func:`gs_draw_sprite()`, and +:c:func:`obs_source_process_filter_end()`. + +Here is an example of effect parameters: + +.. code:: cpp + + uniform float4x4 ViewProj; + uniform texture2d image; + + uniform float4 my_color_param; + uniform float my_float_param; + +Effect parameters can also have default values. Default parameters of +elements that have multiple elements should be treated as an array. + +Here are some examples of default parameters: + +.. code:: cpp + + uniform float4x4 my_matrix = {1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0}; + + uniform float4 my_float4 = {1.0, 0.5, 0.25, 0.0}; + uniform float my_float = 4.0; + uniform int my_int = 5; + +Effect Sampler States +^^^^^^^^^^^^^^^^^^^^^ + +Then, if textures are used, sampler states should be defined. Sampler +states have certain sub-parameters: + +- **Filter** - The type of filtering to use. Can be one of the + following values: + + - **Anisotropy** + - **Point** + - **Linear** + - **MIN_MAG_POINT_MIP_LINEAR** + - **MIN_POINT_MAG_LINEAR_MIP_POINT** + - **MIN_POINT_MAG_MIP_LINEAR** + - **MIN_LINEAR_MAG_MIP_POINT** + - **MIN_LINEAR_MAG_POINT_MIP_LINEAR** + - **MIN_MAG_LINEAR_MIP_POINT** + +- **AddressU**, **AddressV** - Specifies how to handle the sampling + when the coordinate goes beyond 0.0..1.0. Can be one of the following + values: + + - **Wrap** or **Repeat** + - **Clamp** or **None** + - **Mirror** + - **Border** (uses *BorderColor* to fill the color) + - **MirrorOnce** + +- **BorderColor** - Specifies the border color if using the "Border" + address mode. This value should be a hexadecimal value representing + the color, in the format of: AARRGGBB. For example, 7FFF0000 would + have its alpha value at 127, its red value at 255, and blue and green + at 0. If *Border* is not used as an addressing type, this value is + ignored. + +Here is an example of writing a sampler state in an effect file: + +.. code:: cpp + + sampler_state defaultSampler { + Filter = Linear; + AddressU = Border; + AddressV = Border; + BorderColor = 7FFF0000; + }; + +This sampler state would use linear filtering, would use border +addressing for texture coordinate values beyond 0.0..1.0, and the border +color would be the color specified above. + +When a sampler state is used, it's used identically to the HLSL form: + +.. code:: cpp + + [...] + + uniform texture2d image; + + sampler_state defaultSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; + }; + + [...] + + float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET + { + return image.Sample(def_sampler, vert_in.uv); + } + +Effect Vertex/Pixel Semantics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Then structures should be defined for inputs and outputs vertex +semantics. + +Vertex components can have the following semantics: + +- **COLOR** - Color value (*float4*). +- **POSITION** - Position value (*float4*). +- **NORMAL** - Normal value (*float4*). +- **TANGENT** - Tangent value (*float4*). +- **TEXCOORD[0..7]** - Texture cooordinate value (*float2*, *float3*, or + *float4*). + +Here is an example of a vertex semantic structure: + +.. code:: cpp + + struct VertexIn { + float4 my_position : POSITION; + float2 my_texcoord : TEXCOORD0; + }; + +These semantic structures are then passed in as a parameter to the +primary shader entry point, and used as a return value for the vertex +shader. Note that the vertex shader is allowed to return different +semantics than it takes in; but the return type of the vertex shader and +the parameter of the pixel shader must match. + +The semantic structure used for the parameter to the vertex shader +function will require that the vertex buffer have those values, so if +you have POSITION and TEXCOORD0, the vertex buffer will have to have at +least a position buffer and a texture coordinate buffer in it. + +For pixel shaders, they need to return with a TARGET semantic (which is +a float4 RGBA value). Here is an example of how it's usually used with +a pixel shader function: + +.. code:: cpp + + float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET + { + return image.Sample(def_sampler, vert_in.uv); + } + + +Effect Techniques +^^^^^^^^^^^^^^^^^ + +Techniques are used to define the primary vertex/pixel shader entry +functions per pass. One technique can have multiple passes or custom +pass setup. + +*(Author's note: These days, multiple passes aren't really needed; GPUs +are powerful enough to where you can perform all actions in the same +shader. Named passes can be useful for custom draw setups, but even +then you can just make it a separate technique. For that reason, it's +best to just ignore the extra pass functionality.)* + +If you're making an effect filter for video sources, typically you'd +name the pass **Draw**, and then +:c:func:`obs_source_process_filter_end()` will automatically call that +specific effect name. However, you can also use +:c:func:`obs_source_process_filter_tech_end()` to make the filter use a +specific technique by its name. + +The first parameter of the vertex/pixel shader functions in passes +should always be the name of its vertex semantic structure parameter. + +For techniques, it's better to show some examples of how techniques +would be used: + +.. code:: cpp + + uniform float4x4 ViewProj; + uniform texture2d image; + + struct VertInOut { + float4 my_position : POSITION; + float2 my_texcoord : TEXCOORD0; + }; + + VertInOut MyVertexShaderFunc(VertInOut vert_in) + { + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = vert_in.uv; + return vert_out; + } + + float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET + { + return image.Sample(def_sampler, vert_in.uv); + } + + technique Draw + { + pass + { + vertex_shader = MyVertexShaderFunc(vert_in); + pixel_shader = MyPixelShaderFunc(vert_in); + } + }; + +Using Effects +------------- + +The recommended way to use effects is like so: + +.. code:: cpp + + for (gs_effect_loop(effect, "technique")) { + [draw calls go here] + } + +This will automatically handle loading/unloading of the effect and its +shaders for a given technique name. + + +Rendering Video Sources +----------------------- + +A synchronous video source renders in its +:c:member:`obs_source_info.video_render` callback. + +Sources can render with custom drawing (via the OBS_SOURCE_CUSTOM_DRAW +output capability flag), or without. When sources render without custom +rendering, it's recommended to render a single texture with +:c:func:`obs_source_draw()`. Otherwise the source is expected to +perform rendering on its own and manage its own effects. + +Libobs comes with a set of default/standard effects that can be accessed +via the :c:func:`obs_get_base_effect()` function. You can use these +effects to render, or you can create custom effects with +:c:func:`gs_effect_create_from_file()` and render with a custom effect. + + +Rendering Video Effect Filters +------------------------------ + +For most video effect filters, it comprises of adding a layer of +processing shaders to an existing image in its +:c:member:`obs_source_info.video_render` callback. When this is the +case, it's expected that the filter has its own effect created, and to +draw the effect, one would simply use the +:c:func:`obs_source_process_filter_begin()` function, set the parameters +on your custom effect, then call either +:c:func:`obs_source_process_filter_end()` or +:c:func:`obs_source_process_filter_tech_end()` to finish rendering the +filter. + +Here's an example of rendering a filter from the color key filter: + +.. code:: cpp + + static void color_key_render(void *data, gs_effect_t *effect) + { + struct color_key_filter_data *filter = data; + + if (!obs_source_process_filter_begin(filter->context, GS_RGBA, + OBS_ALLOW_DIRECT_RENDERING)) + return; + + gs_effect_set_vec4(filter->color_param, &filter->color); + gs_effect_set_float(filter->contrast_param, filter->contrast); + gs_effect_set_float(filter->brightness_param, filter->brightness); + gs_effect_set_float(filter->gamma_param, filter->gamma); + gs_effect_set_vec4(filter->key_color_param, &filter->key_color); + gs_effect_set_float(filter->similarity_param, filter->similarity); + gs_effect_set_float(filter->smoothness_param, filter->smoothness); + + obs_source_process_filter_end(filter->context, filter->effect, 0, 0); + + UNUSED_PARAMETER(effect); + } + diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst new file mode 100644 index 000000000..fd1008d73 --- /dev/null +++ b/docs/sphinx/index.rst @@ -0,0 +1,26 @@ +.. OBS Studio documentation master file, created by + sphinx-quickstart on Wed Oct 25 00:03:21 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +.. _contents: + +Welcome to OBS Studio's documentation! +====================================== + +.. toctree:: + :maxdepth: 3 + + backend-design + plugins + frontends + graphics + scripting + reference-core + reference-modules + reference-core-objects + reference-libobs-util + reference-libobs-callback + reference-libobs-graphics + reference-libobs-media-io + reference-frontend-api diff --git a/docs/sphinx/make.bat b/docs/sphinx/make.bat new file mode 100644 index 000000000..9635336df --- /dev/null +++ b/docs/sphinx/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build +set SPHINXPROJ=OBSStudio + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/sphinx/plugins.rst b/docs/sphinx/plugins.rst new file mode 100644 index 000000000..2a6dd718b --- /dev/null +++ b/docs/sphinx/plugins.rst @@ -0,0 +1,569 @@ +Plugins +======= +Almost all custom functionality is added through plugin modules, which +are typically dynamic libraries or scripts. The ability to capture +and/or output audio/video, make a recording, output to an RTMP stream, +encode in x264 are all examples of things that are accomplished via +plugin modules. + +Plugins can implement sources, outputs, encoders, and services. + + +Plugin Module Headers +--------------------- +These are some notable headers commonly used by plugins: + +- `libobs/obs-module.h`_ -- The primary header used for creating plugin + modules. This file automatically includes the following files: + + - `libobs/obs.h`_ -- The main libobs header. This file automatically + includes the following files: + + - `libobs/obs-source.h`_ -- Used for implementing sources in plugin + modules + + - `libobs/obs-output.h`_ -- Used for implementing outputs in plugin + modules + + - `libobs/obs-encoder.h`_ -- Used for implementing encoders in + plugin modules + + - `libobs/obs-service.h`_ -- Used for implementing services in + plugin modules + + - `libobs/obs-data.h`_ -- Used for managing settings for libobs + objects + + - `libobs/obs-properties.h`_ -- Used for generating properties for + libobs objects + + - `libobs/graphics/graphics.h`_ -- Used for graphics rendering + + +Common Directory Structure and CMakeLists.txt +--------------------------------------------- +The common way source files are organized is to have one file for plugin +initialization, and then specific files for each individual object +you're implementing. For example, if you were to create a plugin called +'my-plugin', you'd have something like my-plugin.c where plugin +initialization is done, my-source.c for the definition of a custom +source, my-output.c for the definition of a custom output, etc. (This +is not a rule of course) + +This is an example of a common directory structure for a native plugin +module:: + + my-plugin/data/locale/en-US.ini + my-plugin/CMakeLists.txt + my-plugin/my-plugin.c + my-plugin/my-source.c + my-plugin/my-output.c + my-plugin/my-encoder.c + my-plugin/my-service.c + +This would be an example of a common CMakeLists.txt file associated with +these files:: + + # my-plugin/CMakeLists.txt + + project(my-plugin) + + set(my-plugin_SOURCES + my-plugin.c + my-source.c + my-output.c + my-encoder.c + my-service.c) + + add_library(my-plugin MODULE + ${my-plugin_SOURCES}) + target_link_libraries(my-plugin + libobs) + + install_obs_plugin_with_data(my-plugin data) + + +Native Plugin Initialization +---------------------------- +To create a native plugin module, you will need to include the +`libobs/obs-module.h`_ header, use :c:func:`OBS_DECLARE_MODULE()` macro, +then create a definition of the function :c:func:`obs_module_load()`. +In your :c:func:`obs_module_load()` function, you then register any of +your custom sources, outputs, encoders, or services. See the +:doc:`reference-modules` for more information. + +The following is an example of my-plugin.c, which would register one +object of each type: + +.. code:: cpp + + /* my-plugin.c */ + #include + + /* Defines common functions (required) */ + OBS_DECLARE_MODULE() + + /* Implements common ini-based locale (optional) */ + OBS_MODULE_USE_DEFAULT_LOCALE("my-plugin", "en-US") + + extern struct obs_source_info my_source; /* Defined in my-source.c */ + extern struct obs_output_info my_output; /* Defined in my-output.c */ + extern struct obs_encoder_info my_encoder; /* Defined in my-encoder.c */ + extern struct obs_service_info my_service; /* Defined in my-service.c */ + + bool obs_module_load(void) + { + obs_register_source(&my_source); + obs_register_output(&my_output); + obs_register_encoder(&my_encoder); + obs_register_service(&my_service); + return true; + } + + +.. _plugins_sources: + +Sources +------- +Sources are used to render video and/or audio on stream. Things such as +capturing displays/games/audio, playing a video, showing an image, or +playing audio. Sources can also be used to implement audio and video +filters as well as transitions. The `libobs/obs-source.h`_ file is the +dedicated header for implementing sources. See the +:doc:`reference-sources` for more information. + +For example, to implement a source object, you need to define an +:c:type:`obs_source_info` structure and fill it out with information and +callbacks related to your source: + +.. code:: cpp + + /* my-source.c */ + + [...] + + struct obs_source_info my_source { + .id = "my_source", + .type = OBS_SOURCE_TYPE_INPUT, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = my_source_name, + .create = my_source_create, + .destroy = my_source_destroy, + .update = my_source_update, + .video_render = my_source_render, + .get_width = my_source_width, + .get_height = my_source_height + }; + +Then, in my-plugin.c, you would call :c:func:`obs_register_source()` in +:c:func:`obs_module_load()` to register the source with libobs. + +.. code:: cpp + + /* my-plugin.c */ + + [...] + + extern struct obs_source_info my_source; /* Defined in my-source.c */ + + bool obs_module_load(void) + { + obs_register_source(&my_source); + + [...] + + return true; + } + +Some simple examples of sources: + +- Synchronous video source: The `image source `_ +- Asynchronous video source: The `random texture test source `_ +- Audio source: The `sine wave test source `_ +- Video filter: The `test video filter `_ +- Audio filter: The `gain audio filter `_ + + +.. _plugins_outputs: + +Outputs +------- +Outputs allow the ability to output the currently rendering audio/video. +Streaming and recording are two common examples of outputs, but not the +only types of outputs. Outputs can receive the raw data or receive +encoded data. The `libobs/obs-output.h`_ file is the dedicated header +for implementing outputs. See the :doc:`reference-outputs` for more +information. + +For example, to implement an output object, you need to define an +:c:type:`obs_output_info` structure and fill it out with information and +callbacks related to your output: + +.. code:: cpp + + /* my-output.c */ + + [...] + + struct obs_output_info my_output { + .id = "my_output", + .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED, + .get_name = my_output_name, + .create = my_output_create, + .destroy = my_output_destroy, + .start = my_output_start, + .stop = my_output_stop, + .encoded_packet = my_output_data, + .get_total_bytes = my_output_total_bytes, + .encoded_video_codecs = "h264", + .encoded_audio_codecs = "aac" + }; + +Then, in my-plugin.c, you would call :c:func:`obs_register_output()` in +:c:func:`obs_module_load()` to register the output with libobs. + +.. code:: cpp + + /* my-plugin.c */ + + [...] + + extern struct obs_output_info my_output; /* Defined in my-output.c */ + + bool obs_module_load(void) + { + obs_register_output(&my_output); + + [...] + + return true; + } + +Some examples of outputs: + +- Encoded video/audio outputs: + + - The `FLV output `_ + - The `FFmpeg muxer output `_ + - The `RTMP stream output `_ + +- Raw video/audio outputs: + + - The `FFmpeg output `_ + + +.. _plugins_encoders: + +Encoders +-------- +Encoders are OBS-specific implementations of video/audio encoders, which +are used with outputs that use encoders. x264, NVENC, Quicksync are +examples of encoder implementations. The `libobs/obs-encoder.h`_ file +is the dedicated header for implementing encoders. See the +:doc:`reference-encoders` for more information. + +For example, to implement an encoder object, you need to define an +:c:type:`obs_encoder_info` structure and fill it out with information +and callbacks related to your encoder: + +.. code:: cpp + + /* my-encoder.c */ + + [...] + + struct obs_encoder_info my_encoder_encoder = { + .id = "my_encoder", + .type = OBS_ENCODER_VIDEO, + .codec = "h264", + .get_name = my_encoder_name, + .create = my_encoder_create, + .destroy = my_encoder_destroy, + .encode = my_encoder_encode, + .update = my_encoder_update, + .get_extra_data = my_encoder_extra_data, + .get_sei_data = my_encoder_sei, + .get_video_info = my_encoder_video_info + }; + +Then, in my-plugin.c, you would call :c:func:`obs_register_encoder()` +in :c:func:`obs_module_load()` to register the encoder with libobs. + +.. code:: cpp + + /* my-plugin.c */ + + [...] + + extern struct obs_encoder_info my_encoder; /* Defined in my-encoder.c */ + + bool obs_module_load(void) + { + obs_register_encoder(&my_encoder); + + [...] + + return true; + } + +**IMPORTANT NOTE:** Encoder settings currently have a few expected +common setting values that should have a specific naming convention: + + - **"bitrate"** - This value should be used for both video and audio + encoders: bitrate, in kilobits. + - **"rate_control"** - This is a setting used for video encoders. + It's generally expected to have at least a "CBR" rate control. + Other common rate controls are "VBR", "CQP". + - **"keyint_sec"** - For video encoders, sets the keyframe interval + value, in seconds, or closest possible approximation. *(Author's + note: This should have have been "keyint", in frames.)* + +Examples of encoders: + +- Video encoders: + + - The `x264 encoder `_ + - The `FFmpeg NVENC encoder `_ + - The `Quicksync encoder `_ + +- Audio encoders: + + - The `FFmpeg AAC/Opus encoder `_ + + +.. _plugins_services: + +Services +-------- +Services are custom implementations of streaming services, which are +used with outputs that stream. For example, you could have a custom +implementation for streaming to Twitch, and another for YouTube to allow +the ability to log in and use their APIs to do things such as get the +RTMP servers or control the channel. The `libobs/obs-service.h`_ file +is the dedicated header for implementing services. See the +:doc:`reference-services` for more information. + +*(Author's note: the service API is incomplete as of this writing)* + +For example, to implement a service object, you need to define an +:c:type:`obs_service_info` structure and fill it out with information +and callbacks related to your service: + +.. code:: cpp + + /* my-service.c */ + + [...] + + struct obs_service_info my_service_service = { + .id = "my_service", + .get_name = my_service_name, + .create = my_service_create, + .destroy = my_service_destroy, + .encode = my_service_encode, + .update = my_service_update, + .get_url = my_service_url, + .get_key = my_service_key + }; + +Then, in my-plugin.c, you would call :c:func:`obs_register_service()` in +:c:func:`obs_module_load()` to register the service with libobs. + +.. code:: cpp + + /* my-plugin.c */ + + [...] + + extern struct obs_service_info my_service; /* Defined in my-service.c */ + + bool obs_module_load(void) + { + obs_register_service(&my_service); + + [...] + + return true; + } + +The only two existing services objects are the "common RTMP services" +and "custom RTMP service" objects in `plugins/rtmp-services +`_ + + +Settings +-------- +Settings (see `libobs/obs-data.h`_) are used to get or set settings data +typically associated with libobs objects, and can then be saved and +loaded via Json text. See the :doc:`reference-settings` for more +information. + +The *obs_data_t* is the equivalent of a Json object, where it's a string +table of sub-objects, and the *obs_data_array_t* is similarly used to +store an array of *obs_data_t* objects, similar to Json arrays (though +not quite identical). + +To create an *obs_data_t* or *obs_data_array_t* object, you'd call the +:c:func:`obs_data_create()` or :c:func:`obs_data_array_create()` +functions. *obs_data_t* and *obs_data_array_t* objects are reference +counted, so when you are finished with the object, call +:c:func:`obs_data_release()` or :c:func:`obs_data_array_release()` to +release those references. Any time an *obs_data_t* or +*obs_data_array_t* object is returned by a function, their references +are incremented, so you must release those references each time. + +To set values for an *obs_data_t* object, you'd use one of the following +functions: + +.. code:: cpp + + /* Set functions */ + EXPORT void obs_data_set_string(obs_data_t *data, const char *name, const char *val); + EXPORT void obs_data_set_int(obs_data_t *data, const char *name, long long val); + EXPORT void obs_data_set_double(obs_data_t *data, const char *name, double val); + EXPORT void obs_data_set_bool(obs_data_t *data, const char *name, bool val); + EXPORT void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj); + EXPORT void obs_data_set_array(obs_data_t *data, const char *name, obs_data_array_t *array); + +Similarly, to get a value from an *obs_data_t* object, you'd use one of +the following functions: + +.. code:: cpp + + /* Get functions */ + EXPORT const char *obs_data_get_string(obs_data_t *data, const char *name); + EXPORT long long obs_data_get_int(obs_data_t *data, const char *name); + EXPORT double obs_data_get_double(obs_data_t *data, const char *name); + EXPORT bool obs_data_get_bool(obs_data_t *data, const char *name); + EXPORT obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name); + EXPORT obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name); + +Unlike typical Json data objects, the *obs_data_t* object can also set +default values. This allows the ability to control what is returned if +there is no value assigned to a specific string in an *obs_data_t* +object when that data is loaded from a Json string or Json file. Each +libobs object also has a *get_defaults* callback which allows setting +the default settings for the object on creation. + +These functions control the default values are as follows: + +.. code:: cpp + + /* Default value functions. */ + EXPORT void obs_data_set_default_string(obs_data_t *data, const char *name, const char *val); + EXPORT void obs_data_set_default_int(obs_data_t *data, const char *name, long long val); + EXPORT void obs_data_set_default_double(obs_data_t *data, const char *name, double val); + EXPORT void obs_data_set_default_bool(obs_data_t *data, const char *name, bool val); + EXPORT void obs_data_set_default_obj(obs_data_t *data, const char *name, obs_data_t *obj); + + +Properties +---------- +Properties (see `libobs/obs-properties.h`_) are used to automatically +generate user interface to modify settings for a libobs object (if +desired). Each libobs object has a *get_properties* callback which is +used to generate properties. The properties API defines specific +properties that are linked to the object's settings, and the front-end +uses those properties to generate widgets in order to allow the user to +modify the settings. For example, if you had a boolean setting, you +would use :c:func:`obs_properties_add_bool()` to allow the user to be +able to change that setting. See the :doc:`reference-properties` for +more information. + +An example of this: + +.. code:: cpp + + static obs_properties_t *my_source_properties(void *data) + { + obs_properties_t *ppts = obs_properties_create(); + obs_properties_add_bool(ppts, "my_bool", + obs_module_text("MyBool")); + UNUSED_PARAMETER(data); + return ppts; + } + + [...] + + struct obs_source_info my_source { + .get_properties = my_source_properties, + [...] + }; + +The *data* parameter is the object's data if the object is present. +Typically this is unused and probably shouldn't be used if possible. It +can be null if the properties are retrieved without an object associated +with it. + +Properties can also be modified depending on what settings are shown. +For example, you can mark certain properties as disabled or invisible +depending on what a particular setting is set to using the +:c:func:`obs_property_set_modified_callback()` function. + +For example, if you wanted boolean property A to hide text property B: + +.. code:: cpp + + static bool setting_a_modified(obs_properties_t *ppts, + obs_property_t *p, obs_data_t *settings) + { + bool enabled = obs_data_get_bool(settings, "setting_a"); + p = obs_properties_get(ppts, "setting_b"); + obs_property_set_enabled(p, enabled); + + /* return true to update property widgets, false + otherwise */ + return true; + } + + [...] + + static obs_properties_t *my_source_properties(void *data) + { + obs_properties_t *ppts = obs_properties_create(); + obs_property_t *p; + + p = obs_properties_add_bool(ppts, "setting_a", + obs_module_text("SettingA")); + obs_property_set_modified_callback(p, setting_a_modified); + + obs_properties_add_text(ppts, "setting_b", + obs_module_text("SettingB"), + OBS_TEXT_DEFAULT); + return ppts; + } + + +Localization +------------ +Typically, most plugins bundled with OBS Studio will use a simple +ini-file localization method, where each file is a different language. +When using this method, the :c:func:`OBS_MODULE_USE_DEFAULT_LOCALE()` +macro is used which will automatically load/destroy the locale data with +no extra effort on part of the plugin. Then the +:c:func:`obs_module_text()` function (which is automatically declared as +an extern by `libobs/obs-module.h`_) is used when text lookup is needed. + +There are two exports the module used to load/destroy locale: the +:c:func:`obs_module_set_locale()` export, and the +:c:func:`obs_module_free_locale()` export. The +:c:func:`obs_module_set_locale()` export is called by libobs to set the +current language, and then the :c:func:`obs_module_free_locale()` export +is called by libobs on destruction of the module. If you wish to +implement a custom locale implementation for your plugin, you'd want to +define these exports along with the :c:func:`obs_module_text()` extern +yourself instead of relying on the +:c:func:`OBS_MODULE_USE_DEFAULT_LOCALE()` macro. + + +.. --------------------------------------------------------------------------- + +.. _libobs/obs-module.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-module.h +.. _libobs/obs.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs.h +.. _libobs/obs-source.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-source.h +.. _libobs/obs-output.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-output.h +.. _libobs/obs-encoder.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-encoder.h +.. _libobs/obs-service.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-service.h +.. _libobs/obs-data.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-data.h +.. _libobs/obs-properties.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-properties.h +.. _libobs/graphics/graphics.h: https://github.com/jp9000/obs-studio/blob/master/libobs/graphics/graphics.h diff --git a/docs/sphinx/reference-core-objects.rst b/docs/sphinx/reference-core-objects.rst new file mode 100644 index 000000000..8949b586c --- /dev/null +++ b/docs/sphinx/reference-core-objects.rst @@ -0,0 +1,13 @@ +Core API Object Reference +========================= + +.. toctree:: + :maxdepth: 3 + + reference-sources + reference-scenes + reference-outputs + reference-encoders + reference-services + reference-settings + reference-properties diff --git a/docs/sphinx/reference-core.rst b/docs/sphinx/reference-core.rst new file mode 100644 index 000000000..bc4408340 --- /dev/null +++ b/docs/sphinx/reference-core.rst @@ -0,0 +1,664 @@ +OBS Core API Reference +====================== + +.. code:: cpp + + #include + + +.. _obs_init_shutdown_reference: + +Initialization, Shutdown, and Information +----------------------------------------- + +.. function:: bool obs_startup(const char *locale, const char *module_config_path, profiler_name_store_t *store) + + Initializes the OBS core context. + + :param locale: The locale to use for modules + (E.G. "en-US") + :param module_config_path: Path to module config storage directory + (or *NULL* if none) + :param store: The profiler name store for OBS to use or NULL + :return: *false* if already initialized or failed + to initialize + +--------------------- + +.. function:: void obs_shutdown(void) + + Releases all data associated with OBS and terminates the OBS context. + +--------------------- + +.. function:: bool obs_initialized(void) + + :return: true if the main OBS context has been initialized + +--------------------- + +.. function:: uint32_t obs_get_version(void) + + :return: The current core version + +--------------------- + +.. function:: const char *obs_get_version_string(void) + + :return: The current core version string + +--------------------- + +.. function:: void obs_set_locale(const char *locale) + + Sets a new locale to use for modules. This will call + obs_module_set_locale for each module with the new locale. + + :param locale: The locale to use for modules + +--------------------- + +.. function:: const char *obs_get_locale(void) + + :return: The current locale + +--------------------- + +.. function:: profiler_name_store_t *obs_get_profiler_name_store(void) + + :return: The profiler name store (see util/profiler.h) used by OBS, + which is either a name store passed to obs_startup, an + internal name store, or NULL in case obs_initialized() + returns false. + +--------------------- + +.. function:: int obs_reset_video(struct obs_video_info *ovi) + + Sets base video output base resolution/fps/format. + + Note: This data cannot be changed if an output is currently active. + + Note: The graphics module cannot be changed without fully destroying + the OBS context. + + :param ovi: Pointer to an obs_video_info structure containing the + specification of the graphics subsystem, + :return: | OBS_VIDEO_SUCCESS - Success + | OBS_VIDEO_NOT_SUPPORTED - The adapter lacks capabilities + | OBS_VIDEO_INVALID_PARAM - A parameter is invalid + | OBS_VIDEO_CURRENTLY_ACTIVE - Video is currently active + | OBS_VIDEO_MODULE_NOT_FOUND - The graphics module is not found + | OBS_VIDEO_FAIL - Generic failure + + Relevant data types used with this function: + +.. code:: cpp + + struct obs_video_info { + /** + * Graphics module to use (usually "libobs-opengl" or "libobs-d3d11") + */ + const char *graphics_module; + + uint32_t fps_num; /**< Output FPS numerator */ + uint32_t fps_den; /**< Output FPS denominator */ + + uint32_t base_width; /**< Base compositing width */ + uint32_t base_height; /**< Base compositing height */ + + uint32_t output_width; /**< Output width */ + uint32_t output_height; /**< Output height */ + enum video_format output_format; /**< Output format */ + + /** Video adapter index to use (NOTE: avoid for optimus laptops) */ + uint32_t adapter; + + /** Use shaders to convert to different color formats */ + bool gpu_conversion; + + enum video_colorspace colorspace; /**< YUV type (if YUV) */ + enum video_range_type range; /**< YUV range (if YUV) */ + + enum obs_scale_type scale_type; /**< How to scale if scaling */ + }; + +--------------------- + +.. function:: bool obs_reset_audio(const struct obs_audio_info *oai) + + Sets base audio output format/channels/samples/etc. + + Note: Cannot reset base audio if an output is currently active. + + :return: *true* if successful, *false* otherwise + + Relevant data types used with this function: + +.. code:: cpp + + struct obs_audio_info { + uint32_t samples_per_sec; + enum speaker_layout speakers; + }; + +--------------------- + +.. function:: bool obs_get_video_info(struct obs_video_info *ovi) + + Gets the current video settings. + + :return: *false* if no video + +--------------------- + +.. function:: bool obs_get_audio_info(struct obs_audio_info *oai) + + Gets the current audio settings. + + :return: *false* if no audio + +--------------------- + + +Libobs Objects +-------------- + +.. function:: bool obs_enum_source_types(size_t idx, const char **id) + + Enumerates all source types (inputs, filters, transitions, etc). + +--------------------- + +.. function:: bool obs_enum_input_types(size_t idx, const char **id) + + Enumerates all available inputs source types. + + Inputs are general source inputs (such as capture sources, device sources, + etc). + +--------------------- + +.. function:: bool obs_enum_filter_types(size_t idx, const char **id) + + Enumerates all available filter source types. + + Filters are sources that are used to modify the video/audio output of + other sources. + +--------------------- + +.. function:: bool obs_enum_transition_types(size_t idx, const char **id) + + Enumerates all available transition source types. + + Transitions are sources used to transition between two or more other + sources. + +--------------------- + +.. function:: bool obs_enum_output_types(size_t idx, const char **id) + + Enumerates all available output types. + +--------------------- + +.. function:: bool obs_enum_encoder_types(size_t idx, const char **id) + + Enumerates all available encoder types. + +--------------------- + +.. function:: bool obs_enum_service_types(size_t idx, const char **id) + + Enumerates all available service types. + +--------------------- + +.. function:: void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param) + + Enumerates all input sources. + + Callback function returns true to continue enumeration, or false to end + enumeration. + + Use :c:func:`obs_source_get_ref()` or + :c:func:`obs_source_get_weak_source()` if you want to retain a + reference after obs_enum_sources finishes. + +--------------------- + +.. function:: void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t*), void *param) + + Enumerates outputs. + +--------------------- + +.. function:: void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t*), void *param) + + Enumerates encoders. + +--------------------- + +.. function:: obs_source_t *obs_get_source_by_name(const char *name) + + Gets a source by its name. + + Increments the source reference counter, use + :c:func:`obs_source_release()` to release it when complete. + +--------------------- + +.. function:: obs_output_t *obs_get_output_by_name(const char *name) + + Gets an output by its name. + + Increments the output reference counter, use + :c:func:`obs_output_release()` to release it when complete. + +--------------------- + +.. function:: obs_encoder_t *obs_get_encoder_by_name(const char *name) + + Gets an encoder by its name. + + Increments the encoder reference counter, use + :c:func:`obs_encoder_release()` to release it when complete. + +--------------------- + +.. function:: obs_service_t *obs_get_service_by_name(const char *name) + + Gets an service by its name. + + Increments the service reference counter, use + :c:func:`obs_service_release()` to release it when complete. + +--------------------- + +.. function:: obs_data_t *obs_save_source(obs_source_t *source) + + :return: A new reference to a source's saved data + +--------------------- + +.. function:: obs_source_t *obs_load_source(obs_data_t *data) + + :return: A source created from saved data + +--------------------- + +.. function:: void obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb, void *private_data) + + Helper function to load active sources from a data array. + + Relevant data types used with this function: + +.. code:: cpp + + typedef void (*obs_load_source_cb)(void *private_data, obs_source_t *source); + +--------------------- + +.. function:: obs_data_array_t *obs_save_sources(void) + + :return: A data array with the saved data of all active sources + +--------------------- + +.. function:: obs_data_array_t *obs_save_sources_filtered(obs_save_source_filter_cb cb, void *data) + + :return: A data array with the saved data of all active sources, + filtered by the *cb* function + + Relevant data types used with this function: + +.. code:: cpp + + typedef bool (*obs_save_source_filter_cb)(void *data, obs_source_t *source); + +--------------------- + + +Video, Audio, and Graphics +-------------------------- + +.. function:: void obs_enter_graphics(void) + + Helper function for entering the OBS graphics context. + +--------------------- + +.. function:: void obs_leave_graphics(void) + + Helper function for leaving the OBS graphics context. + +--------------------- + +.. function:: audio_t *obs_get_audio(void) + + :return: The main audio output handler for this OBS context + +--------------------- + +.. function:: video_t *obs_get_video(void) + + :return: The main video output handler for this OBS context + +--------------------- + +.. function:: void obs_set_output_source(uint32_t channel, obs_source_t *source) + + Sets the primary output source for a channel. + +--------------------- + +.. function:: obs_source_t *obs_get_output_source(uint32_t channel) + + Gets the primary output source for a channel and increments the reference + counter for that source. Use :c:func:`obs_source_release()` to release. + +--------------------- + +.. function:: gs_effect_t *obs_get_base_effect(enum obs_base_effect effect) + + Returns a commoinly used base effect. + + :param effect: | Can be one of the following values: + | OBS_EFFECT_DEFAULT - RGB/YUV + | OBS_EFFECT_DEFAULT_RECT - RGB/YUV (using texture_rect) + | OBS_EFFECT_OPAQUE - RGB/YUV (alpha set to 1.0) + | OBS_EFFECT_SOLID - RGB/YUV (solid color only) + | OBS_EFFECT_BICUBIC - Bicubic downscale + | OBS_EFFECT_LANCZOS - Lanczos downscale + | OBS_EFFECT_BILINEAR_LOWRES - Bilinear low resolution downscale + | OBS_EFFECT_PREMULTIPLIED_ALPHA - Premultiplied alpha + +--------------------- + +.. function:: void obs_render_main_view(void) + + Renders the main view. + + Note: This function is deprecated. + +--------------------- + +.. function:: void obs_render_main_texture(void) + + Renders the main output texture. Useful for rendering a preview pane + of the main output. + +--------------------- + +.. function:: void obs_set_master_volume(float volume) + + Sets the master user volume. + +--------------------- + +.. function:: float obs_get_master_volume(void) + + :return: The master user volume + +--------------------- + +.. function:: void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data) + + Enumerates audio devices which can be used for audio monitoring. + + Relevant data types used with this function: + +.. code:: cpp + + typedef bool (*obs_enum_audio_device_cb)(void *data, const char *name, const char *id); + +--------------------- + +.. function:: bool obs_set_audio_monitoring_device(const char *name, const char *id) + + Sets the current audio device for audio monitoring. + +--------------------- + +.. function:: void obs_get_audio_monitoring_device(const char **name, const char **id) + + Gets the current audio device for audio monitoring. + +--------------------- + +.. function:: void obs_add_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param) + void obs_remove_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param) + + Adds/removes a main rendering callback. Allows custom rendering to + the main stream/recording output. + +--------------------- + +.. function:: void obs_add_raw_video_callback(const struct video_scale_info *conversion, void (*callback)(void *param, struct video_data *frame), void *param) + void obs_remove_raw_video_callback(void (*callback)(void *param, struct video_data *frame), void *param) + + Adds/removes a raw video callback. Allows the ability to obtain raw + video frames without necessarily using an output. + + :param conversion: Specifies conversion requirements. Can be NULL. + :param callback: The callback that receives raw video frames. + :param param: The private data associated with the callback. + + +Primary signal/procedure handlers +--------------------------------- + +.. function:: signal_handler_t *obs_get_signal_handler(void) + + :return: The primary obs signal handler + + See :ref:`core_signal_handler_reference` for more information on + core signals. + +--------------------- + +.. function:: proc_handler_t *obs_get_proc_handler(void) + + :return: The primary obs procedure handler + + +.. _core_signal_handler_reference: + +Core OBS Signals +---------------- + +**source_create** (ptr source) + + Called when a source has been created. + +**source_destroy** (ptr source) + + Called when a source has been destroyed. + +**source_remove** (ptr source) + + Called when a source has been removed (:c:func:`obs_source_remove()` + has been called on the source). + +**source_save** (ptr source) + + Called when a source is being saved. + +**source_load** (ptr source) + + Called when a source is being loaded. + +**source_activate** (ptr source) + + Called when a source has been activated in the main view (visible on + stream/recording). + +**source_deactivate** (ptr source) + + Called when a source has been deactivated from the main view (no + longer visible on stream/recording). + +**source_show** (ptr source) + + Called when a source is visible on any display and/or on the main + view. + +**source_hide** (ptr source) + + Called when a source is no longer visible on any display and/or on + the main view. + +**source_rename** (ptr source, string new_name, string prev_name) + + Called when a source has been renamed. + +**source_volume** (ptr source, in out float volume) + + Called when a source's volume has changed. + +**source_transition_start** (ptr source) + + Called when a transition has started its transition. + +**source_transition_video_stop** (ptr source) + + Called when a transition has stopped its video transitioning. + +**source_transition_stop** (ptr source) + + Called when a transition has stopped its transition. + +**channel_change** (int channel, in out ptr source, ptr prev_source) + + Called when :c:func:`obs_set_output_source()` has been called. + +**master_volume** (in out float volume) + + Called when the master volume has changed. + +**hotkey_layout_change** () + + Called when the hotkey layout has changed. + +**hotkey_register** (ptr hotkey) + + Called when a hotkey has been registered. + +**hotkey_unregister** (ptr hotkey) + + Called when a hotkey has been unregistered. + +**hotkey_bindings_changed** (ptr hotkey) + + Called when a hotkey's bindings has changed. + +--------------------- + + +.. _display_reference: + +Displays +-------- + +.. function:: obs_display_t *obs_display_create(const struct gs_init_data *graphics_data) + + Adds a new window display linked to the main render pipeline. This creates + a new swap chain which updates every frame. + + *(Important note: do not use more than one display widget within the + hierarchy of the same base window; this will cause presentation + stalls on Macs.)* + + :param graphics_data: The swap chain initialization data + :return: The new display context, or NULL if failed + + Relevant data types used with this function: + +.. code:: cpp + + enum gs_color_format { + [...] + GS_RGBA, + GS_BGRX, + GS_BGRA, + GS_RGBA16F, + GS_RGBA32F, + [...] + }; + + enum gs_zstencil_format { + GS_ZS_NONE, + GS_Z16, + GS_Z24_S8, + GS_Z32F, + GS_Z32F_S8X24 + }; + + struct gs_window { + #if defined(_WIN32) + void *hwnd; + #elif defined(__APPLE__) + __unsafe_unretained id view; + #elif defined(__linux__) || defined(__FreeBSD__) + uint32_t id; + void *display; + #endif + }; + + struct gs_init_data { + struct gs_window window; + uint32_t cx, cy; + uint32_t num_backbuffers; + enum gs_color_format format; + enum gs_zstencil_format zsformat; + uint32_t adapter; + }; + +--------------------- + +.. function:: void obs_display_destroy(obs_display_t *display) + + Destroys a display context. + +--------------------- + +.. function:: void obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy) + + Changes the size of a display context. + +--------------------- + +.. function:: void obs_display_add_draw_callback(obs_display_t *display, void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param) + + Adds a draw callback for a display context, which will be called + whenever the display is rendered. + + :param display: The display context + :param draw: The draw callback which is called each time a frame + updates + :param param: The user data to be associated with this draw callback + +--------------------- + +.. function:: void obs_display_remove_draw_callback(obs_display_t *display, void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param) + + Removes a draw callback for a display context. + +--------------------- + +.. function:: void obs_display_set_enabled(obs_display_t *display, bool enable) + + Enables/disables a display context. + +--------------------- + +.. function:: bool obs_display_enabled(obs_display_t *display) + + :return: *true* if the display is enabled, *false* otherwise + +--------------------- + +.. function:: void obs_display_set_background_color(obs_display_t *display, uint32_t color) + + Sets the background (clear) color for the display context. diff --git a/docs/sphinx/reference-encoders.rst b/docs/sphinx/reference-encoders.rst new file mode 100644 index 000000000..a9b47282c --- /dev/null +++ b/docs/sphinx/reference-encoders.rst @@ -0,0 +1,494 @@ +Encoder API Reference (obs_encoder_t) +===================================== + +Encoders are OBS-specific implementations of video/audio encoders, which +are used with outputs that use encoders. x264, NVENC, Quicksync are +examples of encoder implementations. The `libobs/obs-encoder.h`_ file +is the dedicated header for implementing encoders + +.. type:: obs_encoder_t + + A reference-counted encoder object. + +.. type:: obs_weak_encoder_t + + A weak reference to an encoder object. + +.. code:: cpp + + #include + + +Encoder Definition Structure (obs_encoder_info) +----------------------------------------------- + +.. type:: struct obs_encoder_info + + Encoder definition structure. + +.. member:: const char *obs_encoder_info.id + + Unique string identifier for the encoder (required). + +.. member:: enum obs_encoder_type obs_encoder_info.type + + Type of encoder. + + - **OBS_ENCODER_VIDEO** - Video encoder + - **OBS_ENCODER_AUDIO** - Audio encoder + +.. member:: const char *obs_encoder_info.codec + + The codec, in string form. For example, "h264" for an H.264 encoder. + +.. member:: const char *(*obs_encoder_info.get_name)(void *type_data) + + Get the translated name of the encoder type. + + :param type_data: The type_data variable of this structure + :return: The translated name of the encoder type + +.. member:: void *(*obs_encoder_info.create)(obs_data_t *settings, obs_encoder_t *encoder) + + Creates the implementation data for the encoder. + + :param settings: Settings to initialize the encoder with + :param encoder: Encoder that this data is associated with + :return: The implementation data associated with this encoder + +.. member:: void (*obs_encoder_info.destroy)(void *data) + + Destroys the implementation data for the encoder. + +.. member:: bool (*encode)(void *data, struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet) + + Called to encode video or audio and outputs packets as they become + available. + + :param frame: Raw audio/video data to encode + :param packet: Encoder packet output, if any + :param received_packet: Set to *true* if a packet was received, + *false* otherwise + :return: true if successful, false on critical failure + +.. member:: size_t (*get_frame_size)(void *data) + + :return: An audio encoder's frame size. For example, for AAC this + number would be 1024 + +.. member:: void (*obs_encoder_info.get_defaults)(obs_data_t *settings) + void (*obs_encoder_info.get_defaults2)(void *type_data, obs_data_t *settings) + + Sets the default settings for this encoder. + + :param settings: Default settings. Call obs_data_set_default* + functions on this object to set default setting + values + +.. member:: obs_properties_t *(*obs_encoder_info.get_properties)(void *data) + obs_properties_t *(*obs_encoder_info.get_properties2)(void *data, void *type_data) + + Gets the property information of this encoder. + + (Optional) + + :return: The properties of the encoder + +.. member:: void (*obs_encoder_info.update)(void *data, obs_data_t *settings) + + Updates the settings for this encoder. + + (Optional) + + :param settings: New settings for this encoder + +.. member:: bool (*obs_encoder_info.get_extra_data)(void *data, uint8_t **extra_data, size_t *size) + + Returns extra data associated with this encoder (usually header). + + (Optional) + + :param extra_data: Pointer to receive the extra data + :param size: Pointer to receive the size of the extra + data + :return: true if extra data available, false + otherwise + +.. member:: bool (*obs_encoder_info.get_sei_data)(void *data, uint8_t **sei_data, size_t *size) + + Gets the SEI data of a video encoder that has SEI data. + + (Optional) + + :param sei_data: Pointer to receive the SEI data + :param size: Pointer to receive the SEI data size + :return: true if SEI data available, false otherwise + +.. member:: void (*obs_encoder_info.get_audio_info)(void *data, struct audio_convert_info *info) + + Returns desired audio format and sample information. This callback + can be used to tell the back-end that the audio data needs to be + automatically converted to a different sample rate or audio format + before being sent to the encoder. + + (Optional) + + :param info: Audio format information + +.. member:: void (*obs_encoder_info.get_video_info)(void *data, struct video_scale_info *info) + + Returns desired video format information. This callback can be used + to tell the back-end that the video data needs to be automatically + converted to a different video format or specific size before being + sent to the encoder. + + :param info: Video format information + +.. member:: void *obs_encoder_info.type_data + void (*obs_encoder_info.free_type_data)(void *type_data) + + Private data associated with this entry. Note that this is not the + same as the implementation data; this is used to differentiate + between two different types if the same callbacks are used for more + than one different type. + +.. member:: uint32_t obs_encoder_info.caps + + Can be 0 or a bitwise OR combination of one or more of the following + values: + + - **OBS_ENCODER_CAP_DEPRECATED** - Encoder is deprecated + + +Encoder Packet Structure (encoder_packet) +----------------------------------------- + +.. type:: struct encoder_packet + + Encoder packet structure. + +.. member:: uint8_t *encoder_packet.data + + Packet data. + +.. member:: size_t encoder_packet.size + + Packet size. + +.. member:: int64_t encoder_packet.pts + int64_t encoder_packet.dts + + Packet presentation and decode timestamps. + +.. member:: int32_t encoder_packet.timebase_num + int32_t encoder_packet.timebase_den + + Packet time base. + +.. member:: enum obs_encoder_type encoder_packet.type + + Can be one of the following values: + + - **OBS_ENCODER_VIDEO** - Video data + - **OBS_ENCODER_AUDIO** - Audio data + +.. member:: bool encoder_packet.keyframe + + Packet is a keyframe. + +.. member:: int64_t encoder_packet.dts_usec + + The DTS in microseconds. + + (This should not be set by the encoder implementation) + +.. member:: int64_t encoder_packet.sys_dts_usec + + The system time of this packet in microseconds. + + (This should not be set by the encoder implementation) + +.. member:: int encoder_packet.priority + + Packet priority. This is no longer used. + + (This should not be set by the encoder implementation) + +.. member:: int encoder_packet.drop_priority + + Packet drop priority. + + If this packet needs to be dropped, the next packet must be of this + priority or higher to continue transmission. + + (This should not be set by the encoder implementation) + +.. member:: size_t encoder_packet.track_idx + + Audio track index. + + (This should not be set by the encoder implementation) + +.. member:: obs_encoder_t *encoder_packet.encoder + + Encoder object associated with this packet. + + (This should not be set by the encoder implementation) + + +Raw Frame Data Structure (encoder_frame) +---------------------------------------- + +.. type:: struct encoder_frame + + Raw frame data structure. + +.. member:: uint8_t *encoder_frame.data[MAX_AV_PLANES] + + Raw video/audio data. + +.. member:: uint32_t encoder_frame.linesize[MAX_AV_PLANES] + + Line size of each plane. + +.. member:: uint32_t encoder_frame.frames + + Number of audio frames (if audio). + +.. member:: int64_t encoder_frame.pts + + Presentation timestamp. + + +General Encoder Functions +------------------------- + +.. function:: void obs_register_encoder(struct obs_encoder_info *info) + + Registers an encoder type. Typically used in + :c:func:`obs_module_load()` or in the program's initialization phase. + +--------------------- + +.. function:: const char *obs_encoder_get_display_name(const char *id) + + Calls the :c:member:`obs_encoder_info.get_name` callback to get the + translated display name of an encoder type. + + :param id: The encoder type string identifier + :return: The translated display name of an encoder type + +--------------------- + +.. function:: obs_encoder_t *obs_video_encoder_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data) + + Creates a video encoder with the specified settings. + + The "encoder" context is used for encoding video/audio data. Use + obs_encoder_release to release it. + + :param id: The encoder type string identifier + :param name: The desired name of the encoder. If this is + not unique, it will be made to be unique + :param settings: The settings for the encoder, or *NULL* if + none + :param hotkey_data: Saved hotkey data for the encoder, or *NULL* + if none + :return: A reference to the newly created encoder, or + *NULL* if failed + +--------------------- + +.. function:: obs_encoder_t *obs_audio_encoder_create(const char *id, const char *name, obs_data_t *settings, size_t mixer_idx, obs_data_t *hotkey_data) + + Creates an audio encoder with the specified settings. + + The "encoder" context is used for encoding video/audio data. Use + :c:func:`obs_encoder_release()` to release it. + + :param id: The encoder type string identifier + :param name: The desired name of the encoder. If this is + not unique, it will be made to be unique + :param settings: The settings for the encoder, or *NULL* if + none + :param mixer_idx: The audio mixer index this audio encoder + will capture audio from + :param hotkey_data: Saved hotkey data for the encoder, or *NULL* + if none + :return: A reference to the newly created encoder, or + *NULL* if failed + +--------------------- + +.. function:: void obs_encoder_addref(obs_encoder_t *encoder) + void obs_encoder_release(obs_encoder_t *encoder) + + Adds/releases a reference to an encoder. When the last reference is + released, the encoder is destroyed. + +--------------------- + +.. function:: obs_weak_encoder_t *obs_encoder_get_weak_encoder(obs_encoder_t *encoder) + obs_encoder_t *obs_weak_encoder_get_encoder(obs_weak_encoder_t *weak) + + These functions are used to get a weak reference from a strong encoder + reference, or a strong encoder reference from a weak reference. If + the encoder is destroyed, *obs_weak_encoder_get_encoder* will return + *NULL*. + +--------------------- + +.. function:: void obs_weak_encoder_addref(obs_weak_encoder_t *weak) + void obs_weak_encoder_release(obs_weak_encoder_t *weak) + + Adds/releases a weak reference to an encoder. + +--------------------- + +.. function:: void obs_encoder_set_name(obs_encoder_t *encoder, const char *name) + + Sets the name of an encoder. If the encoder is not private and the + name is not unique, it will automatically be given a unique name. + +--------------------- + +.. function:: const char *obs_encoder_get_name(const obs_encoder_t *encoder) + + :return: The name of the encoder + +--------------------- + +.. function:: const char *obs_encoder_get_codec(const obs_encoder_t *encoder) + const char *obs_get_encoder_codec(const char *id) + + :return: The codec identifier of the encoder + +--------------------- + +.. function:: enum obs_encoder_type obs_encoder_get_type(const obs_encoder_t *encoder) + enum obs_encoder_type obs_get_encoder_type(const char *id) + + :return: The encoder type: OBS_ENCODER_VIDEO or OBS_ENCODER_AUDIO + +--------------------- + +.. function:: void obs_encoder_set_scaled_size(obs_encoder_t *encoder, uint32_t width, uint32_t height) + + Sets the scaled resolution for a video encoder. Set width and height to 0 + to disable scaling. If the encoder is active, this function will trigger + a warning, and do nothing. + +--------------------- + +.. function:: uint32_t obs_encoder_get_width(const obs_encoder_t *encoder) + uint32_t obs_encoder_get_height(const obs_encoder_t *encoder) + + :return: The width/height of a video encoder's encoded image + +--------------------- + +.. function:: uint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder) + + :return: The sample rate of an audio encoder's audio data + +--------------------- + +.. function:: void obs_encoder_set_preferred_video_format(obs_encoder_t *encoder, enum video_format format) + enum video_format obs_encoder_get_preferred_video_format(const obs_encoder_t *encoder) + + + Sets the preferred video format for a video encoder. If the encoder can use + the format specified, it will force a conversion to that format if the + obs output format does not match the preferred format. + + If the format is set to VIDEO_FORMAT_NONE, will revert to the default + functionality of converting only when absolutely necessary. + +--------------------- + +.. function:: obs_data_t *obs_encoder_defaults(const char *id) + obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder) + + :return: An incremented reference to the encoder's default settings + +--------------------- + +.. function:: obs_properties_t *obs_encoder_properties(const obs_encoder_t *encoder) + obs_properties_t *obs_get_encoder_properties(const char *id) + + Use these functions to get the properties of an encoder or encoder + type. Properties are optionally used (if desired) to automatically + generate user interface widgets to allow users to update settings. + + :return: The properties list for a specific existing encoder. Free + with :c:func:`obs_properties_destroy()` + +--------------------- + +.. function:: void obs_encoder_update(obs_encoder_t *encoder, obs_data_t *settings) + + Updates the settings for this encoder context. + +--------------------- + +.. function:: obs_data_t *obs_encoder_get_settings(const obs_encoder_t *encoder) + + :return: An incremented reference to the encoder's settings + +--------------------- + +.. function:: signal_handler_t *obs_encoder_get_signal_handler(const obs_encoder_t *encoder) + + :return: The signal handler of the encoder + +--------------------- + +.. function:: proc_handler_t *obs_encoder_get_proc_handler(const obs_encoder_t *encoder) + + :return: The procedure handler of the encoder + +--------------------- + +.. function:: bool obs_encoder_get_extra_data(const obs_encoder_t *encoder, uint8_t **extra_data, size_t *size) + + Gets extra data (headers) associated with this encoder. + + :return: *true* if successful, *false* if no extra data associated + with this encoder + +--------------------- + +.. function:: void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video) + void obs_encoder_set_audio(obs_encoder_t *encoder, audio_t *audio) + + Sets the video/audio handler to use with this video/audio encoder. + This is used to capture the raw video/audio data. + +--------------------- + +.. function:: video_t *obs_encoder_video(const obs_encoder_t *encoder) + audio_t *obs_encoder_audio(const obs_encoder_t *encoder) + + :return: The video/audio handler associated with this encoder, or + *NULL* if none or not a matching encoder type + +--------------------- + +.. function:: bool obs_encoder_active(const obs_encoder_t *encoder) + + :return: *true* if the encoder is active, *false* otherwise + +--------------------- + + +Functions used by encoders +-------------------------- + +.. function:: void obs_encoder_packet_ref(struct encoder_packet *dst, struct encoder_packet *src) + void obs_encoder_packet_release(struct encoder_packet *packet) + + Adds or releases a reference to an encoder packet. + +.. --------------------------------------------------------------------------- + +.. _libobs/obs-encoder.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-encoder.h diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst new file mode 100644 index 000000000..3746135ca --- /dev/null +++ b/docs/sphinx/reference-frontend-api.rst @@ -0,0 +1,489 @@ +OBS Studio Frontend API +======================= + +The OBS Studio frontend API is the API specific to OBS Studio itself. + +.. code:: cpp + + #include + + +Structures/Enumerations +----------------------- + +.. type:: enum obs_frontend_event + + Specifies a front-end event. Can be one of the following values: + + - **OBS_FRONTEND_EVENT_STREAMING_STARTING** + + Triggered when streaming is starting. + + - **OBS_FRONTEND_EVENT_STREAMING_STARTED** + + Triggered when streaming has successfully started. + + - **OBS_FRONTEND_EVENT_STREAMING_STOPPING** + + Triggered when streaming is stopping. + + - **OBS_FRONTEND_EVENT_STREAMING_STOPPED** + + Triggered when streaming has fully stopped. + + - **OBS_FRONTEND_EVENT_RECORDING_STARTING** + + Triggered when recording is starting. + + - **OBS_FRONTEND_EVENT_RECORDING_STARTED** + + Triggered when recording has successfully started. + + - **OBS_FRONTEND_EVENT_RECORDING_STOPPING** + + Triggered when recording is stopping. + + - **OBS_FRONTEND_EVENT_RECORDING_STOPPED** + + Triggered when recording has fully stopped. + + - **OBS_FRONTEND_EVENT_SCENE_CHANGED** + + Triggered when the current scene has changed. + + - **OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED** + + Triggered when a scenes has been added/removed/reordered by the + user. + + - **OBS_FRONTEND_EVENT_TRANSITION_CHANGED** + + Triggered when the current transition has changed by the user. + + - **OBS_FRONTEND_EVENT_TRANSITION_STOPPED** + + Triggered when a transition has completed. + + - **OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED** + + Triggered when the user adds/removes transitions. + + - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED** + + Triggered when the user has changed the current scene collection. + + - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED** + + Triggered when the user has added/removed/renamed scene + collections. + + - **OBS_FRONTEND_EVENT_PROFILE_CHANGED** + + Triggered when the user has changed the current profile. + + - **OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED** + + Triggered when the user has added/removed/renamed profiles. + + - **OBS_FRONTEND_EVENT_EXIT** + + Triggered when the program is about to exit. + + - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING** + + Triggered when the replay buffer is starting. + + - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED** + + Triggered when the replay buffer has successfully started. + + - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING** + + Triggered when the replay buffer is stopping. + + - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED** + + Triggered when the replay buffer has fully stopped. + + - **OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED** + + Triggered when the user has turned on studio mode. + + - **OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED** + + Triggered when the user has turned off studio mode. + + - **OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED** + + Triggered when the current preview scene has changed in studio + mode. + + - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP** + + Triggered when a scene collection has been completely unloaded, and + the program is either about to load a new scene collection, or the + program is about to exit. + +.. type:: struct obs_frontend_source_list + + - DARRAY(obs_source_t*) **sources** + + Example usage: + +.. code:: cpp + + struct obs_frontend_source_list scenes; + + obs_frontend_get_scenes(&scenes); + + for (size_t i = 0; i < scenes.num; i++) { + obs_source_t *source = scenes.sources.array[i]; + + [...] + } + + obs_frontend_source_list_free(&scenes); + +.. type:: typedef void (*obs_frontend_cb)(void *private_data) + + Frontend tool menu callback. + +.. type:: typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data) + + Frontend event callback. + +.. type:: typedef void (*obs_frontend_save_cb)(obs_data_t *save_data, bool saving, void *private_data) + + Frontend save/load callback. + +.. type:: typedef bool (*obs_frontend_translate_ui_cb)(const char *text, const char **out) + + Translation callback. + + +Functions +--------- + +.. function:: void obs_frontend_source_list_free(struct obs_frontend_source_list *source_list) + + Releases sources within a source list and frees the list. + + :param source_list: Source list to free. + +--------------------------------------- + +.. function:: void *obs_frontend_get_main_window(void) + + :return: The QMainWindow pointer to the OBS Studio window. + +--------------------------------------- + +.. function:: void *obs_frontend_get_main_window_handle(void) + + :return: The native window handle of the OBS Studio window. + +--------------------------------------- + +.. function:: char **obs_frontend_get_scene_names(void) + + :return: The scene name list, ending with NULL. The list is stored + within one contiguous segment of memory, so freeing the + returned pointer with :c:func:`bfree()` will free the entire + list. + +--------------------------------------- + +.. function:: void obs_frontend_get_scenes(struct obs_frontend_source_list *sources) + + :param sources: Pointer to a :c:type:`obs_frontend_source_list` + structure to receive the list of + reference-incremented scenes. Release with + :c:func:`obs_frontend_source_list_free`. + +--------------------------------------- + +.. function:: obs_source_t *obs_frontend_get_current_scene(void) + + :return: A new reference to the currently active scene. + +--------------------------------------- + +.. function:: void obs_frontend_set_current_scene(obs_source_t *scene) + + :param scene: The scene to set as the current scene. + +--------------------------------------- + +.. function:: void obs_frontend_get_transitions(struct obs_frontend_source_list *sources) + + :param sources: Pointer to a :c:type:`obs_frontend_source_list` + structure to receive the list of + reference-incremented transitions. Release with + :c:func:`obs_frontend_source_list_free`. + +--------------------------------------- + +.. function:: obs_source_t *obs_frontend_get_current_transition(void) + + :return: A new reference to the currently active transition. + +--------------------------------------- + +.. function:: void obs_frontend_set_current_transition(obs_source_t *transition) + + :param transition: The transition to set as the current transition. + +--------------------------------------- + +.. function:: char **obs_frontend_get_scene_collections(void) + + :return: The list of profile names, ending with NULL. The list is + stored within one contiguous segment of memory, so freeing + the returned pointer with :c:func:`bfree()` will free the + entire list. + +--------------------------------------- + +.. function:: char *obs_frontend_get_current_scene_collection(void) + + :return: A new pointer to the current scene collection name. Free + with :c:func:`bfree()`. + +--------------------------------------- + +.. function:: void obs_frontend_set_current_scene_collection(const char *collection) + + :param profile: Name of the scene collection to activate. + +--------------------------------------- + +.. function:: char **obs_frontend_get_profiles(void) + + :return: The list of profile names, ending with NULL. The list is + stored within one contiguous segment of memory, so freeing + the returned pointer with :c:func:`bfree()` will free the + entire list. + +--------------------------------------- + +.. function:: char *obs_frontend_get_current_profile(void) + + :return: A new pointer to the current profile name. Free with + :c:func:`bfree()`. + +--------------------------------------- + +.. function:: void obs_frontend_set_current_profile(const char *profile) + + :param profile: Name of the profile to activate. + +--------------------------------------- + +.. function:: void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) + + Adds a callback that will be called when a frontend event occurs. + See :c:type:`obs_frontend_event` on what sort of events can be + triggered. + + :param callback: Callback to use when a frontend event occurs. + :param private_data: Private data associated with the callback. + +--------------------------------------- + +.. function:: void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data) + + Removes an event callback. + + :param callback: Callback to remove. + :param private_data: Private data associated with the callback. + +--------------------------------------- + +.. function:: void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data) + + Adds a callback that will be called when the current scene collection + is being saved/loaded. + + :param callback: Callback to use when saving/loading a scene + collection. + :param private_data: Private data associated with the callback. + +--------------------------------------- + +.. function:: void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data) + + Removes a save/load callback. + + :param callback: Callback to remove. + :param private_data: Private data associated with the callback. + +--------------------------------------- + +.. function:: void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data) + + Adds a callback that will be called right before a scene collection + is loaded. Useful if you + + :param callback: Callback to use when pre-loading. + :param private_data: Private data associated with the callback. + +--------------------------------------- + +.. function:: void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data) + + Removes a pre-load callback. + + :param callback: Callback to remove. + :param private_data: Private data associated with the callback. + +--------------------------------------- + +.. function:: void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate) + + Pushes a UI translation callback. This allows a front-end plugin to + intercept when Qt is automatically generating translating data. + Typically this is just called with obs_module_get_string. + + :param translate: The translation callback to use. + +--------------------------------------- + +.. function:: void obs_frontend_pop_ui_translation(void) + + Pops the current UI translation callback. + +--------------------------------------- + +.. function:: void obs_frontend_streaming_start(void) + + Starts streaming. + +--------------------------------------- + +.. function:: void obs_frontend_streaming_stop(void) + + Stops streaming. + +--------------------------------------- + +.. function:: bool obs_frontend_streaming_active(void) + + :return: *true* if streaming active, *false* otherwise. + +--------------------------------------- + +.. function:: void obs_frontend_recording_start(void) + + Starts recording. + +--------------------------------------- + +.. function:: void obs_frontend_recording_stop(void) + + Stops recording. + +--------------------------------------- + +.. function:: bool obs_frontend_recording_active(void) + + :return: *true* if recording active, *false* otherwise. + +--------------------------------------- + +.. function:: void obs_frontend_replay_buffer_start(void) + + Starts replay buffer. + +--------------------------------------- + +.. function:: void obs_frontend_replay_buffer_stop(void) + + Stops replay buffer. + +--------------------------------------- + +.. function:: void obs_frontend_replay_buffer_save(void) + + Saves a replay if the replay buffer is active. + +--------------------------------------- + +.. function:: bool obs_frontend_replay_buffer_active(void) + + :return: *true* if replay buffer active, *false* otherwise. + +--------------------------------------- + +.. function:: void obs_frontend_save(void) + + Saves the current scene collection. + +--------------------------------------- + +.. function:: obs_output_t *obs_frontend_get_streaming_output(void) + + :return: A new reference to the current streaming output. + +--------------------------------------- + +.. function:: obs_output_t *obs_frontend_get_recording_output(void) + + :return: A new reference to the current srecording output. + +--------------------------------------- + +.. function:: obs_output_t *obs_frontend_get_replay_buffer_output(void) + + :return: A new reference to the current replay buffer output. + +--------------------------------------- + +.. function:: void obs_frontend_set_streaming_service(obs_service_t *service) + + Sets the current streaming service to stream with. + + :param service: The streaming service to set. + +--------------------------------------- + +.. function:: obs_service_t *obs_frontend_get_streaming_service(void) + + :return: A new reference to the current streaming service object. + +--------------------------------------- + +.. function:: void obs_frontend_save_streaming_service(void) + + Saves the current streaming service data. + +--------------------------------------- + +.. function:: bool obs_frontend_preview_program_mode_active(void) + + :return: *true* if studio mode is active, *false* otherwise. + +--------------------------------------- + +.. function:: void obs_frontend_set_preview_program_mode(bool enable) + + Activates/deactivates studio mode. + + :param enable: *true* to activate studio mode, *false* to deactivate + studio mode. + +--------------------------------------- + +.. function:: obs_source_t *obs_frontend_get_current_preview_scene(void) + + :return: A new reference to the current preview scene if studio mode + is active, or the current scene if studio mode is not + active. + +--------------------------------------- + +.. function:: void obs_frontend_set_current_preview_scene(obs_source_t *scene) + + Sets the current preview scene in studio mode, or the currently + active scene if not in studio mode. + + :param scene: The scene to set as the current preview. diff --git a/docs/sphinx/reference-libobs-callback.rst b/docs/sphinx/reference-libobs-callback.rst new file mode 100644 index 000000000..40e327193 --- /dev/null +++ b/docs/sphinx/reference-libobs-callback.rst @@ -0,0 +1,288 @@ +Callback API Reference (libobs/callback) +======================================== + + +Calldata +-------- + +The :c:type:`calldata_t` object is used to pass parameters from signal +handlers or to procedure handlers. + +.. type:: calldata_t + +--------------------- + +.. function:: void calldata_init(calldata_t *data) + + Initializes a calldata structure (zeroes it). + + :param data: Calldata structure + +--------------------- + +.. function:: void calldata_free(calldata_t *data) + + Frees a calldata structure. + + :param data: Calldata structure + +--------------------- + +.. function:: void calldata_set_int(calldata_t *data, const char *name, long long val) + + Sets an integer parameter. + + :param data: Calldata structure + :param name: Parameter name + :param val: Integer value + +--------------------- + +.. function:: void calldata_set_float(calldata_t *data, const char *name, double val) + + Sets a floating point parameter. + + :param data: Calldata structure + :param name: Parameter name + :param val: Floating point value + +--------------------- + +.. function:: void calldata_set_bool(calldata_t *data, const char *name, bool val) + + Sets a boolean parameter. + + :param data: Calldata structure + :param name: Parameter name + :param val: Boolean value + +--------------------- + +.. function:: void calldata_set_ptr(calldata_t *data, const char *name, void *ptr) + + Sets a pointer parameter. + + :param data: Calldata structure + :param name: Parameter name + :param val: Pointer value + +--------------------- + +.. function:: void calldata_set_string(calldata_t *data, const char *name, const char *str) + + Sets a string parameter. + + :param data: Calldata structure + :param name: Parameter name + :param val: String + +--------------------- + +.. function:: long long calldata_int(const calldata_t *data, const char *name) + + Gets an integer parameter. + + :param data: Calldata structure + :param name: Parameter name + :return: Integer value + +--------------------- + +.. function:: double calldata_float(const calldata_t *data, const char *name) + + Gets a floating point parameter. + + :param data: Calldata structure + :param name: Parameter name + :return: Floating point value + +--------------------- + +.. function:: bool calldata_bool(const calldata_t *data, const char *name) + + Gets a boolean parameter. + + :param data: Calldata structure + :param name: Parameter name + :return: Boolean value + +--------------------- + +.. function:: void *calldata_ptr(const calldata_t *data, const char *name) + + Gets a pointer parameter. + + :param data: Calldata structure + :param name: Parameter name + :return: Pointer value + +--------------------- + +.. function:: const char *calldata_string(const calldata_t *data, const char *name) + + Gets a string parameter. + + :param data: Calldata structure + :param name: Parameter name + :return: String value + +--------------------- + + +Signals +------- + +Signals are used for all event-based callbacks. + +.. code:: cpp + + #include + +.. type:: signal_handler_t + +--------------------- + +.. type:: typedef void (*signal_callback_t)(void *data, calldata_t *cd) + + Signal callback. + + :param data: Private data passed to this callback + :param cd: Calldata object + +--------------------- + +.. function:: signal_handler_t *signal_handler_create(void) + + Creates a new signal handler object. + + :return: A new signal handler object + +--------------------- + +.. function:: void signal_handler_destroy(signal_handler_t *handler) + + Destroys a signal handler. + + :param handler: Signal handler object + +--------------------- + +.. function:: bool signal_handler_add(signal_handler_t *handler, const char *signal_decl) + + Adds a signal to a signal handler. + + :param handler: Signal handler object + :param signal_decl: Signal declaration string + +--------------------- + +.. function:: bool signal_handler_add_array(signal_handler_t *handler, const char **signal_decls) + + Adds multiple signals to a signal handler. + + :param handler: Signal handler object + :param signal_decls: An array of signal declaration strings, + terminated by *NULL* + +--------------------- + +.. function:: void signal_handler_connect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data) + + Connect a callback to a signal on a signal handler. + + :param handler: Signal handler object + :param callback: Signal callback + :param data: Private data passed the callback + +--------------------- + +.. function:: void signal_handler_connect_ref(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data) + + Connect a callback to a signal on a signal handler, and increments + the handler's internal reference counter, preventing it from being + destroyed until the signal has been disconnected. + + :param handler: Signal handler object + :param callback: Signal callback + :param data: Private data passed the callback + +--------------------- + +.. function:: void signal_handler_disconnect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data) + + Disconnects a callback from a signal on a signal handler. + + :param handler: Signal handler object + :param callback: Signal callback + :param data: Private data passed the callback + +--------------------- + +.. function:: void signal_handler_signal(signal_handler_t *handler, const char *signal, calldata_t *params) + + Triggers a signal, calling all connected callbacks. + + :param handler: Signal handler object + :param signal: Name of signal to trigger + :param params: Parameters to pass to the signal + +--------------------- + + +Procedure Handlers +------------------ + +Procedure handlers are used to call functions without having to have +direct access to declarations or callback pointers. + +.. code:: cpp + + #include + +.. type:: proc_handler_t + +--------------------- + +.. type:: typedef void (*proc_handler_proc_t)(void *data, calldata_t *cd) + + Procedure handler callback. + + :param data: Private data passed to this callback + :param cd: Calldata object + +--------------------- + +.. function:: proc_handler_t *proc_handler_create(void) + + Creates a new procedure handler. + + :return: A new procedure handler object + +--------------------- + +.. function:: void proc_handler_destroy(proc_handler_t *handler) + + Destroys a procedure handler object. + + :param handler: Procedure handler object + +--------------------- + +.. function:: void proc_handler_add(proc_handler_t *handler, const char *decl_string, proc_handler_proc_t proc, void *data) + + Adds a procedure to a procedure handler. + + :param handler: Procedure handler object + :param decl_string: Procedure declaration string + :param proc: Procedure callback + :param data: Private data to pass to the callback + +--------------------- + +.. function:: bool proc_handler_call(proc_handler_t *handler, const char *name, calldata_t *params) + + Calls a procedure within the procedure handler. + + :param handler: Procedure handler object + :param name: Name of procedure to call + :param params: Calldata structure to pass to the procedure diff --git a/docs/sphinx/reference-libobs-graphics-axisang.rst b/docs/sphinx/reference-libobs-graphics-axisang.rst new file mode 100644 index 000000000..5414cbadd --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-axisang.rst @@ -0,0 +1,65 @@ +Axis Angle +========== + +Provides a helper structure for conversion to quaternions. + +.. code:: cpp + + #include + +.. type:: struct axisang +.. member:: float axisang.x + + X axis + +.. member:: float axisang.y + + Y axis + +.. member:: float axisang.z + + Z axis + +.. member:: float axisang.w + + Angle + +.. member:: float axisang.ptr[4] + +--------------------- + +.. function:: void axisang_zero(struct axisang *dst) + + Zeroes the axis angle. + + :param dst: Axis angle + +--------------------- + +.. function:: void axisang_copy(struct axisang *dst, struct axisang *aa) + + Copies an axis angle. + + :param dst: Axis angle to copy to + :param aa: Axis angle to copy from + +--------------------- + +.. function:: void axisang_set(struct axisang *dst, float x, float y, float z, float w) + + Sets an axis angle. + + :param dst: Axis angle to set + :param x: X axis + :param y: Y axis + :param z: Z axis + :param w: Angle + +--------------------- + +.. function:: void axisang_from_quat(struct axisang *dst, const struct quat *q) + + Creates an axis angle from a quaternion. + + :param dst: Axis angle destination + :param q: Quaternion to convert diff --git a/docs/sphinx/reference-libobs-graphics-effects.rst b/docs/sphinx/reference-libobs-graphics-effects.rst new file mode 100644 index 000000000..e7c0ec389 --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-effects.rst @@ -0,0 +1,400 @@ +Effects (Shaders) +================= + +Effects are a single collection of related shaders. They're used for +easily writing vertex and pixel shaders together all in the same file in +HLSL format. + +.. code:: cpp + + #include + +.. type:: typedef struct gs_effect gs_effect_t + + Effect object. + +.. type:: typedef struct gs_effect_technique gs_technique_t + + Technique object. + +.. type:: typedef struct gs_effect_param gs_eparam_t + + Effect parameter object. + + +--------------------- + +.. function:: gs_effect_t *gs_effect_create_from_file(const char *file, char **error_string) + + Creates an effect from file. + + :param file: Path to the effect file + :param error_string: Receives a pointer to the error string, which + must be freed with :c:func:`bfree()`. If + *NULL*, this parameter is ignored. + :return: The effect object, or *NULL* on error + +--------------------- + +.. function:: gs_effect_t *gs_effect_create(const char *effect_string, const char *filename, char **error_string) + + Creates an effect from a string. + + :param effect_String: Effect string + :param error_string: Receives a pointer to the error string, which + must be freed with :c:func:`bfree()`. If + *NULL*, this parameter is ignored. + :return: The effect object, or *NULL* on error + +--------------------- + +.. function:: void gs_effect_destroy(gs_effect_t *effect) + + Destroys the effect + + :param effect: Effect object + +--------------------- + +.. function:: gs_technique_t *gs_effect_get_technique(const gs_effect_t *effect, const char *name) + + Gets a technique of the effect. + + :param effect: Effect object + :param name: Name of the technique + :return: Technique object, or *NULL* if not found + +--------------------- + +.. function:: gs_technique_t *gs_effect_get_current_technique(const gs_effect_t *effect) + + Gets the current active technique of the effect. + + :param effect: Effect object + :return: Technique object, or *NULL* if none currently active + +--------------------- + +.. function:: size_t gs_technique_begin(gs_technique_t *technique) + + Begins a technique. + + :param technique: Technique object + :return: Number of passes this technique uses + +--------------------- + +.. function:: void gs_technique_end(gs_technique_t *technique) + + Ends a technique. Make sure all active passes have been ended before + calling. + + :param technique: Technique object + +--------------------- + +.. function:: bool gs_technique_begin_pass(gs_technique_t *technique, size_t pass) + + Begins a pass. Automatically loads the vertex/pixel shaders + associated with this pass. Draw after calling this function. + + :param technique: Technique object + :param pass: Pass index + :return: *true* if the pass is valid, *false* otherwise + +--------------------- + +.. function:: bool gs_technique_begin_pass_by_name(gs_technique_t *technique, const char *name) + + Begins a pass by its name if the pass has a name. Automatically + loads the vertex/pixel shaders associated with this pass. Draw after + calling this function. + + :param technique: Technique object + :param name: Name of the pass + :return: *true* if the pass is valid, *false* otherwise + +--------------------- + +.. function:: void gs_technique_end_pass(gs_technique_t *technique) + + Ends a pass. + + :param technique: Technique object + +--------------------- + +.. function:: size_t gs_effect_get_num_params(const gs_effect_t *effect) + + Gets the number of parameters associated with the effect. + + :param effect: Effect object + :return: Number of parameters the effect has + +--------------------- + +.. function:: gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param) + + Gets a parameter of an effect by its index. + + :param effect: Effect object + :param param: Parameter index + :return: The effect parameter object, or *NULL* if index + invalid + +--------------------- + +.. function:: gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, const char *name) + + Gets parameter of an effect by its name. + + :param effect: Effect object + :param name: Name of the parameter + :return: The effect parameter object, or *NULL* if not found + +--------------------- + +.. function:: size_t gs_param_get_num_annotations(const gs_eparam_t *param) + + Gets the number of annotations associated with the parameter. + + :param param: Param object + :return: Number of annotations the param has + +--------------------- + +.. function:: gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, size_t annotation) + + Gets an annotation of a param by its index. + + :param param: Param object + :param param: Annotation index + :return: The effect parameter object (annotation), or *NULL* if index + invalid + +--------------------- + +.. function:: gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *pardam, const char *annotation) + + Gets parameter of an effect by its name. + + :param param: Param object + :param name: Name of the annotation + :return: The effect parameter object (annotation), or *NULL* if not found + +--------------------- + +.. function:: bool gs_effect_loop(gs_effect_t *effect, const char *name) + + Helper function that automatically begins techniques/passes. + + :param effect: Effect object + :param name: Name of the technique to execute + :return: *true* to draw, *false* when complete + + Here is an example of how this function is typically used: + +.. code:: cpp + + for (gs_effect_loop(effect, "my_technique")) { + /* perform drawing here */ + [...] + } + +--------------------- + +.. function:: gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect) + + Gets the view/projection matrix parameter ("viewproj") of the effect. + + :param effect: Effect object + :return: The view/projection matrix parameter of the effect + +--------------------- + +.. function:: gs_eparam_t *gs_effect_get_world_matrix(const gs_effect_t *effect) + + Gets the world matrix parameter ("world") of the effect. + + :param effect: Effect object + :return: The world matrix parameter of the effect + +--------------------- + +.. function:: void gs_effect_get_param_info(const gs_eparam_t *param, struct gs_effect_param_info *info) + + Gets information about an effect parameter. + + :param param: Effect parameter + :param info: Pointer to receive the data + + Relevant data types used with this function: + +.. code:: cpp + + enum gs_shader_param_type { + GS_SHADER_PARAM_UNKNOWN, + GS_SHADER_PARAM_BOOL, + GS_SHADER_PARAM_FLOAT, + GS_SHADER_PARAM_INT, + GS_SHADER_PARAM_STRING, + GS_SHADER_PARAM_VEC2, + GS_SHADER_PARAM_VEC3, + GS_SHADER_PARAM_VEC4, + GS_SHADER_PARAM_INT2, + GS_SHADER_PARAM_INT3, + GS_SHADER_PARAM_INT4, + GS_SHADER_PARAM_MATRIX4X4, + GS_SHADER_PARAM_TEXTURE, + }; + + struct gs_effect_param_info { + const char *name; + enum gs_shader_param_type type; + } + +--------------------- + +.. function:: void gs_effect_set_bool(gs_eparam_t *param, bool val) + + Sets a boolean parameter. + + :param param: Effect parameter + :param val: Boolean value + +--------------------- + +.. function:: void gs_effect_set_float(gs_eparam_t *param, float val) + + Sets a floating point parameter. + + :param param: Effect parameter + :param val: Floating point value + +--------------------- + +.. function:: void gs_effect_set_int(gs_eparam_t *param, int val) + + Sets a integer parameter. + + :param param: Effect parameter + :param val: Integer value + +--------------------- + +.. function:: void gs_effect_set_matrix4(gs_eparam_t *param, const struct matrix4 *val) + + Sets a matrix parameter. + + :param param: Effect parameter + :param val: Matrix + +--------------------- + +.. function:: void gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val) + + Sets a 2-component vector parameter. + + :param param: Effect parameter + :param val: Vector + +--------------------- + +.. function:: void gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val) + + Sets a 3-component vector parameter. + + :param param: Effect parameter + :param val: Vector + +--------------------- + +.. function:: void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val) + + Sets a 4-component vector parameter. + + :param param: Effect parameter + :param val: Vector + +--------------------- + +.. function:: void gs_effect_set_color(gs_eparam_t *param, uint32_t argb) + + Convenience function for setting a color value via an integer value. + + :param param: Effect parameter + :param argb: Integer color value (i.e. hex value would be + 0xAARRGGBB) + +--------------------- + +.. function:: void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val) + + Sets a texture parameter. + + :param param: Effect parameter + :param val: Texture + +--------------------- + +.. function:: void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size) + + Sets a parameter with data manually. + + :param param: Effect parameter + :param val: Pointer to data + :param size: Size of data + +--------------------- + +.. function:: void gs_effect_set_default(gs_eparam_t *param) + + Sets the parameter to its default value + + :param: Effect parameter + +--------------------- + +.. function:: void gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler) + + Manually changes the sampler for an effect parameter the next time + it's used. + + :param param: Effect parameter + :param sampler: Sampler state object + +--------------------- + +.. function:: void *gs_effect_get_val(gs_eparam_t *param) + + Returns a copy of the param's current value. + + :param param: Effect parameter + :return: A pointer to the copied byte value of the param's current value. Freed with :c:func:`bfree()`. + +--------------------- + +.. function:: void gs_effect_get_default_val(gs_eparam_t *param) + + Returns a copy of the param's default value. + + :param param: Effect parameter + :return: A pointer to the copied byte value of the param's default value. Freed with :c:func:`bfree()`. + +--------------------- + +.. function:: size_t gs_effect_get_val_size(gs_eparam_t *param) + + Returns the size in bytes of the param's current value. + + :param param: Effect parameter + :return: The size in bytes of the param's current value. + +--------------------- + +.. function:: size_t gs_effect_get_default_val_size(gs_eparam_t *param) + + Returns the size in bytes of the param's default value. + + :param param: Effect parameter + :return: The size in bytes of the param's default value. diff --git a/docs/sphinx/reference-libobs-graphics-graphics.rst b/docs/sphinx/reference-libobs-graphics-graphics.rst new file mode 100644 index 000000000..d2207508a --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-graphics.rst @@ -0,0 +1,1462 @@ +Core Graphics API +================= + +.. code:: cpp + + #include + + +Graphics Enumerations +--------------------- + +.. type:: enum gs_draw_mode + + Draw mode. Can be one of the following values: + + - GS_POINTS - Draws points + - GS_LINES - Draws individual lines + - GS_LINESTRIP - Draws a line strip + - GS_TRIS - Draws individual triangles + - GS_TRISTRIP - Draws a triangle strip + +.. type:: enum gs_color_format + + Color format. Can be one of the following values: + + - GS_UNKNOWN - Unknown format + - GS_A8 - 8 bit alpha channel only + - GS_R8 - 8 bit red channel only + - GS_RGBA - RGBA, 8 bits per channel + - GS_BGRX - BGRX, 8 bits per channel + - GS_BGRA - BGRA, 8 bits per channel + - GS_R10G10B10A2 - RGBA, 10 bits per channel except alpha, which is 2 + bits + - GS_RGBA16 - RGBA, 16 bits per channel + - GS_R16 - 16 bit red channel only + - GS_RGBA16F - RGBA, 16 bit floating point per channel + - GS_RGBA32F - RGBA, 32 bit floating point per channel + - GS_RG16F - 16 bit floating point red and green channels only + - GS_RG32F - 32 bit floating point red and green channels only + - GS_R16F - 16 bit floating point red channel only + - GS_R32F - 32 bit floating point red channel only + - GS_DXT1 - Compressed DXT1 + - GS_DXT3 - Compressed DXT3 + - GS_DXT5 - Compressed DXT5 + +.. type:: enum gs_zstencil_format + + Z-Stencil buffer format. Can be one of the following values: + + - GS_ZS_NONE - No Z-stencil buffer + - GS_Z16 - 16 bit Z buffer + - GS_Z24_S8 - 24 bit Z buffer, 8 bit stencil + - GS_Z32F - 32 bit floating point Z buffer + - GS_Z32F_S8X24 - 32 bit floating point Z buffer, 8 bit stencil + +.. type:: enum gs_index_type + + Index buffer type. Can be one of the following values: + + - GS_UNSIGNED_SHORT - 16 bit index + - GS_UNSIGNED_LONG - 32 bit index + +.. type:: enum gs_cull_mode + + Cull mode. Can be one of the following values: + + - GS_BACK - Cull back faces + - GS_FRONT - Cull front faces + - GS_NEITHER - Cull neither + +.. type:: enum gs_blend_type + + Blend type. Can be one of the following values: + + - GS_BLEND_ZERO + - GS_BLEND_ONE + - GS_BLEND_SRCCOLOR + - GS_BLEND_INVSRCCOLOR + - GS_BLEND_SRCALPHA + - GS_BLEND_INVSRCALPHA + - GS_BLEND_DSTCOLOR + - GS_BLEND_INVDSTCOLOR + - GS_BLEND_DSTALPHA + - GS_BLEND_INVDSTALPHA + - GS_BLEND_SRCALPHASAT + +.. type:: enum gs_depth_test + + Depth test type. Can be one of the following values: + + - GS_NEVER + - GS_LESS + - GS_LEQUAL + - GS_EQUAL + - GS_GEQUAL + - GS_GREATER + - GS_NOTEQUAL + - GS_ALWAYS + +.. type:: enum gs_stencil_side + + Stencil side. Can be one of the following values: + + - GS_STENCIL_FRONT=1 + - GS_STENCIL_BACK + - GS_STENCIL_BOTH + +.. type:: enum gs_stencil_op_type + + Stencil operation type. Can be one of the following values: + + - GS_KEEP + - GS_ZERO + - GS_REPLACE + - GS_INCR + - GS_DECR + - GS_INVERT + +.. type:: enum gs_cube_sides + + Cubemap side. Can be one of the following values: + + - GS_POSITIVE_X + - GS_NEGATIVE_X + - GS_POSITIVE_Y + - GS_NEGATIVE_Y + - GS_POSITIVE_Z + - GS_NEGATIVE_Z + +.. type:: enum gs_sample_filter + + Sample filter type. Can be one of the following values: + + - GS_FILTER_POINT + - GS_FILTER_LINEAR + - GS_FILTER_ANISOTROPIC + - GS_FILTER_MIN_MAG_POINT_MIP_LINEAR + - GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT + - GS_FILTER_MIN_POINT_MAG_MIP_LINEAR + - GS_FILTER_MIN_LINEAR_MAG_MIP_POINT + - GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR + - GS_FILTER_MIN_MAG_LINEAR_MIP_POINT + +.. type:: enum gs_address_mode + + Address mode. Can be one of the following values: + + - GS_ADDRESS_CLAMP + - GS_ADDRESS_WRAP + - GS_ADDRESS_MIRROR + - GS_ADDRESS_BORDER + - GS_ADDRESS_MIRRORONCE + +.. type:: enum gs_texture_type + + Texture type. Can be one of the following values: + + - GS_TEXTURE_2D + - GS_TEXTURE_3D + - GS_TEXTURE_CUBE + + +Graphics Structures +------------------- + +.. type:: struct gs_monitor_info +.. member:: int gs_monitor_info.rotation_degrees +.. member:: long gs_monitor_info.x +.. member:: long gs_monitor_info.y +.. member:: long gs_monitor_info.cx +.. member:: long gs_monitor_info.cy + +--------------------- + +.. type:: struct gs_tvertarray +.. member:: size_t gs_tvertarray.width +.. member:: void *gs_tvertarray.array + +--------------------- + +.. type:: struct gs_vb_data +.. member:: size_t gs_vb_data.num +.. member:: struct vec3 *gs_vb_data.points +.. member:: struct vec3 *gs_vb_data.normals +.. member:: struct vec3 *gs_vb_data.tangents +.. member:: uint32_t *gs_vb_data.colors +.. member:: size_t gs_vb_data.num_tex +.. member:: struct gs_tvertarray *gs_vb_data.tvarray + +--------------------- + +.. type:: struct gs_sampler_info +.. member:: enum gs_sample_filter gs_sampler_info.filter +.. member:: enum gs_address_mode gs_sampler_info.address_u +.. member:: enum gs_address_mode gs_sampler_info.address_v +.. member:: enum gs_address_mode gs_sampler_info.address_w +.. member:: int gs_sampler_info.max_anisotropy +.. member:: uint32_t gs_sampler_info.border_color + +--------------------- + +.. type:: struct gs_display_mode +.. member:: uint32_t gs_display_mode.width +.. member:: uint32_t gs_display_mode.height +.. member:: uint32_t gs_display_mode.bits +.. member:: uint32_t gs_display_mode.freq + +--------------------- + +.. type:: struct gs_rect +.. member:: int gs_rect.x +.. member:: int gs_rect.y +.. member:: int gs_rect.cx +.. member:: int gs_rect.cy + +--------------------- + +.. type:: struct gs_window + + A window structure. This structure is used with a native widget. + +.. member:: void *gs_window.hwnd + + (Windows only) an HWND widget. + +.. member:: id gs_window.view + + (Mac only) A view ID. + +.. member:: uint32_t gs_window.id + void* gs_window.display + + (Linux only) Window ID and display + +--------------------- + +.. type:: struct gs_init_data + + Swap chain initialization data. + +.. member:: struct gs_window gs_init_data.window +.. member:: uint32_t gs_init_data.cx +.. member:: uint32_t gs_init_data.cy +.. member:: uint32_t gs_init_data.num_backbuffers +.. member:: enum gs_color_format gs_init_data.format +.. member:: enum gs_zstencil_format gs_init_data.zsformat +.. member:: uint32_t gs_init_data.adapter + +--------------------- + + +Initialization Functions +------------------------ + +.. function:: void gs_enum_adapters(bool (*callback)(void *param, const char *name, uint32_t id), void *param) + + Enumerates adapters (this really only applies on windows). + + :param callback: Enumeration callback + :param param: Private data passed to the callback + +--------------------- + +.. function:: int gs_create(graphics_t **graphics, const char *module, uint32_t adapter) + + Creates a graphics context + + :param graphics: Pointer to receive the graphics context + :param module: Module name + :param adapter: Adapter index + :return: Can return one of the following values: + + - GS_SUCCESS + - GS_ERROR_FAIL + - GS_ERROR_MODULE_NOT_FOUND + - GS_ERROR_NOT_SUPPORTED + +--------------------- + +.. function:: void gs_destroy(graphics_t *graphics) + + Destroys a graphics context + + :param graphics: Graphics context + +--------------------- + +.. function:: void gs_enter_context(graphics_t *graphics) + + Enters and locks the graphics context + + :param graphics: Graphics context + +--------------------- + +.. function:: void gs_leave_context(void) + + Leaves and unlocks the graphics context + + :param graphics: Graphics context + +--------------------- + +.. function:: graphics_t *gs_get_context(void) + + :return: The currently locked graphics context for this thread + +--------------------- + + +Matrix Stack Functions +---------------------- + +.. function:: void gs_matrix_push(void) + + Pushes the matrix stack and duplicates the current matrix. + +--------------------- + +.. function:: void gs_matrix_pop(void) + + Pops the current matrix from the matrix stack. + +--------------------- + +.. function:: void gs_matrix_identity(void) + + Sets the current matrix to an identity matrix. + +--------------------- + +.. function:: void gs_matrix_transpose(void) + + Transposes the current matrix. + +--------------------- + +.. function:: void gs_matrix_set(const struct matrix4 *matrix) + + Sets the current matrix. + + :param matrix: The matrix to set + +--------------------- + +.. function:: void gs_matrix_get(struct matrix4 *dst) + + Gets the current matrix + + :param dst: Destination matrix + +--------------------- + +.. function:: void gs_matrix_mul(const struct matrix4 *matrix) + + Multiplies the current matrix + + :param matrix: Matrix to multiply the current stack matrix with + +--------------------- + +.. function:: void gs_matrix_rotquat(const struct quat *rot) + + Multiplies the current matrix with a quaternion + + :param rot: Quaternion to multiple the current matrix stack with + +--------------------- + +.. function:: void gs_matrix_rotaa(const struct axisang *rot) + void gs_matrix_rotaa4f(float x, float y, float z, float angle) + + Multiplies the current matrix with an axis angle + + :param rot: Axis angle to multiple the current matrix stack with + +--------------------- + +.. function:: void gs_matrix_translate(const struct vec3 *pos) + void gs_matrix_translate3f(float x, float y, float z) + + Translates the current matrix + + :param pos: Vector to translate the current matrix stack with + +--------------------- + +.. function:: void gs_matrix_scale(const struct vec3 *scale) + void gs_matrix_scale3f(float x, float y, float z) + + Scales the current matrix + + :param scale: Scale value to scale the current matrix stack with + +--------------------- + + +Draw Functions +-------------- + +.. function:: gs_effect_t *gs_get_effect(void) + + :return: The currently active effect, or *NULL* if none active + +--------------------- + +.. function:: void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, uint32_t height) + + Draws a 2D sprite. Sets the "image" parameter of the current effect + to the texture and renders a quad. + + If width or height is 0, the width or height of the texture will be + used. The flip value specifies whether the texture should be flipped + on the U or V axis with GS_FLIP_U and GS_FLIP_V. + + :param tex: Texture to draw + :param flip: Can be 0 or a bitwise-OR combination of one of the + following values: + + - GS_FLIP_U - Flips the texture horizontally + - GS_FLIP_V - Flips the texture vertically + + :param width: Width + :param height: Height + +--------------------- + +.. function:: void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy) + + Draws a subregion of a 2D sprite. Sets the "image" parameter of the + current effect to the texture and renders a quad. + + :param tex: Texture to draw + :param flip: Can be 0 or a bitwise-OR combination of one of the + following values: + + - GS_FLIP_U - Flips the texture horizontally + - GS_FLIP_V - Flips the texture vertically + + :param x: X value within subregion + :param y: Y value within subregion + :param cx: CX value of subregion + :param cy: CY value of subregion + +--------------------- + +.. function:: void gs_reset_viewport(void) + + Sets the viewport to current swap chain size + +--------------------- + +.. function:: void gs_set_2d_mode(void) + + Sets the projection matrix to a default screen-sized orthographic + mode + +--------------------- + +.. function:: void gs_set_3d_mode(double fovy, double znear, double zfar) + + Sets the projection matrix to a default screen-sized perspective + mode + + :param fovy: Field of view (in degrees) + :param znear: Near plane + :param zfar: Far plane + +--------------------- + +.. function:: void gs_viewport_push(void) + + Pushes/stores the current viewport + +--------------------- + +.. function:: void gs_viewport_pop(void) + + Pops/recalls the last pushed viewport + +--------------------- + +.. function:: void gs_perspective(float fovy, float aspect, float znear, float zfar) + + Sets the projection matrix to a perspective mode + + :param fovy: Field of view (in degrees) + :param aspect: Aspect ratio + :param znear: Near plane + :param zfar: Far plane + +--------------------- + +.. function:: void gs_blend_state_push(void) + + Pushes/stores the current blend state + +--------------------- + +.. function:: void gs_blend_state_pop(void) + + Pops/restores the last blend state + +--------------------- + +.. function:: void gs_reset_blend_state(void) + + Sets the blend state to the default value: source alpha and invert + source alpha. + +--------------------- + + +Swap Chains +----------- + +.. function:: gs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data) + + Creates a swap chain (display view on a native widget) + + :param data: Swap chain initialization data + :return: New swap chain object, or *NULL* if failed + +--------------------- + +.. function:: void gs_swapchain_destroy(gs_swapchain_t *swapchain) + + Destroys a swap chain + +--------------------- + +.. function:: void gs_resize(uint32_t cx, uint32_t cy) + + Resizes the currently active swap chain + + :param cx: New width + :param cy: New height + +--------------------- + +.. function:: void gs_get_size(uint32_t *cx, uint32_t *cy) + + Gets the size of the currently active swap chain + + :param cx: Pointer to receive width + :param cy: Pointer to receive height + +--------------------- + +.. function:: uint32_t gs_get_width(void) + + Gets the width of the currently active swap chain + +--------------------- + +.. function:: uint32_t gs_get_height(void) + + Gets the height of the currently active swap chain + +--------------------- + + +Resource Loading +---------------- + +.. function:: void gs_load_vertexbuffer(gs_vertbuffer_t *vertbuffer) + + Loads a vertex buffer + + :param vertbuffer: Vertex buffer to load, or NULL to unload + +--------------------- + +.. function:: void gs_load_indexbuffer(gs_indexbuffer_t *indexbuffer) + + Loads a index buffer + + :param indexbuffer: Index buffer to load, or NULL to unload + +--------------------- + +.. function:: void gs_load_texture(gs_texture_t *tex, int unit) + + Loads a texture (this is usually not called manually) + + :param tex: Texture to load, or NULL to unload + :param unit: Texture unit to load texture for + +--------------------- + +.. function:: void gs_load_samplerstate(gs_samplerstate_t *samplerstate, int unit) + + Loads a sampler state (this is usually not called manually) + + :param samplerstate: Sampler state to load, or NULL to unload + :param unit: Texture unit to load sampler state for + +--------------------- + +.. function:: void gs_load_swapchain(gs_swapchain_t *swapchain) + + Loads a swapchain + + :param swapchain: Swap chain to load, or NULL to unload + +--------------------- + + +Draw Functions +-------------- + +.. function:: gs_texture_t *gs_get_render_target(void) + + :return: The currently active render target + +--------------------- + +.. function:: gs_zstencil_t *gs_get_zstencil_target(void) + + :return: The currently active Z-stencil target + +--------------------- + +.. function:: void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil) + + Sets the active render target + + :param tex: Texture to set as the active render target + :param zstencil: Z-stencil to use as the active render target + +--------------------- + +.. function:: void gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil) + + Sets a cubemap side as the active render target + + :param cubetex: Cubemap + :param side: Cubemap side + :param zstencil: Z-stencil buffer, or *NULL* if none + +--------------------- + +.. function:: void gs_copy_texture(gs_texture_t *dst, gs_texture_t *src) + + Copies a texture + + :param dst: Destination texture + :param src: Source texture + +--------------------- + +.. function:: void gs_stage_texture(gs_stagesurf_t *dst, gs_texture_t *src) + + Copies a texture to a staging surface and copies it to RAM. Ideally + best to give this a frame to process to prevent stalling. + + :param dst: Staging surface + :param src: Texture to stage + +--------------------- + +.. function:: void gs_begin_scene(void) + void gs_end_scene(void) + + Begins/ends a scene (this is automatically called by libobs, there's + no need to call this manually). + +--------------------- + +.. function:: void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts) + + Draws a primitive or set of primitives. + + :param draw_mode: The primitive draw mode to use + :param start_vert: Starting vertex index + :param num_verts: Number of vertices + +--------------------- + +.. function:: void gs_clear(uint32_t clear_flags, const struct vec4 *color, float depth, uint8_t stencil) + + Clears color/depth/stencil buffers. + + :param clear_flags: Flags to clear with. Can be one of the following + values: + + - GS_CLEAR_COLOR - Clears color buffer + - GS_CLEAR_DEPTH - Clears depth buffer + - GS_CLEAR_STENCIL - Clears stencil buffer + + :param color: Color value to clear the color buffer with + :param depth: Depth value to clear the depth buffer with + :param stencil: Stencil value to clear the stencil buffer with + +--------------------- + +.. function:: void gs_present(void) + + Displays what was rendered on to the current render target + +--------------------- + +.. function:: void gs_flush(void) + + Flushes GPU calls + +--------------------- + +.. function:: void gs_set_cull_mode(enum gs_cull_mode mode) + + Sets the current cull mode. + + :param mode: Cull mode + +--------------------- + +.. function:: enum gs_cull_mode gs_get_cull_mode(void) + + :return: The current cull mode + +--------------------- + +.. function:: void gs_enable_blending(bool enable) + + Enables/disables blending + + :param enable: *true* to enable, *false* to disable + +--------------------- + +.. function:: void gs_enable_depth_test(bool enable) + + Enables/disables depth testing + + :param enable: *true* to enable, *false* to disable + +--------------------- + +.. function:: void gs_enable_stencil_test(bool enable) + + Enables/disables stencil testing + + :param enable: *true* to enable, *false* to disable + +--------------------- + +.. function:: void gs_enable_stencil_write(bool enable) + + Enables/disables stencil writing + + :param enable: *true* to enable, *false* to disable + +--------------------- + +.. function:: void gs_enable_color(bool red, bool green, bool blue, bool alpha) + + Enables/disables specific color channels + + :param red: *true* to enable red channel, *false* to disable + :param green: *true* to enable green channel, *false* to disable + :param blue: *true* to enable blue channel, *false* to disable + :param alpha: *true* to enable alpha channel, *false* to disable + +--------------------- + +.. function:: void gs_blend_function(enum gs_blend_type src, enum gs_blend_type dest) + + Sets the blend function + + :param src: Blend type for the source + :param dest: Blend type for the destination + +--------------------- + +.. function:: void gs_blend_function_separate(enum gs_blend_type src_c, enum gs_blend_type dest_c, enum gs_blend_type src_a, enum gs_blend_type dest_a) + + Sets the blend function for RGB and alpha separately + + :param src_c: Blend type for the source RGB + :param dest_c: Blend type for the destination RGB + :param src_a: Blend type for the source alpha + :param dest_a: Blend type for the destination alpha + +--------------------- + +.. function:: void gs_depth_function(enum gs_depth_test test) + + Sets the depth function + + :param test: Sets the depth test type + +--------------------- + +.. function:: void gs_stencil_function(enum gs_stencil_side side, enum gs_depth_test test) + + Sets the stencil function + + :param side: Stencil side + :param test: Depth test + +--------------------- + +.. function:: void gs_stencil_op(enum gs_stencil_side side, enum gs_stencil_op_type fail, enum gs_stencil_op_type zfail, enum gs_stencil_op_type zpass) + + Sets the stencil operation + + :param side: Stencil side + :param fail: Operation to perform on stencil test failure + :param zfail: Operation to perform on depth test failure + :param zpass: Operation to perform on depth test success + +--------------------- + +.. function:: void gs_set_viewport(int x, int y, int width, int height) + + Sets the current viewport + + :param x: X position relative to upper left + :param y: Y position relative to upper left + :param width: Width of the viewport + :param height: Height of the viewport + +--------------------- + +.. function:: void gs_get_viewport(struct gs_rect *rect) + + Gets the current viewport + + :param rect: Pointer to recieve viewport rectangle + +--------------------- + +.. function:: void gs_set_scissor_rect(const struct gs_rect *rect) + + Sets or clears the current scissor rectangle + + :rect: Scissor rectangle, or *NULL* to clear + +--------------------- + +.. function:: void gs_ortho(float left, float right, float top, float bottom, float znear, float zfar) + + Sets the projection matrix to an orthographic matrix + +--------------------- + +.. function:: void gs_frustum(float left, float right, float top, float bottom, float znear, float zfar) + + Sets the projection matrix to a frustum matrix + +--------------------- + +.. function:: void gs_projection_push(void) + + Pushes/stores the current projection matrix + +--------------------- + +.. function:: void gs_projection_pop(void) + + Pops/restores the last projection matrix pushed + +--------------------- + + +Texture Functions +----------------- + +.. function:: gs_texture_t *gs_texture_create(uint32_t width, uint32_t height, enum gs_color_format color_format, uint32_t levels, const uint8_t **data, uint32_t flags) + + Creates a texture. + + :param width: Width + :param height: Height + :param color_format: Color format + :param levels: Number of total texture levels. Set to 1 if no + mip-mapping + :param data: Pointer to array of texture data pointers + :param flags: Can be 0 or a bitwise-OR combination of one or + more of the following value: + + - GS_BUILD_MIPMAPS - Automatically builds + mipmaps (Note: not fully tested) + - GS_DYNAMIC - Dynamic + - GS_RENDER_TARGET - Render target + + :return: A new texture object + +--------------------- + +.. function:: gs_texture_t *gs_texture_create_from_file(const char *file) + + Creates a texture from a file. Note that this isn't recommended for + animated gifs -- instead use the :ref:`image_file_helper`. + + :param file: Image file to open + +--------------------- + +.. function:: void gs_texture_destroy(gs_texture_t *tex) + + Destroys a texture + + :param tex: Texture object + +--------------------- + +.. function:: uint32_t gs_texture_get_width(const gs_texture_t *tex) + + Gets the texture's width + + :param tex: Texture object + :return: The texture's width + +--------------------- + +.. function:: uint32_t gs_texture_get_height(const gs_texture_t *tex) + + Gets the texture's height + + :param tex: Texture object + :return: The texture's height + +--------------------- + +.. function:: enum gs_color_format gs_texture_get_color_format(const gs_texture_t *tex) + + Gets the texture's color format + + :param tex: Texture object + :return: The texture's color format + +--------------------- + +.. function:: bool gs_texture_map(gs_texture_t *tex, uint8_t **ptr, uint32_t *linesize) + + Maps a texture. + + :param tex: Texture object + :param ptr: Pointer to receive the pointer to the texture data + to write to + :param linesize: Pointer to receive the line size (pitch) of the + texture + +--------------------- + +.. function:: void gs_texture_unmap(gs_texture_t *tex) + + Unmaps a texture. + + :param tex: Texture object + +--------------------- + +.. function:: void gs_texture_set_image(gs_texture_t *tex, const uint8_t *data, uint32_t linesize, bool invert) + + Sets the image of a dynamic texture + + :param tex: Texture object + :param data: Data to set as the image + :param linesize: Line size (pitch) of the data + :param invert: *true* to invert vertically, *false* otherwise + +--------------------- + +.. function:: gs_texture_t *gs_texture_create_from_iosurface(void *iosurf) + + **Mac only:** Creates a texture from an IOSurface. + + :param iosurf: IOSurface object + +--------------------- + +.. function:: bool gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf) + + **Mac only:** Rebinds a texture to another IOSurface + + :param texture: Texture object + :param iosuf: IOSurface object + +--------------------- + +.. function:: gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height) + + **Windows only:** Creates a GDI-interop texture + + :param width: Width + :param height: Height + +--------------------- + +.. function:: void *gs_texture_get_dc(gs_texture_t *gdi_tex) + + **Windows only:** Gets the HDC of a GDI-interop texture. Call + :c:func:`gs_texture_release_dc()` to release the HDC. + + :param gdi_tex: GDI-interop texture object + :return: HDC object + +--------------------- + +.. function:: void gs_texture_release_dc(gs_texture_t *gdi_tex) + + **Windows only:** Releases the HDC of the GDI-interop texture. + + :param gdi_tex: GDI-interop texture object + +--------------------- + +.. function:: gs_texture_t *gs_texture_open_shared(uint32_t handle) + + **Windows only:** Creates a texture from a shared texture handle. + + :param handle: Shared texture handle + :return: A texture object + +--------------------- + +.. function:: bool gs_gdi_texture_available(void) + + **Windows only:** Returns whether GDI-interop textures are available. + + :return: *true* if available, *false* otherwise + +--------------------- + +.. function:: bool gs_shared_texture_available(void) + + **Windows only:** Returns whether shared textures are available. + + :return: *true* if available, *false* otherwise + +--------------------- + + +Cube Texture Functions +---------------------- + +.. function:: gs_texture_t *gs_cubetexture_create(uint32_t size, enum gs_color_format color_format, uint32_t levels, const uint8_t **data, uint32_t flags) + + Creates a cubemap texture. + + :param size: Width/height/depth value + :param color_format: Color format + :param levels: Number of texture levels + :param data: Pointer to array of texture data pointers + :param flags: Can be 0 or a bitwise-OR combination of one or + more of the following value: + + - GS_BUILD_MIPMAPS - Automatically builds + mipmaps (Note: not fully tested) + - GS_DYNAMIC - Dynamic + - GS_RENDER_TARGET - Render target + + :return: A new cube texture object + +--------------------- + +.. function:: void gs_cubetexture_destroy(gs_texture_t *cubetex) + + Destroys a cube texture. + + :param cubetex: Cube texture object + +--------------------- + +.. function:: uint32_t gs_cubetexture_get_size(const gs_texture_t *cubetex) + + Get the width/height/depth value of a cube texture. + + :param cubetex: Cube texture object + :return: The width/height/depth value of the cube texture + +--------------------- + +.. function:: enum gs_color_format gs_cubetexture_get_color_format(const gs_texture_t *cubetex) + + Gets the color format of a cube texture. + + :param cubetex: Cube texture object + :return: The color format of the cube texture + +--------------------- + +.. function:: void gs_cubetexture_set_image(gs_texture_t *cubetex, uint32_t side, const void *data, uint32_t linesize, bool invert) + + Sets an image of a cube texture side. + + :param cubetex: Cube texture object + :param side: Side + :param data: Texture data to set + :param linesize: Line size (pitch) of the texture data + :param invert: *true* to invert texture data, *false* otherwise + +--------------------- + + +Staging Surface Functions +------------------------- + +Staging surfaces are used to efficiently copy textures from VRAM to RAM. + +.. function:: gs_stagesurf_t *gs_stagesurface_create(uint32_t width, uint32_t height, enum gs_color_format color_format) + + Creates a staging surface. + + :param width: Width + :param height: Height + :param color_format: Color format + :return: The staging surface object + +--------------------- + +.. function:: void gs_stagesurface_destroy(gs_stagesurf_t *stagesurf) + + Destroys a staging surface. + + :param stagesurf: Staging surface object + +--------------------- + +.. function:: uint32_t gs_stagesurface_get_width(const gs_stagesurf_t *stagesurf) + uint32_t gs_stagesurface_get_height(const gs_stagesurf_t *stagesurf) + + Gets the width/height of a staging surface object. + + :param stagesurf: Staging surface object + :return: Width/height of the staging surface + +--------------------- + +.. function:: enum gs_color_format gs_stagesurface_get_color_format(const gs_stagesurf_t *stagesurf) + + Gets the color format of a staging surface object. + + :param stagesurf: Staging surface object + :return: Color format of the staging surface + +--------------------- + +.. function:: bool gs_stagesurface_map(gs_stagesurf_t *stagesurf, uint8_t **data, uint32_t *linesize) + + Maps the staging surface texture (for reading). Call + :c:func:`gs_stagesurface_unmap()` to unmap when complete. + + :param stagesurf: Staging surface object + :param data: Pointer to receive texture data pointer + :param linesize: Pointer to receive line size (pitch) of the texture + data + :return: *true* if map successful, *false* otherwise + +--------------------- + +.. function:: void gs_stagesurface_unmap(gs_stagesurf_t *stagesurf) + + Unmaps a staging surface. + + :param stagesurf: Staging surface object + +--------------------- + + +Z-Stencil Functions +------------------- + +.. function:: gs_zstencil_t *gs_zstencil_create(uint32_t width, uint32_t height, enum gs_zstencil_format format) + + Creates a Z-stencil surface object. + + :param width: Width + :param height: Height + :param format: Format + :return: New Z-stencil surface object, or *NULL* if failed + +--------------------- + +.. function:: void gs_zstencil_destroy(gs_zstencil_t *zstencil) + + Destroys a Z-stencil buffer. + + :param zstencil: Z-stencil surface object + +--------------------- + + +Sampler State Functions +----------------------- + +.. function:: gs_samplerstate_t *gs_samplerstate_create(const struct gs_sampler_info *info) + + Creates a sampler state object. + + :param info: Sampler state information + :return: New sampler state object + +--------------------- + +.. function:: void gs_samplerstate_destroy(gs_samplerstate_t *samplerstate) + + Destroys a sampler state object. + + :param samplerstate: Sampler state object + +--------------------- + + +Vertex Buffer Functions +----------------------- + +.. function:: gs_vertbuffer_t *gs_vertexbuffer_create(struct gs_vb_data *data, uint32_t flags) + + Creates a vertex buffer. + + :param data: Vertex buffer data to create vertex buffer with. The + structure should be created with gs_vbdata_create(), + and then buffers in this structure should be allocated + with :c:func:`bmalloc()`, :c:func:`bzalloc()`, or + :c:func:`brealloc()`. The ownership of the gs_vb_data + pointer is then passed to the function, and they should + not be destroyed by the caller once passed + + :param flags: Creation flags. Can be 0 or a bitwise-OR combination + of any of the following values: + + - GS_DYNAMIC - Can be dynamically updated in real time. + - GS_DUP_BUFFER - Do not pass buffer ownership of the + structure or the buffer pointers within the + structure. + + :return: A new vertex buffer object, or *NULL* if failed + +--------------------- + +.. function:: void gs_vertexbuffer_destroy(gs_vertbuffer_t *vertbuffer) + + Destroys a vertex buffer object. + + :param vertbuffer: Vertex buffer object + +--------------------- + +.. function:: void gs_vertexbuffer_flush(gs_vertbuffer_t *vertbuffer) + + Flushes a vertex buffer to its interval vertex data object. To + modify its internal vertex data, call + :c:func:`gs_vertexbuffer_get_data()`. + + Can only be used with dynamic vertex buffer objects. + + :param vertbuffer: Vertex buffer object + +--------------------- + +.. function:: void gs_vertexbuffer_flush_direct(gs_vertbuffer_t *vertbuffer, const struct gs_vb_data *data) + + Directly flushes a vertex buffer to the specified vertex buffer data. + . + + Can only be used with dynamic vertex buffer objects. + + :param vertbuffer: Vertex buffer object + :param data: Vertex buffer data to flush. Components that + don't need to be flushed can be left *NULL* + +--------------------- + +.. function:: struct gs_vb_data *gs_vertexbuffer_get_data(const gs_vertbuffer_t *vertbuffer) + + Gets the vertex buffer data associated with a vertex buffer object. + This data can be changed and vertex buffer can be updated with + :c:func:`gs_vertexbuffer_flush()`. + + Can only be used with dynamic vertex buffer objects. + + :param vertbuffer: Vertex buffer object + :return: Vertex buffer data structure + +--------------------- + + +Index Buffer Functions +---------------------- + +.. function:: gs_indexbuffer_t *gs_indexbuffer_create(enum gs_index_type type, void *indices, size_t num, uint32_t flags) + + Creates an index buffer. + + :param type: Index buffer type + :param indices: Index buffer data. This buffer must be allocated + with :c:func:`bmalloc()`, :c:func:`bzalloc()`, or + :c:func:`bralloc()`, and ownership of this buffer is + passed to the index buffer object. + :param num: Number of indices in the buffer + + :param flags: Creation flags. Can be 0 or a bitwise-OR combination + of any of the following values: + + - GS_DYNAMIC - Can be dynamically updated in real time. + - GS_DUP_BUFFER - Do not pass buffer ownership + + :return: A new index buffer object, or *NULL* if failed + +--------------------- + +.. function:: void gs_indexbuffer_destroy(gs_indexbuffer_t *indexbuffer) + + Destroys an index buffer object. + + :param indexbuffer: Index buffer object + +--------------------- + +.. function:: void gs_indexbuffer_flush(gs_indexbuffer_t *indexbuffer) + + Flushes a index buffer to its interval index data object. To modify + its internal index data, call :c:func:`gs_indexbuffer_get_data()`. + + Can only be used with dynamic index buffer objects. + + :param indexbuffer: Index buffer object + +--------------------- + +.. function:: void gs_indexbuffer_flush_direct(gs_indexbuffer_t *indexbuffer, const void *data) + + Flushes a index buffer to the specified index buffer data. + + Can only be used with dynamic index buffer objects. + + :param indexbuffer: Index buffer object + :param data: Index buffer data to flush + +--------------------- + +.. function:: void *gs_indexbuffer_get_data(const gs_indexbuffer_t *indexbuffer) + + Gets the index buffer data associated with a index buffer object. + This data can be changed and index buffer can be updated with + :c:func:`gs_indexbuffer_flush()`. + + Can only be used with dynamic index buffer objects. + + :param vertbuffer: Index buffer object + :return: Index buffer data pointer + +--------------------- + +.. function:: size_t gs_indexbuffer_get_num_indices(const gs_indexbuffer_t *indexbuffer) + + Gets the number of indices associated with this index buffer. + + :param indexbuffer: Index buffer object + :return: Number of indices the vertex buffer object has + +--------------------- + +.. function:: enum gs_index_type gs_indexbuffer_get_type(const gs_indexbuffer_t *indexbuffer) + + Gets the type of index buffer. + + :param indexbuffer: Index buffer object + :return: Index buffer type + +--------------------- + + +Display Duplicator (Windows Only) +--------------------------------- + +.. function:: gs_duplicator_t *gs_duplicator_create(int monitor_idx) + +--------------------- + +.. function:: void gs_duplicator_destroy(gs_duplicator_t *duplicator) + +--------------------- + +.. function:: bool gs_duplicator_update_frame(gs_duplicator_t *duplicator) + +--------------------- + +.. function:: gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator) + +--------------------- + +.. function:: bool gs_get_duplicator_monitor_info(int monitor_idx, struct gs_monitor_info *monitor_info) + +--------------------- + + +Render Helper Functions +----------------------- + +.. function:: void gs_render_start(bool b_new) + +--------------------- + +.. function:: void gs_render_stop(enum gs_draw_mode mode) + +--------------------- + +.. function:: gs_vertbuffer_t *gs_render_save(void) + +--------------------- + +.. function:: void gs_vertex2f(float x, float y) + +--------------------- + +.. function:: void gs_vertex3f(float x, float y, float z) + +--------------------- + +.. function:: void gs_normal3f(float x, float y, float z) + +--------------------- + +.. function:: void gs_color(uint32_t color) + +--------------------- + +.. function:: void gs_texcoord(float x, float y, int unit) + +--------------------- + +.. function:: void gs_vertex2v(const struct vec2 *v) + +--------------------- + +.. function:: void gs_vertex3v(const struct vec3 *v) + +--------------------- + +.. function:: void gs_normal3v(const struct vec3 *v) + +--------------------- + +.. function:: void gs_color4v(const struct vec4 *v) + +--------------------- + +.. function:: void gs_texcoord2v(const struct vec2 *v, int unit) + +--------------------- + + +Graphics Types +-------------- + +.. type:: typedef struct gs_duplicator gs_duplicator_t +.. type:: typedef struct gs_texture gs_texture_t +.. type:: typedef struct gs_stage_surface gs_stagesurf_t +.. type:: typedef struct gs_zstencil_buffer gs_zstencil_t +.. type:: typedef struct gs_vertex_buffer gs_vertbuffer_t +.. type:: typedef struct gs_index_buffer gs_indexbuffer_t +.. type:: typedef struct gs_sampler_state gs_samplerstate_t +.. type:: typedef struct gs_swap_chain gs_swapchain_t +.. type:: typedef struct gs_texture_render gs_texrender_t +.. type:: typedef struct gs_shader gs_shader_t +.. type:: typedef struct gs_shader_param gs_sparam_t +.. type:: typedef struct gs_device gs_device_t +.. type:: typedef struct graphics_subsystem graphics_t diff --git a/docs/sphinx/reference-libobs-graphics-image-file.rst b/docs/sphinx/reference-libobs-graphics-image-file.rst new file mode 100644 index 000000000..dd52db73f --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-image-file.rst @@ -0,0 +1,71 @@ +.. _image_file_helper: + +Image File Helper +================= + +Helper functions/type for easily loading/managing image files, including +animated gif files. + +.. code:: cpp + + #include + +.. type:: struct gs_image_file + + Image file structure + +.. type:: gs_texture_t *gs_image_file.texture + + Texture + +.. type:: typedef struct gs_image_file gs_image_file_t + + Image file type + +--------------------- + +.. function:: void gs_image_file_init(gs_image_file_t *image, const char *file) + + Loads an initializes an image file helper. Does not initialize the + texture; call :c:func:`gs_image_file_init_texture()` to initialize + the texture. + + :param image: Image file helper to initialize + :param file: Path to the image file to load + +--------------------- + +.. function:: void gs_image_file_free(gs_image_file_t *image) + + Frees an image file helper + + :param image: Image file helper + +--------------------- + +.. function:: void gs_image_file_init_texture(gs_image_file_t *image) + + Initializes the texture of an image file helper. This is separate + from :c:func:`gs_image_file_init()` because it allows deferring the + graphics initialization if needed. + + :param image: Image file helper + +--------------------- + +.. function:: bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns) + + Performs a tick operation on the image file helper (used primarily + for animated file). Does not update the texture until + :c:func:`gs_image_file_update_texture()` is called. + + :param image: Image file helper + :param elapsed_time_ns: Elapsed time in nanoseconds + +--------------------- + +.. function:: void gs_image_file_update_texture(gs_image_file_t *image) + + Updates the texture (used primarily for animated files) + + :param image: Image file helper diff --git a/docs/sphinx/reference-libobs-graphics-math.rst b/docs/sphinx/reference-libobs-graphics-math.rst new file mode 100644 index 000000000..c5cffe416 --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-math.rst @@ -0,0 +1,39 @@ +Extra Math Functions/Macros +=========================== + +.. code:: + + #include + +Helper functions/macros for graphics math. + +.. function:: RAD(val) + + Macro that converts a floating point degrees value to radians. + +.. function:: DEG(val) + + Macro that converts a floating point radians value to degrees. + +**LARGE_EPSILON** 1e-2f + + Large epsilon value. + +**EPSILON** 1e-4f + + Epsilon value. + +**TINY_EPSILON** 1e-5f + + Tiny Epsilon value. + +**M_INFINITE** 3.4e38f + + Infinite value + +--------------------- + +.. function:: float rand_float(int positive_only) + + Generates a random floating point value (from -1.0f..1.0f, or + 0.0f..1.0f if *positive_only* is set). diff --git a/docs/sphinx/reference-libobs-graphics-matrix4.rst b/docs/sphinx/reference-libobs-graphics-matrix4.rst new file mode 100644 index 000000000..6924cbc76 --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-matrix4.rst @@ -0,0 +1,151 @@ +Matrix +====== + +.. code:: cpp + + #include + +.. type:: struct matrix4 + + Matrix structure + +.. member:: struct vec4 matrix4.x + + X component vector + +.. member:: struct vec4 matrix4.y + + Y component vector + +.. member:: struct vec4 matrix4.z + + Z component vector + +.. member:: struct vec4 matrix4.w + + W component vector + +--------------------- + +.. function:: void matrix4_copy(struct matrix4 *dst, const struct matrix4 *m) + + Copies a matrix + + :param dst: Destination matrix + :param m: Matrix to copy + +--------------------- + +.. function:: void matrix4_identity(struct matrix4 *dst) + + Sets an identity matrix + + :param dst: Destination matrix + +--------------------- + +.. function:: void matrix4_from_quat(struct matrix4 *dst, const struct quat *q) + + Converts a quaternion to a matrix + + :param dst: Destination matrix + :param q: Quaternion to convert + +--------------------- + +.. function:: void matrix4_from_axisang(struct matrix4 *dst, const struct axisang *aa) + + Converts an axis angle to a matrix + + :param dst: Destination matrix + :param aa: Axis angle to convert + +--------------------- + +.. function:: void matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1, const struct matrix4 *m2) + + Multiples two matrices + + :param dst: Destination matrix + :param m1: Matrix 1 + :param m2: Matrix 2 + +--------------------- + +.. function:: float matrix4_determinant(const struct matrix4 *m) + + Gets the determinant value of a matrix + + :param m: Matrix + :return: Determinant + +--------------------- + +.. function:: void matrix4_translate3v(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v) + void matrix4_translate3f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z) + + Translates the matrix by a 3-component vector + + :param dst: Destination matrix + :param m: Matrix to translate + :param v: Translation vector + +--------------------- + +.. function:: void matrix4_translate4v(struct matrix4 *dst, const struct matrix4 *m, const struct vec4 *v) + + Translates the matrix by a 4-component vector + + :param dst: Destination matrix + :param m: Matrix to translate + :param v: Translation vector + +--------------------- + +.. function:: void matrix4_rotate(struct matrix4 *dst, const struct matrix4 *m, const struct quat *q) + + Rotates a matrix by a quaternion + + :param dst: Destination matrix + :param m: Matrix to rotate + :param q: Rotation quaternion + +--------------------- + +.. function:: void matrix4_rotate_aa(struct matrix4 *dst, const struct matrix4 *m, const struct axisang *aa) + void matrix4_rotate_aa4f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z, float rot) + + Rotates a matrix by an axis angle + + :param dst: Destination matrix + :param m: Matrix to rotate + :param aa: Rotation anxis angle + +--------------------- + +.. function:: void matrix4_scale(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v) + void matrix4_scale3f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z) + + Scales each matrix component by the components of a 3-component vector + + :param dst: Destination matrix + :param m: Matrix to scale + :param v: Scale vector + +--------------------- + +.. function:: bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m) + + Inverts a matrix + + :param dst: Destination matrix + :param m: Matrix to invert + +--------------------- + +.. function:: void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m) + + Transposes a matrix + + :param dst: Destination matrix + :param m: Matrix to transpose diff --git a/docs/sphinx/reference-libobs-graphics-quat.rst b/docs/sphinx/reference-libobs-graphics-quat.rst new file mode 100644 index 000000000..fbaa4f41a --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-quat.rst @@ -0,0 +1,228 @@ +Quaternion +========== + +.. code:: cpp + + #include + +.. type:: struct quat + + Two component quaternion structure. + +.. member:: float quat.x + + X component + +.. member:: float quat.y + + Y component + +.. member:: float quat.z + + Z component + +.. member:: float quat.w + + W component + +.. member:: float quat.ptr[4] + + Unioned array of all components + +--------------------- + +.. function:: void quat_identity(struct quat *dst) + + Sets a quaternion to {0.0f, 0.0f, 0.0f, 1.0f}. + + :param dst: Destination + +--------------------- + +.. function:: void quat_set(struct quat *dst, float x, float y) + + Sets the individual components of a quaternion. + + :param dst: Destination + :param x: X component + :param y: Y component + :param y: Z component + :param w: W component + +--------------------- + +.. function:: void quat_copy(struct quat *dst, const struct quat *v) + + Copies a quaternion + + :param dst: Destination + :param v: Quaternion to copy + +--------------------- + +.. function:: void quat_add(struct quat *dst, const struct quat *v1, const struct quat *v2) + + Adds two quaternions + + :param dst: Destination + :param v1: Quaternion 1 + :param v2: Quaternion 2 + +--------------------- + +.. function:: void quat_sub(struct quat *dst, const struct quat *v1, const struct quat *v2) + + Subtracts two quaternions + + :param dst: Destination + :param v1: Quaternion being subtracted from + :param v2: Quaternion being subtracted + +--------------------- + +.. function:: void quat_mul(struct quat *dst, const struct quat *v1, const struct quat *v2) + + Multiplies two quaternions + + :param dst: Destination + :param v1: Quaternion 1 + :param v2: Quaternion 2 + +--------------------- + +.. function:: void quat_addf(struct quat *dst, const struct quat *v, float f) + + Adds a floating point to all components + + :param dst: Destination + :param dst: Quaternion + :param f: Floating point + +--------------------- + +.. function:: void quat_subf(struct quat *dst, const struct quat *v, float f) + + Subtracts a floating point from all components + + :param dst: Destination + :param v: Quaternion being subtracted from + :param f: Floating point being subtracted + +--------------------- + +.. function:: void quat_mulf(struct quat *dst, const struct quat *v, float f) + + Multiplies a floating point with all components + + :param dst: Destination + :param dst: Quaternion + :param f: Floating point + +--------------------- + +.. function:: void quat_inv(struct quat *dst, const struct quat *v) + + Inverts a quaternion + + :param dst: Destination + :param v: Quaternion to invert + +--------------------- + +.. function:: float quat_dot(const struct quat *v1, const struct quat *v2) + + Performs a dot product between two quaternions + + :param v1: Quaternion 1 + :param v2: Quaternion 2 + :return: Result of the dot product + +--------------------- + +.. function:: float quat_len(const struct quat *v) + + Gets the length of a quaternion + + :param v: Quaternion + :return: The quaternion's length + +--------------------- + +.. function:: float quat_dist(const struct quat *v1, const struct quat *v2) + + Gets the distance between two quaternions + + :param v1: Quaternion 1 + :param v2: Quaternion 2 + :return: Distance between the two quaternions + +--------------------- + +.. function:: void quat_from_axisang(struct quat *dst, const struct axisang *aa) + + Converts an axis angle to a quaternion + + :param dst: Destination quaternion + :param aa: Axis angle + +--------------------- + +.. function:: void quat_from_matrix4(struct quat *dst, const struct matrix4 *m) + + Converts the rotational properties of a matrix to a quaternion + + :param dst: Destination quaternion + :param m: Matrix to convert + +--------------------- + +.. function:: void quat_get_dir(struct vec3 *dst, const struct quat *q) + + Converts a quaternion to a directional vector + + :param dst: Destination 3-component vector + :param q: Quaternion + +--------------------- + +.. function:: void quat_set_look_dir(struct quat *dst, const struct vec3 *dir) + + Creates a quaternion from a specific "look" direction + + :param dst: Destination quaternion + :param dir: 3-component vector representing the look direction + +--------------------- + +.. function:: void quat_interpolate(struct quat *dst, const struct quat *q1, const struct quat *q2, float t) + + Linearly interpolates two quaternions + + :param dst: Destination quaternion + :param q1: Quaternion 1 + :param q2: Quaternion 2 + :param t: Time value (0.0f..1.0f) + +--------------------- + +.. function:: void quat_get_tangent(struct quat *dst, const struct quat *prev, const struct quat *q, const struct quat *next) + + Gets a tangent value for the center of three rotational values + + :param dst: Destination quaternion + :param prev: Previous rotation + :param q: Rotation to get tangent for + :param next: Next rotation + +--------------------- + +.. function:: void quat_interpolate_cubic(struct quat *dst, const struct quat *q1, const struct quat *q2, const struct quat *m1, const struct quat *m2, float t) + + Performs cubic interpolation between two quaternions + + :param dst: Destination quaternion + :param q1: Quaternion 1 + :param q2: Quaternion 2 + :param m1: Tangent 1 + :param m2: Tangent 2 + :param t: Time value (0.0f..1.0f) diff --git a/docs/sphinx/reference-libobs-graphics-vec2.rst b/docs/sphinx/reference-libobs-graphics-vec2.rst new file mode 100644 index 000000000..41c7c8222 --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-vec2.rst @@ -0,0 +1,253 @@ +2-Component Vector +================== + +.. code:: cpp + + #include + +.. type:: struct vec2 + + Two component vector structure. + +.. member:: float vec2.x + + X component + +.. member:: float vec2.y + + Y component + +.. member:: float vec2.ptr[2] + + Unioned array of both components + +--------------------- + +.. function:: void vec2_zero(struct vec2 *dst) + + Zeroes a vector + + :param dst: Destination + +--------------------- + +.. function:: void vec2_set(struct vec2 *dst, float x, float y) + + Sets the individual components of a 2-component vector. + + :param dst: Destination + :param x: X component + :param y: Y component + +--------------------- + +.. function:: void vec2_copy(struct vec2 *dst, const struct vec2 *v) + + Copies a vector + + :param dst: Destination + :param v: Vector to copy + +--------------------- + +.. function:: void vec2_add(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2) + + Adds two vectors + + :param dst: Destination + :param v1: Vector 1 + :param v2: Vector 2 + +--------------------- + +.. function:: void vec2_sub(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2) + + Subtracts two vectors + + :param dst: Destination + :param v1: Vector being subtracted from + :param v2: Vector being subtracted + +--------------------- + +.. function:: void vec2_mul(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2) + + Multiplies two vectors + + :param dst: Destination + :param v1: Vector 1 + :param v2: Vector 2 + +--------------------- + +.. function:: void vec2_div(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2) + + Divides two vectors + + :param dst: Destination + :param v1: Dividend + :param v2: Divisor + +--------------------- + +.. function:: void vec2_addf(struct vec2 *dst, const struct vec2 *v, float f) + + Adds a floating point to all components + + :param dst: Destination + :param dst: Vector + :param f: Floating point + +--------------------- + +.. function:: void vec2_subf(struct vec2 *dst, const struct vec2 *v, float f) + + Subtracts a floating point from all components + + :param dst: Destination + :param v: Vector being subtracted from + :param f: Floating point being subtracted + +--------------------- + +.. function:: void vec2_mulf(struct vec2 *dst, const struct vec2 *v, float f) + + Multiplies a floating point with all components + + :param dst: Destination + :param dst: Vector + :param f: Floating point + +--------------------- + +.. function:: void vec2_divf(struct vec2 *dst, const struct vec2 *v, float f) + + Divides a floating point from all components + + :param dst: Destination + :param v: Vector (dividend) + :param f: Floating point (divisor) + +--------------------- + +.. function:: void vec2_neg(struct vec2 *dst, const struct vec2 *v) + + Negates a vector + + :param dst: Destination + :param v: Vector to negate + +--------------------- + +.. function:: float vec2_dot(const struct vec2 *v1, const struct vec2 *v2) + + Performs a dot product between two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :return: Result of the dot product + +--------------------- + +.. function:: float vec2_len(const struct vec2 *v) + + Gets the length of a vector + + :param v: Vector + :return: The vector's length + +--------------------- + +.. function:: float vec2_dist(const struct vec2 *v1, const struct vec2 *v2) + + Gets the distance between two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :return: Distance between the two vectors + +--------------------- + +.. function:: void vec2_minf(struct vec2 *dst, const struct vec2 *v, float val) + + Gets the minimum values between a vector's components and a floating point + + :param dst: Destination + :param v: Vector + :param val: Floating point + +--------------------- + +.. function:: void vec2_min(struct vec2 *dst, const struct vec2 *v, const struct vec2 *min_v) + + Gets the minimum values between two vectors + + :param dst: Destination + :param v: Vector 1 + :param min_v: Vector 2 + +--------------------- + +.. function:: void vec2_maxf(struct vec2 *dst, const struct vec2 *v, float val) + + Gets the maximum values between a vector's components and a floating point + + :param dst: Destination + :param v: Vector + :param val: Floating point + +--------------------- + +.. function:: void vec2_max(struct vec2 *dst, const struct vec2 *v, const struct vec2 *max_v) + + Gets the maximum values between two vectors + + :param dst: Destination + :param v: Vector 1 + :param max_v: Vector 2 + +--------------------- + +.. function:: void vec2_abs(struct vec2 *dst, const struct vec2 *v) + + Gets the absolute values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: void vec2_floor(struct vec2 *dst, const struct vec2 *v) + + Gets the floor values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: void vec2_ceil(struct vec2 *dst, const struct vec2 *v) + + Gets the ceiling values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: int vec2_close(const struct vec2 *v1, const struct vec2 *v2, float epsilon) + + Compares two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :param epsilon: Maximum precision for comparison + +--------------------- + +.. function:: void vec2_norm(struct vec2 *dst, const struct vec2 *v) + + Normalizes a vector + + :param dst: Desination + :param v: Vector to normalize diff --git a/docs/sphinx/reference-libobs-graphics-vec3.rst b/docs/sphinx/reference-libobs-graphics-vec3.rst new file mode 100644 index 000000000..7a2e9387d --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-vec3.rst @@ -0,0 +1,306 @@ +3-Component Vector +================== + +.. code:: cpp + + #include + +.. type:: struct vec3 + + Two component vector structure. + +.. member:: float vec3.x + + X component + +.. member:: float vec3.y + + Y component + +.. member:: float vec3.z + + Z component + +.. member:: float vec3.ptr[3] + + Unioned array of all components + +--------------------- + +.. function:: void vec3_zero(struct vec3 *dst) + + Zeroes a vector + + :param dst: Destination + +--------------------- + +.. function:: void vec3_set(struct vec3 *dst, float x, float y) + + Sets the individual components of a 3-component vector. + + :param dst: Destination + :param x: X component + :param y: Y component + :param y: Z component + +--------------------- + +.. function:: void vec3_copy(struct vec3 *dst, const struct vec3 *v) + + Copies a vector + + :param dst: Destination + :param v: Vector to copy + +--------------------- + +.. function:: void vec3_from_vec4(struct vec3 *dst, const struct vec4 *v) + + Creates a 3-component vector from a 4-component vector + + :param dst: 3-component vector destination + :param v: 4-component vector + +--------------------- + +.. function:: void vec3_add(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2) + + Adds two vectors + + :param dst: Destination + :param v1: Vector 1 + :param v2: Vector 2 + +--------------------- + +.. function:: void vec3_sub(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2) + + Subtracts two vectors + + :param dst: Destination + :param v1: Vector being subtracted from + :param v2: Vector being subtracted + +--------------------- + +.. function:: void vec3_mul(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2) + + Multiplies two vectors + + :param dst: Destination + :param v1: Vector 1 + :param v2: Vector 2 + +--------------------- + +.. function:: void vec3_div(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2) + + Divides two vectors + + :param dst: Destination + :param v1: Dividend + :param v2: Divisor + +--------------------- + +.. function:: void vec3_addf(struct vec3 *dst, const struct vec3 *v, float f) + + Adds a floating point to all components + + :param dst: Destination + :param dst: Vector + :param f: Floating point + +--------------------- + +.. function:: void vec3_subf(struct vec3 *dst, const struct vec3 *v, float f) + + Subtracts a floating point from all components + + :param dst: Destination + :param v: Vector being subtracted from + :param f: Floating point being subtracted + +--------------------- + +.. function:: void vec3_mulf(struct vec3 *dst, const struct vec3 *v, float f) + + Multiplies a floating point with all components + + :param dst: Destination + :param dst: Vector + :param f: Floating point + +--------------------- + +.. function:: void vec3_divf(struct vec3 *dst, const struct vec3 *v, float f) + + Divides a floating point from all components + + :param dst: Destination + :param v: Vector (dividend) + :param f: Floating point (divisor) + +--------------------- + +.. function:: void vec3_neg(struct vec3 *dst, const struct vec3 *v) + + Negates a vector + + :param dst: Destination + :param v: Vector to negate + +--------------------- + +.. function:: float vec3_dot(const struct vec3 *v1, const struct vec3 *v2) + + Performs a dot product between two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :return: Result of the dot product + +--------------------- + +.. function:: void vec3_cross(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2) + + Performs a cross product between two vectors + + :param dst: Destination + :param v1: Vector 1 + :param v2: Vector 2 + +--------------------- + +.. function:: float vec3_len(const struct vec3 *v) + + Gets the length of a vector + + :param v: Vector + :return: The vector's length + +--------------------- + +.. function:: float vec3_dist(const struct vec3 *v1, const struct vec3 *v2) + + Gets the distance between two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :return: Distance between the two vectors + +--------------------- + +.. function:: void vec3_minf(struct vec3 *dst, const struct vec3 *v, float val) + + Gets the minimum values between a vector's components and a floating point + + :param dst: Destination + :param v: Vector + :param val: Floating point + +--------------------- + +.. function:: void vec3_min(struct vec3 *dst, const struct vec3 *v, const struct vec3 *min_v) + + Gets the minimum values between two vectors + + :param dst: Destination + :param v: Vector 1 + :param min_v: Vector 2 + +--------------------- + +.. function:: void vec3_maxf(struct vec3 *dst, const struct vec3 *v, float val) + + Gets the maximum values between a vector's components and a floating point + + :param dst: Destination + :param v: Vector + :param val: Floating point + +--------------------- + +.. function:: void vec3_max(struct vec3 *dst, const struct vec3 *v, const struct vec3 *max_v) + + Gets the maximum values between two vectors + + :param dst: Destination + :param v: Vector 1 + :param max_v: Vector 2 + +--------------------- + +.. function:: void vec3_abs(struct vec3 *dst, const struct vec3 *v) + + Gets the absolute values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: void vec3_floor(struct vec3 *dst, const struct vec3 *v) + + Gets the floor values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: void vec3_ceil(struct vec3 *dst, const struct vec3 *v) + + Gets the ceiling values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: int vec3_close(const struct vec3 *v1, const struct vec3 *v2, float epsilon) + + Compares two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :param epsilon: Maximum precision for comparison + +--------------------- + +.. function:: void vec3_norm(struct vec3 *dst, const struct vec3 *v) + + Normalizes a vector + + :param dst: Desination + :param v: Vector to normalize + +--------------------- + +.. function:: void vec3_transform(struct vec3 *dst, const struct vec3 *v, const struct matrix4 *m) + + Transforms a vector + + :param dst: Destination + :param v: Vector + :param m: Matrix + +--------------------- + +.. function:: void vec3_rotate(struct vec3 *dst, const struct vec3 *v, const struct matrix3 *m) + + Rotates a vector + + :param dst: Destination + :param v: Vector + :param m: Matrix + +--------------------- + +.. function:: void vec3_rand(struct vec3 *dst, int positive_only) + + Generates a random vector + + :param dst: Destination + :param positive_only: *true* if positive only, *false* otherwise diff --git a/docs/sphinx/reference-libobs-graphics-vec4.rst b/docs/sphinx/reference-libobs-graphics-vec4.rst new file mode 100644 index 000000000..15725e8f6 --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics-vec4.rst @@ -0,0 +1,282 @@ +4-Component Vector +================== + +.. code:: cpp + + #include + +.. type:: struct vec4 + + Two component vector structure. + +.. member:: float vec4.x + + X component + +.. member:: float vec4.y + + Y component + +.. member:: float vec4.z + + Z component + +.. member:: float vec4.w + + W component + +.. member:: float vec4.ptr[4] + + Unioned array of all components + +--------------------- + +.. function:: void vec4_zero(struct vec4 *dst) + + Zeroes a vector + + :param dst: Destination + +--------------------- + +.. function:: void vec4_set(struct vec4 *dst, float x, float y, float z, float w) + + Sets the individual components of a 4-component vector. + + :param dst: Destination + :param x: X component + :param y: Y component + :param z: Z component + :param w: W component + +--------------------- + +.. function:: void vec4_copy(struct vec4 *dst, const struct vec4 *v) + + Copies a vector + + :param dst: Destination + :param v: Vector to copy + +--------------------- + +.. function:: void vec4_from_vec3(struct vec4 *dst, const struct vec3 *v) + + Creates a 4-component vector from a 3-component vector + + :param dst: 4-component vector destination + :param v: 3-component vector + +--------------------- + +.. function:: void vec4_add(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2) + + Adds two vectors + + :param dst: Destination + :param v1: Vector 1 + :param v2: Vector 2 + +--------------------- + +.. function:: void vec4_sub(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2) + + Subtracts two vectors + + :param dst: Destination + :param v1: Vector being subtracted from + :param v2: Vector being subtracted + +--------------------- + +.. function:: void vec4_mul(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2) + + Multiplies two vectors + + :param dst: Destination + :param v1: Vector 1 + :param v2: Vector 2 + +--------------------- + +.. function:: void vec4_div(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2) + + Divides two vectors + + :param dst: Destination + :param v1: Dividend + :param v2: Divisor + +--------------------- + +.. function:: void vec4_addf(struct vec4 *dst, const struct vec4 *v, float f) + + Adds a floating point to all components + + :param dst: Destination + :param dst: Vector + :param f: Floating point + +--------------------- + +.. function:: void vec4_subf(struct vec4 *dst, const struct vec4 *v, float f) + + Subtracts a floating point from all components + + :param dst: Destination + :param v: Vector being subtracted from + :param f: Floating point being subtracted + +--------------------- + +.. function:: void vec4_mulf(struct vec4 *dst, const struct vec4 *v, float f) + + Multiplies a floating point with all components + + :param dst: Destination + :param dst: Vector + :param f: Floating point + +--------------------- + +.. function:: void vec4_divf(struct vec4 *dst, const struct vec4 *v, float f) + + Divides a floating point from all components + + :param dst: Destination + :param v: Vector (dividend) + :param f: Floating point (divisor) + +--------------------- + +.. function:: void vec4_neg(struct vec4 *dst, const struct vec4 *v) + + Negates a vector + + :param dst: Destination + :param v: Vector to negate + +--------------------- + +.. function:: float vec4_dot(const struct vec4 *v1, const struct vec4 *v2) + + Performs a dot product between two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :return: Result of the dot product + +--------------------- + +.. function:: float vec4_len(const struct vec4 *v) + + Gets the length of a vector + + :param v: Vector + :return: The vector's length + +--------------------- + +.. function:: float vec4_dist(const struct vec4 *v1, const struct vec4 *v2) + + Gets the distance between two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :return: Distance between the two vectors + +--------------------- + +.. function:: void vec4_minf(struct vec4 *dst, const struct vec4 *v, float val) + + Gets the minimum values between a vector's components and a floating point + + :param dst: Destination + :param v: Vector + :param val: Floating point + +--------------------- + +.. function:: void vec4_min(struct vec4 *dst, const struct vec4 *v, const struct vec4 *min_v) + + Gets the minimum values between two vectors + + :param dst: Destination + :param v: Vector 1 + :param min_v: Vector 2 + +--------------------- + +.. function:: void vec4_maxf(struct vec4 *dst, const struct vec4 *v, float val) + + Gets the maximum values between a vector's components and a floating point + + :param dst: Destination + :param v: Vector + :param val: Floating point + +--------------------- + +.. function:: void vec4_max(struct vec4 *dst, const struct vec4 *v, const struct vec4 *max_v) + + Gets the maximum values between two vectors + + :param dst: Destination + :param v: Vector 1 + :param max_v: Vector 2 + +--------------------- + +.. function:: void vec4_abs(struct vec4 *dst, const struct vec4 *v) + + Gets the absolute values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: void vec4_floor(struct vec4 *dst, const struct vec4 *v) + + Gets the floor values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: void vec4_ceil(struct vec4 *dst, const struct vec4 *v) + + Gets the ceiling values of each component + + :param dst: Destination + :param v: Vector + +--------------------- + +.. function:: int vec4_close(const struct vec4 *v1, const struct vec4 *v2, float epsilon) + + Compares two vectors + + :param v1: Vector 1 + :param v2: Vector 2 + :param epsilon: Maximum precision for comparison + +--------------------- + +.. function:: void vec4_norm(struct vec4 *dst, const struct vec4 *v) + + Normalizes a vector + + :param dst: Desination + :param v: Vector to normalize + +--------------------- + +.. function:: void vec4_transform(struct vec4 *dst, const struct vec4 *v, const struct matrix4 *m) + + Transforms a vector + + :param dst: Destination + :param v: Vector + :param m: Matrix diff --git a/docs/sphinx/reference-libobs-graphics.rst b/docs/sphinx/reference-libobs-graphics.rst new file mode 100644 index 000000000..d3553b26b --- /dev/null +++ b/docs/sphinx/reference-libobs-graphics.rst @@ -0,0 +1,17 @@ +Graphics API Reference (libobs/graphics) +======================================== + +.. toctree:: + :maxdepth: 2 + + reference-libobs-graphics-effects + reference-libobs-graphics-vec2 + reference-libobs-graphics-vec3 + reference-libobs-graphics-vec4 + reference-libobs-graphics-quat + reference-libobs-graphics-matrix4 + reference-libobs-graphics-math + reference-libobs-graphics-image-file + reference-libobs-graphics-axisang + reference-libobs-graphics-graphics + diff --git a/docs/sphinx/reference-libobs-media-io.rst b/docs/sphinx/reference-libobs-media-io.rst new file mode 100644 index 000000000..6e978cede --- /dev/null +++ b/docs/sphinx/reference-libobs-media-io.rst @@ -0,0 +1,463 @@ +Media I/O API Reference (libobs/media-io) +========================================= + +.. code:: cpp + + #include + + +Video Handler +------------- + +.. type:: video_t + + Video output handler object + +--------------------- + +.. type:: enum video_format + + Video format. Can be one of the following values: + + - VIDEO_FORMAT_I420 + - VIDEO_FORMAT_NV12 + + - VIDEO_FORMAT_YVYU + - VIDEO_FORMAT_YUY2 + - VIDEO_FORMAT_UYVY + + - VIDEO_FORMAT_RGBA + - VIDEO_FORMAT_BGRA + - VIDEO_FORMAT_BGRX + - VIDEO_FORMAT_Y800 + + - VIDEO_FORMAT_I444 + +--------------------- + +.. type:: enum video_colorspace + + YUV color space. Can be one of the following values: + + - VIDEO_CS_DEFAULT - Equivalent to VIDEO_CS_601 + - VIDEO_CS_601 - 601 color space + - VIDEO_CS_709 - 709 color space + +--------------------- + +.. type:: enum video_range_type + + YUV color range. + + - VIDEO_RANGE_DEFAULT - Equivalent to VIDEO_RANGE_PARTIAL + - VIDEO_RANGE_PARTIAL - Partial range + - VIDEO_RANGE_FULL - Full range + +--------------------- + +.. type:: struct video_data + + Video frame structure. + +.. member:: uint8_t *video_data.data[MAX_AV_PLANES] +.. member:: uint32_t video_data.linesize[MAX_AV_PLANES] +.. member:: uint64_t video_data.timestamp + +--------------------- + +.. type:: struct video_output_info + + Video output handler information + +.. member:: const char *video_output_info.name +.. member:: enum video_format video_output_info.format +.. member:: uint32_t video_output_info.fps_num +.. member:: uint32_t video_output_info.fps_den +.. member:: uint32_t video_output_info.width +.. member:: uint32_t video_output_info.height +.. member:: size_t video_output_info.cache_size +.. member:: enum video_colorspace video_output_info.colorspace +.. member:: enum video_range_type video_output_info.range + +--------------------- + +.. function:: enum video_format video_format_from_fourcc(uint32_t fourcc) + + Converts a fourcc value to a video format. + + :param forcecc: Fourcc value + :return: Video format + +--------------------- + +.. function:: bool video_format_get_parameters(enum video_colorspace color_space, enum video_range_type range, float matrix[16], float min_range[3], float max_range[3]) + + Converts a color space/range to matrix/min/max values. + + :param color_space: Color space to convert + :param range: Color range to convert + :param matrix: Pointer to the matrix + :param min_range: Pointer to get the minimum range value + :param max_range: Pointer to get the maximum range value + +--------------------- + +.. function:: bool video_output_connect(video_t *video, const struct video_scale_info *conversion, void (*callback)(void *param, struct video_data *frame), void *param) + + Connects a raw video callback to the video output handler. + + :param video: Video output handler object + :param callback: Callback to receive video data + :param param: Private data to pass to the callback + +--------------------- + +.. function:: void video_output_disconnect(video_t *video, void (*callback)(void *param, struct video_data *frame), void *param) + + Disconnects a raw video callback from the video output handler. + + :param video: Video output handler object + :param callback: Callback + :param param: Private data + +--------------------- + +.. function:: const struct video_output_info *video_output_get_info(const video_t *video) + + Gets the full video information of the video output handler. + + :param video: Video output handler object + :return: Video output info structure pointer + +--------------------- + +.. function:: uint64_t video_output_get_frame_time(const video_t *video) + + Gets the frame interval of the video output handler. + + :param video: Video output handler object + :return: Video frame interval in nanoseconds + +--------------------- + +.. function:: enum video_format video_output_get_format(const video_t *video) + + Gets the video format of the video output handler. + + :param video: Video output handler object + :return: Video format + +--------------------- + +.. function:: uint32_t video_output_get_width(const video_t *video) +.. function:: uint32_t video_output_get_height(const video_t *video) + + Gets the width/height of the video output handler. + + :param video: Video output handler object + :return: Width/height + +--------------------- + +.. function:: double video_output_get_frame_rate(const video_t *video) + + Gets the frame rate (as a floating point) of the video output + handler. + + :param video: Video output handler object + :return: Frame rate + +--------------------- + +.. function:: uint32_t video_output_get_skipped_frames(const video_t *video) + + Gets the skipped frame count of the video output handler. + + :param video: Video output handler object + :return: Skipped frame count + +--------------------- + +.. function:: uint32_t video_output_get_total_frames(const video_t *video) + + Gets the total frames processed of the video output handler. + + :param video: Video output handler object + :return: Total frames processed + +--------------------- + + +Audio Handler +------------- + +.. type:: audio_t + +--------------------- + +.. type:: enum audio_format + + Audio format. Can be one of the following values: + + - AUDIO_FORMAT_UNKNOWN + - AUDIO_FORMAT_U8BIT + - AUDIO_FORMAT_16BIT + - AUDIO_FORMAT_32BIT + - AUDIO_FORMAT_FLOAT + - AUDIO_FORMAT_U8BIT_PLANAR + - AUDIO_FORMAT_16BIT_PLANAR + - AUDIO_FORMAT_32BIT_PLANAR + - AUDIO_FORMAT_FLOAT_PLANAR + +--------------------- + +.. type:: enum speaker_layout + + Speaker layout. Can be one of the following values: + + - SPEAKERS_UNKNOWN + - SPEAKERS_MONO + - SPEAKERS_STEREO + - SPEAKERS_2POINT1 + - SPEAKERS_QUAD + - SPEAKERS_4POINT1 + - SPEAKERS_5POINT1 + - SPEAKERS_5POINT1_SURROUND + - SPEAKERS_7POINT1 + - SPEAKERS_7POINT1_SURROUND + - SPEAKERS_SURROUND + +--------------------- + +.. type:: struct audio_data + + Audio data structure. + +.. member:: uint8_t *audio_data.data[MAX_AV_PLANES] +.. member:: uint32_t audio_data.frames +.. member:: uint64_t audio_data.timestamp + +--------------------- + +.. type:: struct audio_output_data +.. member:: float *audio_output_data.data[MAX_AUDIO_CHANNELS] + +--------------------- + +.. type:: struct audio_output_info +.. member:: const char *audio_output_info.name +.. member:: uint32_t audio_output_info.samples_per_sec +.. member:: enum audio_format audio_output_info.format +.. member:: enum speaker_layout audio_output_info.speakers +.. member:: audio_input_callback_t audio_output_info.input_callback +.. member:: void *audio_output_info.input_param + +--------------------- + +.. type:: struct audio_convert_info +.. member:: uint32_t audio_convert_info.samples_per_sec +.. member:: enum audio_format audio_convert_info.format +.. member:: enum speaker_layout audio_convert_info.speakers + +--------------------- + +.. type:: typedef bool (*audio_input_callback_t)(void *param, uint64_t start_ts, uint64_t end_ts, uint64_t *new_ts, uint32_t active_mixers, struct audio_output_data *mixes) + + Audio input callback (typically used internally). + +--------------------- + +.. function:: uint32_t get_audio_channels(enum speaker_layout speakers) + + Converts a speaker layout to its audio channel count. + + :param speakers: Speaker layout enumeration + :return: Channel count + +--------------------- + +.. function:: size_t get_audio_bytes_per_channel(enum audio_format format) + + Gets the audio bytes per channel for a specific audio format. + + :param format: Audio format + :return: Bytes per channel + +--------------------- + +.. function:: bool is_audio_planar(enum audio_format format) + + Returns whether the audio format is a planar format. + + :param format: Audio format + :return: *true* if audio is planar, *false* otherwise + +--------------------- + +.. function:: size_t get_audio_planes(enum audio_format format, enum speaker_layout speakers) + + Gets the number of audio planes for a specific audio format and + speaker layout. + + :param format: Audio format + :param speakers: Speaker layout + :return: Number of audio planes + +--------------------- + +.. function:: size_t get_audio_size(enum audio_format format, enum speaker_layout speakers, uint32_t frames) + + Gets the audio block size for a specific frame out with the given + format and speaker layout. + + :param format: Audio format + :param speakers: Speaker layout + :param frames: Audio frame count + :return: Audio block size + +--------------------- + +.. function:: uint64_t audio_frames_to_ns(size_t sample_rate, uint64_t frames) + + Helper function to convert a specific number of audio frames to + nanoseconds based upon its sample rate. + + :param sample_rate: Sample rate + :param frames: Frame count + :return: Nanoseconds + +--------------------- + +.. function:: uint64_t ns_to_audio_frames(size_t sample_rate, uint64_t ns) + + Helper function to convert a specific number of nanoseconds to audio + frame count based upon its sample rate. + + :param sample_rate: Sample rate + :param ns: Nanoseconds + :return: Frame count + +--------------------- + +.. type:: typedef void (*audio_output_callback_t)(void *param, size_t mix_idx, struct audio_data *data) + + Audio output callback. Typically used internally. + +--------------------- + +.. function:: bool audio_output_connect(audio_t *audio, size_t mix_idx, const struct audio_convert_info *conversion, audio_output_callback_t callback, void *param) + + Connects a raw audio callback to the audio output handler. + Optionally allows audio conversion if necessary. + + :param audio: Audio output handler object + :param mix_idx: Mix index to get raw audio from + :param conversion: Audio conversion information, or *NULL* for no + conversion + :param callback: Raw audio callback + :param param: Private data to pass to the callback + +--------------------- + +.. function:: void audio_output_disconnect(audio_t *audio, size_t mix_idx, audio_output_callback_t callback, void *param) + + Disconnects a raw audio callback from the audio output handler. + + :param audio: Audio output handler object + :param mix_idx: Mix index to get raw audio from + :param callback: Raw audio callback + :param param: Private data to pass to the callback + +--------------------- + +.. function:: size_t audio_output_get_block_size(const audio_t *audio) + + Gets the audio block size of an audio output handler. + + :param audio: Audio output handler object + :return: Audio block size + +--------------------- + +.. function:: size_t audio_output_get_planes(const audio_t *audio) + + Gets the plane count of an audio output handler. + + :param audio: Audio output handler object + :return: Audio plane count + +--------------------- + +.. function:: size_t audio_output_get_channels(const audio_t *audio) + + Gets the channel count of an audio output handler. + + :param audio: Audio output handler object + :return: Audio channel count + +--------------------- + +.. function:: uint32_t audio_output_get_sample_rate(const audio_t *audio) + + Gets the sample rate of an audio output handler. + + :param audio: Audio output handler object + :return: Audio sample rate + +--------------------- + +.. function:: const struct audio_output_info *audio_output_get_info(const audio_t *audio) + + Gets all audio information for an audio output handler. + + :param audio: Audio output handler object + :return: Pointer to audio output information structure + +--------------------- + + +Resampler +--------- + +FFmpeg wrapper to resample audio. + +.. type:: typedef struct audio_resampler audio_resampler_t + +--------------------- + +.. type:: struct resample_info +.. member:: uint32_t resample_info.samples_per_sec +.. member:: enum audio_format resample_info.format +.. member:: enum speaker_layout resample_info.speakers + +--------------------- + +.. function:: audio_resampler_t *audio_resampler_create(const struct resample_info *dst, const struct resample_info *src) + + Creates an audio resampler. + + :param dst: Destination audio information + :param src: Source audio information + :return: Audio resampler object + +--------------------- + +.. function:: void audio_resampler_destroy(audio_resampler_t *resampler) + + Destroys an audio resampler. + + :param resampler: Audio resampler object + +--------------------- + +.. function:: bool audio_resampler_resample(audio_resampler_t *resampler, uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset, const uint8_t *const input[], uint32_t in_frames) + + Resamples audio frames. + + :param resampler: Audio resampler object + :param output: Pointer to receive converted audio frames + :param out_frames: Pointer to receive converted audio frame count + :param ts_offset: Pointer to receive timestamp offset (in + nanoseconds) + :param const input: Input frames to convert + :param in_frames: Input frame count diff --git a/docs/sphinx/reference-libobs-util-base.rst b/docs/sphinx/reference-libobs-util-base.rst new file mode 100644 index 000000000..a3d4744f8 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-base.rst @@ -0,0 +1,75 @@ +Logging +======= + +Functions for logging and getting log data. + +.. code:: cpp + + #include + + +Logging Levels +-------------- + +**LOG_ERROR** = 100 + + Use if there's a problem that can potentially affect the program, + but isn't enough to require termination of the program. + + Use in creation functions and core subsystem functions. Places that + should definitely not fail. + +**LOG_WARNING** = 200 + + Use if a problem occurs that doesn't affect the program and is + recoverable. + + Use in places where failure isn't entirely unexpected, and can + be handled safely. + +**LOG_INFO** = 300 + + Informative message to be displayed in the log. + +**LOG_DEBUG** = 400 + + Debug message to be used mostly by and for developers. + + +Logging Functions +----------------- + +.. type:: typedef void (*log_handler_t)(int lvl, const char *msg, va_list args, void *p) + + Logging callback. + +--------------------- + +.. function:: void base_set_log_handler(log_handler_t handler, void *param) + void base_get_log_handler(log_handler_t *handler, void **param) + + Sets/gets the current log handler. + +--------------------- + +.. function:: void base_set_crash_handler(void (*handler)(const char *, va_list, void *), void *param) + + Sets the current crash handler. + +--------------------- + +.. function:: void blogva(int log_level, const char *format, va_list args) + + Logging function (using a va_list). + +--------------------- + +.. function:: void blog(int log_level, const char *format, ...) + + Logging function. + +--------------------- + +.. function:: void bcrash(const char *format, ...) + + Crash function. diff --git a/docs/sphinx/reference-libobs-util-bmem.rst b/docs/sphinx/reference-libobs-util-bmem.rst new file mode 100644 index 000000000..36bc93cbe --- /dev/null +++ b/docs/sphinx/reference-libobs-util-bmem.rst @@ -0,0 +1,62 @@ +Memory Management +================= + +Various functions and helpers used for memory management. + +.. code:: cpp + + #include + + +Memory Functions +---------------- + +.. function:: void *bmalloc(size_t size) + + Allocates memory and increases the memory leak counter. + +--------------------- + +.. function:: void *brealloc(void *ptr, size_t size) + + Reallocates memory. Use only with memory that's been allocated by + :c:func:`bmalloc()`. + +--------------------- + +.. function:: void bfree(void *ptr) + + Frees memory allocated with :c:func:`bmalloc()` or :c:func:`bfree()`. + +--------------------- + +.. function:: long bnum_allocs(void) + + Returns current number of active allocations. + +--------------------- + +.. function:: void *bmemdup(const void *ptr, size_t size) + + Duplicates memory. + +--------------------- + +.. function:: void *bzalloc(size_t size) + + Inline function that allocates zeroed memory. + +--------------------- + +.. function:: char *bstrdup_n(const char *str, size_t n) + wchar_t *bwstrdup_n(const wchar_t *str, size_t n) + + Duplicates a string of *n* bytes and automatically zero-terminates + it. + +--------------------- + +.. function:: char *bstrdup(const char *str) + wchar_t *bwstrdup(const wchar_t *str) + + Duplicates a string. diff --git a/docs/sphinx/reference-libobs-util-circlebuf.rst b/docs/sphinx/reference-libobs-util-circlebuf.rst new file mode 100644 index 000000000..5d290a741 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-circlebuf.rst @@ -0,0 +1,158 @@ +Circular Buffers +================ + +A circular buffer that will automatically increase in size as necessary +as data is pushed to the front or back. + +.. code:: cpp + + #include + + +Circular Buffer Structure (struct circlebuf) +-------------------------------------------- + +.. type:: struct circlebuf +.. member:: void *circlebuf.data +.. member:: size_t circlebuf.size +.. member:: size_t circlebuf.start_pos +.. member:: size_t circlebuf.end_pos +.. member:: size_t circlebuf.capacity + + +Circular Buffer Inline Functions +-------------------------------- + +.. function:: void circlebuf_init(struct circlebuf *cb) + + Initializes a circular buffer (just zeroes out the entire structure). + + :param cb: The circular buffer + +--------------------- + +.. function:: void circlebuf_free(struct circlebuf *cb) + + Frees a circular buffer. + + :param cb: The circular buffer + +--------------------- + +.. function:: void circlebuf_reserve(struct circlebuf *cb, size_t capacity) + + Reserves a specific amount of buffer space to ensure minimum + upsizing. + + :param cb: The circular buffer + :param capacity: The new capacity, in bytes + +--------------------- + +.. function:: void circlebuf_upsize(struct circlebuf *cb, size_t size) + + Sets the current active (not just reserved) size. Any new data is + zeroed. + + :param cb: The circular buffer + :param size: The new size, in bytes + +--------------------- + +.. function:: void circlebuf_place(struct circlebuf *cb, size_t position, const void *data, size_t size) + + Places data at a specific positional index (relative to the starting + point) within the circular buffer. + + :param cb: The circular buffer + :param position: Positional index relative to starting point + :param data: Data to insert + :param size: Size of data to insert + +--------------------- + +.. function:: void circlebuf_push_back(struct circlebuf *cb, const void *data, size_t size) + + Pushes data to the end of the circular buffer. + + :param cb: The circular buffer + :param data: Data + :param size: Size of data + +--------------------- + +.. function:: void circlebuf_push_front(struct circlebuf *cb, const void *data, size_t size) + + Pushes data to the front of the circular buffer. + + :param cb: The circular buffer + :param data: Data + :param size: Size of data + +--------------------- + +.. function:: void circlebuf_push_back_zero(struct circlebuf *cb, size_t size) + + Pushes zeroed data to the end of the circular buffer. + + :param cb: The circular buffer + :param size: Size + +--------------------- + +.. function:: void circlebuf_push_front_zero(struct circlebuf *cb, size_t size) + + Pushes zeroed data to the front of the circular buffer. + + :param cb: The circular buffer + :param size: Size + +--------------------- + +.. function:: void circlebuf_peek_front(struct circlebuf *cb, void *data, size_t size) + + Peeks data at the front of the circular buffer. + + :param cb: The circular buffer + :param data: Buffer to store data in + :param size: Size of data to retrieve + +--------------------- + +.. function:: void circlebuf_peek_back(struct circlebuf *cb, void *data, size_t size) + + Peeks data at the back of the circular buffer. + + :param cb: The circular buffer + :param data: Buffer to store data in + :param size: Size of data to retrieve + +--------------------- + +.. function:: void circlebuf_pop_front(struct circlebuf *cb, void *data, size_t size) + + Pops data from the front of the circular buffer. + + :param cb: The circular buffer + :param data: Buffer to store data in, or *NULL* + :param size: Size of data to retrieve + +--------------------- + +.. function:: void circlebuf_pop_back(struct circlebuf *cb, void *data, size_t size) + + Pops data from the back of the circular buffer. + + :param cb: The circular buffer + :param data: Buffer to store data in, or *NULL* + :param size: Size of data to retrieve + +--------------------- + +.. function:: void *circlebuf_data(struct circlebuf *cb, size_t idx) + + Gets a direct pointer to data at a specific positional index within + the circular buffer, relative to the starting point. + + :param cb: The circular buffer + :param idx: Byte index relative to the starting point diff --git a/docs/sphinx/reference-libobs-util-config-file.rst b/docs/sphinx/reference-libobs-util-config-file.rst new file mode 100644 index 000000000..d2f8b9086 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-config-file.rst @@ -0,0 +1,312 @@ +Config Files +============ + +The configuration file functions are a simple implementation of the INI +file format, with the addition of default values. + +.. code:: cpp + + #include + +.. type:: config_t + + +Config File Functions +--------------------- + +.. function:: config_t *config_create(const char *file) + + Creates a new configuration object and associates it with the + specified file name. + + :param file: Path to the new configuration file + :return: A new configuration file object + +---------------------- + +.. function:: int config_open(config_t **config, const char *file, enum config_open_type open_type) + + Opens a configuration file. + + :param config: Pointer that receives a pointer to a new configuration + file object (if successful) + :param file: Path to the configuration file + :param open_type: Can be one of the following values: + + - CONFIG_OPEN_EXISTING - Fail if the file doesn't + exist. + - CONFIG_OPEN_ALWAYS - Try to open the file. If + the file doesn't exist, create it. + + :return: Can return the following values: + + - CONFIG_SUCCESS - Successful + - CONFIG_FILENOTFOUND - File not found + - CONFIG_ERROR - Generic error + +---------------------- + +.. function:: int config_open_string(config_t **config, const char *str) + + Opens configuration data via a string rather than a file. + + :param config: Pointer that receives a pointer to a new configuration + file object (if successful) + :param str: Configuration string + + :return: Can return the following values: + + - CONFIG_SUCCESS - Successful + - CONFIG_FILENOTFOUND - File not found + - CONFIG_ERROR - Generic error + +---------------------- + +.. function:: int config_save(config_t *config) + + Saves configuration data to a file (if associated with a file). + + :param config: Configuration object + + :return: Can return the following values: + + - CONFIG_SUCCESS - Successful + - CONFIG_FILENOTFOUND - File not found + - CONFIG_ERROR - Generic error + +---------------------- + +.. function:: int config_save_safe(config_t *config, const char *temp_ext, const char *backup_ext) + + Saves configuration data and minimizes overwrite corruption risk. + Saves the file with the file name + + :param config: Configuration object + :param temp_ext: Temporary extension for the new file + :param backup_ext: Backup extension for the old file. Can be *NULL* + if no backup is desired. + + :return: Can return the following values: + + - CONFIG_SUCCESS - Successful + - CONFIG_FILENOTFOUND - File not found + - CONFIG_ERROR - Generic error + +---------------------- + +.. function:: void config_close(config_t *config) + + Closes the configuration object. + + :param config: Configuration object + +---------------------- + +.. function:: size_t config_num_sections(config_t *config) + + Returns the number of sections. + + :param config: Configuration object + :return: Number of configuration sections + +---------------------- + +.. function:: const char *config_get_section(config_t *config, size_t idx) + + Returns a section name based upon its index. + + :param config: Configuration object + :param idx: Index of the section + :return: The section's name + +Set/Get Functions +----------------- + +.. function:: void config_set_string(config_t *config, const char *section, const char *name, const char *value) + + Sets a string value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The string value + +---------------------- + +.. function:: void config_set_int(config_t *config, const char *section, const char *name, int64_t value) + + Sets an integer value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The integer value + +---------------------- + +.. function:: void config_set_uint(config_t *config, const char *section, const char *name, uint64_t value) + + Sets an unsigned integer value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The unsigned integer value + +---------------------- + +.. function:: void config_set_bool(config_t *config, const char *section, const char *name, bool value) + + Sets a boolean value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The boolean value + +---------------------- + +.. function:: void config_set_double(config_t *config, const char *section, const char *name, double value) + + Sets a floating point value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The floating point value + +---------------------- + +.. function:: const char *config_get_string(config_t *config, const char *section, const char *name) + + Gets a string value. If the value is not set, it will use the + default value. If there is no default value, it will return *NULL*. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :return: The string value + +---------------------- + +.. function:: int64_t config_get_int(config_t *config, const char *section, const char *name) + + Gets an integer value. If the value is not set, it will use the + default value. If there is no default value, it will return 0. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :return: The integer value + +---------------------- + +.. function:: uint64_t config_get_uint(config_t *config, const char *section, const char *name) + + Gets an unsigned integer value. If the value is not set, it will use + the default value. If there is no default value, it will return 0. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :return: The unsigned integer value + +---------------------- + +.. function:: bool config_get_bool(config_t *config, const char *section, const char *name) + + Gets a boolean value. If the value is not set, it will use the + default value. If there is no default value, it will return false. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :return: The boolean value + +---------------------- + +.. function:: double config_get_double(config_t *config, const char *section, const char *name) + + Gets a floating point value. If the value is not set, it will use + the default value. If there is no default value, it will return 0.0. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :return: The floating point value + +---------------------- + +.. function:: bool config_remove_value(config_t *config, const char *section, const char *name) + + Removes a value. Does not remove the default value if any. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + + +Default Value Functions +----------------------- + +.. function:: int config_open_defaults(config_t *config, const char *file) + + Opens a file and uses it for default values. + + :param config: Configuration object + :param file: The file to open for default values + +---------------------- + +.. function:: void config_set_default_string(config_t *config, const char *section, const char *name, const char *value) + + Sets a default string value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The string value + +---------------------- + +.. function:: void config_set_default_int(config_t *config, const char *section, const char *name, int64_t value) + + Sets a default integer value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The integer value + +---------------------- + +.. function:: void config_set_default_uint(config_t *config, const char *section, const char *name, uint64_t value) + + Sets a default unsigned integer value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The unsigned integer value + +---------------------- + +.. function:: void config_set_default_bool(config_t *config, const char *section, const char *name, bool value) + + Sets a default boolean value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The boolean value + +---------------------- + +.. function:: void config_set_default_double(config_t *config, const char *section, const char *name, double value) + + Sets a default floating point value. + + :param config: Configuration object + :param section: The section of the value + :param name: The value name + :param value: The floating point value diff --git a/docs/sphinx/reference-libobs-util-darray.rst b/docs/sphinx/reference-libobs-util-darray.rst new file mode 100644 index 000000000..13ca27334 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-darray.rst @@ -0,0 +1,275 @@ +Dynamic Arrays +============== + +Dynamically resizing arrays (a C equivalent to std::vector). + +.. code:: cpp + + #include + +.. type:: struct darray + + The base dynamic array structure. + +.. type:: DARRAY(type) + + Macro for a dynamic array based upon an actual type. Use this with + da_* macros. + +.. member:: void *darray.array + + The array pointer. + +.. member:: size_t darray.num + + The number of items within the array. + +.. member:: size_t darray.capacity + + The capacity of the array. + + +Dynamic Array Macros +-------------------- + +These macro functions are used with variables created with the +**DARRAY** type macro. When using these functions, do not use the +dynamic array value with a reference (&) operator. For example: + +.. code:: cpp + + /* creates an array of integers: 0..9 */ + DARRAY(int) array_of_integers; + da_init(array_of_integers); + + for (size_t i = 0; i < 10; i++) + da_push_back(array_of_integers, &i); + + [...] + + /* free when complete */ + da_free(array_of_integers); + +.. function:: void da_init(da) + + Initializes a dynamic array. + + :param da: The dynamic array + +--------------------- + +.. function:: void da_free(da) + + Frees a dynamic array. + + :param da: The dynamic array + +--------------------- + +.. function:: void *da_end(da) + + Gets a pointer to the last value. + + :param da: The dynamic array + :return: The last value of a dynamic array, or *NULL* if empty. + +--------------------- + +.. function:: void da_reserve(da, size_t capacity) + + Reserves a specific amount of buffer space for the dynamic array. + + :param da: The dynamic array + :param capacity: New capacity of the dynamic array + +--------------------- + +.. function:: void da_resize(da, size_t new_size) + + Resizes the dynamic array with zeroed values. + + :param da: The dynamic array + :param size: New size of the dynamic array + +--------------------- + +.. function:: void da_copy(da_dst, da_src) + + Makes a copy of a dyanmic array. + + :param da_dst: The dynamic array to copy to + :param da_src: The dynamic array to copy from + +--------------------- + +.. function:: void da_copy_array(da, const void *src_array, size_t size) + + Makes a copy of an array pointer. + + :param da: The dynamic array + :param src_array: The array pointer to make a copy from + :param size: New size of the dynamic array + +--------------------- + +.. function:: void da_move(da_dst, da_src) + + Moves one dynamic array variable to another without allocating new + data. *da_dst* is freed before moving, *da_dst* is set to *da_src*, + then *da_src* is then zeroed. + + :param da_dst: Destination variable + :param da_src: Source variable + +--------------------- + +.. function:: size_t da_find(da, const void *item_data, size_t starting_idx) + + Finds a value based upon its data. If the value cannot be found, the + return value will be DARRAY_INVALID (-1). + + :param da: The dynamic array + :param item_data: The item data to find + :param starting_idx: The index to start from or 0 to search the + entire array + +--------------------- + +.. function:: void da_push_back(da, const void *data) + + Pushes data to the back of the array. + + :param da: The dynamic array + :param data: Pointer to the new data to push + +--------------------- + +.. function:: void *da_push_back_new(da) + + Pushes a zeroed value to the back of the array, and returns a pointer + to it. + + :param da: The dynamic array + :return: Pointer to the new value + +--------------------- + +.. function:: void da_push_back_array(da, const void *src_array, size_t item_count) + + Pushes an array of values to the back of the array. + + :param da: The dynamic array + :param src_array: Pointer of the array of values + :param item_count: Number of items to push back + +--------------------- + +.. function:: void da_insert(da, size_t idx, const void *data) + + Inserts a value at a given index. + + :param da: The dynamic array: + :param idx: Index where the new item will be inserted + :param data: Pointer to the item data to insert + +--------------------- + +.. function:: void *da_insert_new(da, size_t idx) + + Inserts a new zeroed value at a specific index, and returns a pointer + to it. + + :param da: The dynamic array + :param idx: Index to insert at + :return: Pointer to the new value + +--------------------- + +.. function:: void da_insert_da(da_dst, size_t idx, da_src) + + Inserts a dynamic array in to another dynamic array at a specific + index. + + :param da_dst: Destination dynamic array being inserted in to + :param idx: Index to insert the data at + :param da_src: The dynamic array to insert + +--------------------- + +.. function:: void da_erase(da, size_t idx) + + Erases an item at a specific index. + + :param da: The dynamic array + :param idx: The index of the value to remove + +--------------------- + +.. function:: void da_erase_item(da, const void *item_data) + + Erases an item that matches the value specified + + :param da: The dynamic array + :param item_data: Pointer to the data to remove + +--------------------- + +.. function:: void da_erase_range(da, size_t start_idx, size_t end_idx) + + Erases a range of values. + + :param da: The dynamic array + :param start_idx: The starting index + :param end_idx: The ending index + +--------------------- + +.. function:: void da_pop_back(da) + + Removes one item from the end of a dynamic array. + + :param da: The dynamic array + +--------------------- + +.. function:: void da_join(da_dst, da_src) + + Pushes *da_src* to the end of *da_dst* and frees *da_src*. + + :param da_dst: The destination dynamic array + :param da_src: The source dynamic array + +--------------------- + +.. function:: void da_split(da_dst1, da_dst2, da_src, size_t split_idx) + + Creates two dynamic arrays by splitting another dynamic array at a + specific index. If the destination arrays are not freed, they will + be freed before getting their new values. The array being split will + not be freed. + + :param da_dst1: Dynamic array that will get the lower half + :param da_dst2: Dynamic array that will get the upper half + :param da_src: Dynamic array to split + :param split_idx: Index to split *da_src* at + +--------------------- + +.. function:: void da_move_item(da, size_t src_idx, size_t dst_idx) + + Moves an item from one index to another, moving data between if + necessary. + + :param da: The dynamic array + :param src_idx: The index of the item to move + :param dst_idx: The new index of where the item will be moved to + +--------------------- + +.. function:: void da_swap(da, size_t idx1, size_t idx2) + + Swaps two values at the given indices. + + :param da: The dynamic array + :param idx1: Index of the first item to swap + :param idx2: Index of the second item to swap diff --git a/docs/sphinx/reference-libobs-util-dstr.rst b/docs/sphinx/reference-libobs-util-dstr.rst new file mode 100644 index 000000000..20fb9115f --- /dev/null +++ b/docs/sphinx/reference-libobs-util-dstr.rst @@ -0,0 +1,490 @@ +Dynamic Strings And String Helpers +================================== + +Provides string helper structures/functions (roughly equivalent to +std::string). + +.. code:: cpp + + #include + + +Dynamic String Structure (struct dstr) +-------------------------------------- + +.. type:: struct dstr +.. member:: char *dstr.array +.. member:: size_t dstr.len +.. member:: size_t dstr.capacity + + +General String Helper Functions +------------------------------- + +.. function:: int astrcmpi(const char *str1, const char *str2) + + Case insensitive string comparison function. + +---------------------- + +.. function:: int wstrcmpi(const wchar_t *str1, const wchar_t *str2) + + Case insensitive wide string comparison function. + +---------------------- + +.. function:: int astrcmp_n(const char *str1, const char *str2, size_t n) + + String comparison function for a specific number of characters. + +---------------------- + +.. function:: int wstrcmp_n(const wchar_t *str1, const wchar_t *str2, size_t n) + + Wide string comparison function for a specific number of characters. + +---------------------- + +.. function:: int astrcmpi_n(const char *str1, const char *str2, size_t n) + + Case insensitive string comparison function for a specific number of + characters. + +---------------------- + +.. function:: int wstrcmpi_n(const wchar_t *str1, const wchar_t *str2, size_t n) + + Case insensitive wide string comparison function for a specific + number of characters. + +---------------------- + +.. function:: char *astrstri(const char *str, const char *find) + + Case insensitive version of strstr. + +---------------------- + +.. function:: wchar_t *wstrstri(const wchar_t *str, const wchar_t *find) + + Case insensitive version of wcsstr. + +---------------------- + +.. function:: char *strdepad(char *str) + + Removes padding characters (tab, space, CR, LF) from the front and + end of a string. + +---------------------- + +.. function:: wchar_t *wcsdepad(wchar_t *str) + + Removes padding characters (tab, space, CR, LF) from the front and + end of a wide string. + +---------------------- + +.. function:: char **strlist_split(const char *str, char split_ch, bool include_empty) + + Splits a string in to a list of multiple sub-strings. Free with + :c:func:`strlist_free()`. + +---------------------- + +.. function:: void strlist_free(char **strlist) + + Frees a string list created with :c:func:`strlist_split()`. + +--------------------- + + +Dynamic String Functions +------------------------ + +.. function:: void dstr_init(struct dstr *dst) + + Initializes a dynamic string variable (just zeroes the variable). + + :param dst: Dynamic string to initialize + +---------------------- + +.. function:: void dstr_init_move(struct dstr *dst, struct dstr *src) + + Moves a *src* to *dst* without copying data and zeroes *src*. + + :param dst: Destination + :param src: Source + +---------------------- + +.. function:: void dstr_init_move_array(struct dstr *dst, char *str) + + Sets a bmalloc-allocated string as the dynamic string without + copying/reallocating. + + :param dst: Dynamic string to initialize + :param str: bmalloc-allocated string + +---------------------- + +.. function:: void dstr_init_copy(struct dstr *dst, const char *src) + + Initializes a dynamic string with a copy of a string + + :param dst: Dynamic string to initialize + :param src: String to copy + +---------------------- + +.. function:: void dstr_init_copy_dstr(struct dstr *dst, const struct dstr *src) + + Initializes a dynamic string with a copy of another dynamic string + + :param dst: Dynamic string to initialize + :param src: Dynamic string to copy + +---------------------- + +.. function:: void dstr_free(struct dstr *dst) + + Frees a dynamic string. + + :param dst: Dynamic string + +---------------------- + +.. function:: void dstr_copy(struct dstr *dst, const char *array) + + Copies a string. + + :param dst: Dynamic string + :param array: String to copy + +---------------------- + +.. function:: void dstr_copy_dstr(struct dstr *dst, const struct dstr *src) + + Copies another dynamic string. + + :param dst: Dynamic string + :param src: Dynamic string to copy + +---------------------- + +.. function:: void dstr_ncopy(struct dstr *dst, const char *array, const size_t len) + + Copies a specific number of characters from a string. + + :param dst: Dynamic string + :param array: String to copy + :param len: Number of characters to copy + +---------------------- + +.. function:: void dstr_ncopy_dstr(struct dstr *dst, const struct dstr *src, const size_t len) + + Copies a specific number of characters from another dynamic string. + + :param dst: Dynamic string + :param src: Dynamic tring to copy + :param len: Number of characters to copy + +---------------------- + +.. function:: void dstr_resize(struct dstr *dst, const size_t num) + + Sets the size of the dynamic string. If the new size is bigger than + current size, zeroes the new characters. + + :param dst: Dynamic string + :param num: New size + +---------------------- + +.. function:: void dstr_reserve(struct dstr *dst, const size_t num) + + Reserves a specific number of characters in the buffer (but does not + change the size). Does not work if the value is smaller than the + current reserve size. + + :param dst: Dynamic string + :param num: New reserve size + +---------------------- + +.. function:: bool dstr_is_empty(const struct dstr *str) + + Returns whether the dynamic string is empty. + + :param str: Dynamic string + :return: *true* if empty, *false* otherwise + +---------------------- + +.. function:: void dstr_cat(struct dstr *dst, const char *array) + + Concatenates a dynamic string. + + :param dst: Dynamic string + :param array: String to concatenate with + +---------------------- + +.. function:: void dstr_cat_dstr(struct dstr *dst, const struct dstr *str) + + Concatenates a dyanmic string with another dynamic string. + + :param dst: Dynamic string to concatenate to + :param str: Dynamic string to concatenate with + +---------------------- + +.. function:: void dstr_cat_ch(struct dstr *dst, char ch) + + Concatenates a dynamic string with a single character. + + :param dst: Dynamic string to concatenate to + :param ch: Character to concatenate + +---------------------- + +.. function:: void dstr_ncat(struct dstr *dst, const char *array, const size_t len) + + Concatenates a dynamic string with a specific number of characters + from a string. + + :param dst: Dynamic string to concatenate to + :param array: String to concatenate with + :param len: Number of characters to concatenate with + +---------------------- + +.. function:: void dstr_ncat_dstr(struct dstr *dst, const struct dstr *str, const size_t len) + + Concatenates a dynamic string with a specific number of characters + from another dynamic string. + + :param dst: Dynamic string to concatenate to + :param str: Dynamic string to concatenate with + :param len: Number of characters to concatenate with + +---------------------- + +.. function:: void dstr_insert(struct dstr *dst, const size_t idx, const char *array) + + Inserts a string in to a dynamic string at a specific index. + + :param dst: Dynamic string to insert in to + :param idx: Character index to insert at + :param array: String to insert + +---------------------- + +.. function:: void dstr_insert_dstr(struct dstr *dst, const size_t idx, const struct dstr *str) + + Inserts another dynamic string in to a dynamic string at a specific + index. + + :param dst: Dynamic string to insert in to + :param idx: Character index to insert at + :param str: Dynamic string to insert + +---------------------- + +.. function:: void dstr_insert_ch(struct dstr *dst, const size_t idx, const char ch) + + Inserts a character in to a dynamic string at a specific index. + + :param dst: Dynamic string to insert in to + :param idx: Character index to insert at + :param ch: Character to insert + +---------------------- + +.. function:: void dstr_remove(struct dstr *dst, const size_t idx, const size_t count) + + Removes a specific number of characters starting from a specific + index. + + :param dst: Dyanmic string + :param idx: Index to start removing characters at + :param count: Number of characters to remove + +---------------------- + +.. function:: void dstr_printf(struct dstr *dst, const char *format, ...) + void dstr_vprintf(struct dstr *dst, const char *format, va_list args) + + Sets a dynamic string to a formatted string. + + :param dst: Dynamic string + :param format: Format string + +---------------------- + +.. function:: void dstr_catf(struct dstr *dst, const char *format, ...) + void dstr_vcatf(struct dstr *dst, const char *format, va_list args) + + Concatenates a dynamic string with a formatted string. + + :param dst: Dynamic string + :param format: Format string + +---------------------- + +.. function:: const char *dstr_find_i(const struct dstr *str, const char *find) + + Finds a string within a dynamic string, case insensitive. + + :param str: Dynamic string + :param find: String to find + :return: Pointer to the first occurrence, or *NULL* if not found + +---------------------- + +.. function:: const char *dstr_find(const struct dstr *str, const char *find) + + Finds a string within a dynamic string. + + :param str: Dynamic string + :param find: String to find + :return: Pointer to the first occurrence, or *NULL* if not found + +---------------------- + +.. function:: void dstr_replace(struct dstr *str, const char *find, const char *replace) + + Replaces all occurrences of *find* with *replace*. + + :param str: Dynamic string + :param find: String to find + :param replace: Replacement string + +---------------------- + +.. function:: int dstr_cmp(const struct dstr *str1, const char *str2) + + Compares with a string. + + :param str1: Dynamic string + :param str2: String to compare + :return: 0 if equal, nonzero otherwise + +---------------------- + +.. function:: int dstr_cmpi(const struct dstr *str1, const char *str2) + + Compares with a string, case-insensitive. + + :param str1: Dynamic string + :param str2: String to compare + :return: 0 if equal, nonzero otherwise + +---------------------- + +.. function:: int dstr_ncmp(const struct dstr *str1, const char *str2, const size_t n) + + Compares a specific number of characters. + + :param str1: Dynamic string + :param str2: String to compare + :param n: Number of characters to compare + :return: 0 if equal, nonzero otherwise + +---------------------- + +.. function:: int dstr_ncmpi(const struct dstr *str1, const char *str2, const size_t n) + + Compares a specific number of characters, case-insensitive. + + :param str1: Dynamic string + :param str2: String to compare + :param n: Number of characters to compare + :return: 0 if equal, nonzero otherwise + +---------------------- + +.. function:: void dstr_depad(struct dstr *dst) + + Removes all padding characters (tabs, spaces, CR, LF) from the front + and ends of a dynamic string. + + :param dst: Dynamic string + +---------------------- + +.. function:: void dstr_left(struct dstr *dst, const struct dstr *str, const size_t pos) + + Copies a certain number of characters from the left side of one + dynamic string in to another. + + :param dst: Destination + :param str: Source + :param pos: Number of characters + +---------------------- + +.. function:: void dstr_mid(struct dstr *dst, const struct dstr *str, const size_t start, const size_t count) + + Copies a certain number of characters from the middle of one dynamic + string in to another. + + :param dst: Destination + :param str: Source + :param start: Starting index within *str* + :param count: Number of characters to copy + +---------------------- + +.. function:: void dstr_right(struct dstr *dst, const struct dstr *str, const size_t pos) + + Copies a certain number of characters from the right of one dynamic + string in to another. + + :param dst: Destination + :param str: Source + :param pos: Index of *str* to copy from + +---------------------- + +.. function:: char dstr_end(const struct dstr *str) + + :param str: Dynamic string + :return: The last character of a dynamic string + +---------------------- + +.. function:: void dstr_from_wcs(struct dstr *dst, const wchar_t *wstr) + + Copies a wide string in to a dynamic string and converts it to UTF-8. + + :param dst: Dynamic string + :param wstr: Wide string + +---------------------- + +.. function:: wchar_t *dstr_to_wcs(const struct dstr *str) + + Converts a dynamic array to a wide string. Free with + :c:func:`bfree()`. + + :param str: Dynamic string + :return: Wide string allocation. Free with :c:func:`bfree()` + +---------------------- + +.. function:: void dstr_to_upper(struct dstr *str) + + Converts all characters within a dynamic array to uppercase. + + :param str: Dynamic string + +---------------------- + +.. function:: void dstr_to_lower(struct dstr *str) + + Converts all characters within a dynamic array to lowercase. + + :param str: Dynamic string diff --git a/docs/sphinx/reference-libobs-util-platform.rst b/docs/sphinx/reference-libobs-util-platform.rst new file mode 100644 index 000000000..6c059d250 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-platform.rst @@ -0,0 +1,465 @@ +Platform Helpers +================ + +These functions/structures/types are used to perform actions that +typically don't have shared functions across different operating systems +and platforms. + +.. code:: cpp + + #include + + +File Functions +-------------- + +.. function:: FILE *os_wfopen(const wchar_t *path, const char *mode) + + Opens a file with a wide string path. + +---------------------- + +.. function:: FILE *os_fopen(const char *path, const char *mode) + + Opens a file with a UTF8 string path. + +---------------------- + +.. function:: int64_t os_fgetsize(FILE *file) + + Returns a file's size. + +---------------------- + +.. function:: int os_stat(const char *file, struct stat *st) + + Equivalent to the posix *stat* function. + +---------------------- + +.. function:: int os_fseeki64(FILE *file, int64_t offset, int origin) + + Equivalent to fseek. + +---------------------- + +.. function:: int64_t os_ftelli64(FILE *file) + + Gets the current file position. + +---------------------- + +.. function:: size_t os_fread_utf8(FILE *file, char **pstr) + + Reads a UTF8 encoded file and allocates a pointer to the UTF8 string. + +---------------------- + +.. function:: char *os_quick_read_utf8_file(const char *path) + + Reads a UTF8 encoded file and returns an allocated pointer to the + string. + +---------------------- + +.. function:: bool os_quick_write_utf8_file(const char *path, const char *str, size_t len, bool marker) + + Writes a UTF8 encoded file. + +---------------------- + +.. function:: bool os_quick_write_utf8_file_safe(const char *path, const char *str, size_t len, bool marker, const char *temp_ext, const char *backup_ext) + + Writes a UTF8 encoded file with overwrite corruption prevention. + +---------------------- + +.. function:: int64_t os_get_file_size(const char *path) + + Gets a file's size. + +---------------------- + +.. function:: int64_t os_get_free_space(const char *path) + + Gets free space of a specific file path. + +--------------------- + + +String Conversion Functions +--------------------------- + +.. function:: size_t os_utf8_to_wcs(const char *str, size_t len, wchar_t *dst, size_t dst_size) + + Converts a UTF8 string to a wide string. + +---------------------- + +.. function:: size_t os_wcs_to_utf8(const wchar_t *str, size_t len, char *dst, size_t dst_size) + + Converts a wide string to a UTF8 string. + +---------------------- + +.. function:: size_t os_utf8_to_wcs_ptr(const char *str, size_t len, wchar_t **pstr) + + Gets an bmalloc-allocated wide string converted from a UTF8 string. + +---------------------- + +.. function:: size_t os_wcs_to_utf8_ptr(const wchar_t *str, size_t len, char **pstr) + + Gets an bmalloc-allocated UTF8 string converted from a wide string. + +--------------------- + + +Number/String Conversion Functions +---------------------------------- + +.. function:: double os_strtod(const char *str) + + Converts a string to a double. + +---------------------- + +.. function:: int os_dtostr(double value, char *dst, size_t size) + + Converts a double to a string. + +--------------------- + + +Dynamic Link Library Functions +------------------------------ + +These functions are roughly equivalent to dlopen/dlsym/dlclose. + +.. function:: void *os_dlopen(const char *path) + + Opens a dynamic library. + +---------------------- + +.. function:: void *os_dlsym(void *module, const char *func) + + Returns a symbol from a dynamic library. + +---------------------- + +.. function:: void os_dlclose(void *module) + + Closes a dynamic library. + +--------------------- + + +CPU Usage Functions +------------------- + +.. function:: os_cpu_usage_info_t *os_cpu_usage_info_start(void) + + Creates a CPU usage information object. + +---------------------- + +.. function:: double os_cpu_usage_info_query(os_cpu_usage_info_t *info) + + Queries the current CPU usage. + +---------------------- + +.. function:: void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info) + + Destroys a CPU usage information object. + +--------------------- + + +Sleep/Time Functions +-------------------- + +.. function:: bool os_sleepto_ns(uint64_t time_target) + + Sleeps to a specific time with high precision, in nanoseconds. + +--------------------- + +.. function:: void os_sleep_ms(uint32_t duration) + + Sleeps for a specific number of milliseconds. + +--------------------- + +.. function:: uint64_t os_gettime_ns(void) + + Gets the current high-precision system time, in nanoseconds. + +--------------------- + +Other Path/File Functions +------------------------- + +.. function:: int os_get_config_path(char *dst, size_t size, const char *name) + char *os_get_config_path_ptr(const char *name) + + Gets the user-specific application configuration data path. + +--------------------- + +.. function:: int os_get_program_data_path(char *dst, size_t size, const char *name) + char *os_get_program_data_path_ptr(const char *name) + + Gets the application configuration data path. + +--------------------- + +.. function:: bool os_file_exists(const char *path) + + Returns true if a file/directory exists, false otherwise. + +--------------------- + +.. function:: size_t os_get_abs_path(const char *path, char *abspath, size_t size) + char *os_get_abs_path_ptr(const char *path) + + Converts a relative path to an absolute path. + +--------------------- + +.. function:: const char *os_get_path_extension(const char *path) + + Returns the extension portion of a path string. + +--------------------- + +.. type:: typedef struct os_dir os_dir_t + + A directory object. + +.. type:: struct os_dirent + + A directory entry record. + +.. member:: char os_dirent.d_name[256] + + The directory entry name. + +.. member:: bool os_dirent.directory + + *true* if the entry is a directory. + +--------------------- + +.. function:: os_dir_t *os_opendir(const char *path) + + Opens a directory object to enumerate files within the directory. + +--------------------- + +.. function:: struct os_dirent *os_readdir(os_dir_t *dir) + + Returns the linked list of directory entries. + +--------------------- + +.. function:: void os_closedir(os_dir_t *dir) + + Closes a directory object. + +--------------------- + +.. type:: struct os_globent + + A glob entry. + +.. member:: char *os_globent.path + + The full path to the glob entry. + +.. member:: bool os_globent.directory + + *true* if the glob entry is a directory, *false* otherwise. + +.. type:: struct os_glob_info + + A glob object. + +.. member:: size_t os_glob_info.gl_pathc + + Number of glob entries. + +.. member:: struct os_globent *os_glob_info.gl_pathv + + Array of glob entries. + +.. type:: typedef struct os_glob_info os_glob_t + +--------------------- + +.. function:: int os_glob(const char *pattern, int flags, os_glob_t **pglob) + + Enumerates files based upon a glob string. + +--------------------- + +.. function:: void os_globfree(os_glob_t *pglob) + + Frees a glob object. + +--------------------- + +.. function:: int os_unlink(const char *path) + + Deletes a file. + +--------------------- + +.. function:: int os_rmdir(const char *path) + + Deletes a directory. + +--------------------- + +.. function:: char *os_getcwd(char *path, size_t size) + + Returns a new bmalloc-allocated path to the current working + directory. + +--------------------- + +.. function:: int os_chdir(const char *path) + + Changes the current working directory. + +--------------------- + +.. function:: int os_mkdir(const char *path) + + Creates a directory. + +--------------------- + +.. function:: int os_mkdirs(const char *path) + + Creates a full directory path if it doesn't exist. + +--------------------- + +.. function:: int os_rename(const char *old_path, const char *new_path) + + Renames a file. + +--------------------- + +.. function:: int os_copyfile(const char *file_in, const char *file_out) + + Copys a file. + +--------------------- + +.. function:: int os_safe_replace(const char *target_path, const char *from_path, const char *backup_path) + + Safely replaces a file. + +--------------------- + +.. function:: char *os_generate_formatted_filename(const char *extension, bool space, const char *format) + + Returns a new bmalloc-allocated filename generated from specific + formatting. + +--------------------- + + +Sleep-Inhibition Functions +-------------------------- + +These functions/types are used to inhibit the computer from going to +sleep. + +.. type:: struct os_inhibit_info +.. type:: typedef struct os_inhibit_info os_inhibit_t + +--------------------- + +.. function:: os_inhibit_t *os_inhibit_sleep_create(const char *reason) + + Creates a sleep inhibition object. + +--------------------- + +.. function:: bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active) + + Activates/deactivates a sleep inhibition object. + +--------------------- + +.. function:: void os_inhibit_sleep_destroy(os_inhibit_t *info) + + Destroys a sleep inhibition object. If the sleep inhibition object + was active, it will be deactivated. + +--------------------- + + +Other Functions +--------------- + +.. function:: void os_breakpoint(void) + + Triggers a debugger breakpoint (or crashes the program if no debugger + present). + +--------------------- + +.. function:: int os_get_physical_cores(void) + + Returns the number of physical cores available. + +--------------------- + +.. function:: int os_get_logical_cores(void) + + Returns the number of logical cores available. + +--------------------- + +.. function:: uint64_t os_get_sys_free_size(void) + + Returns the amount of memory available. + +--------------------- + +.. type:: struct os_proc_memory_usage + + Memory usage structure. + +.. member:: uint64_t os_proc_memory_usage.resident_size + + Resident size. + +.. member:: uint64_t os_proc_memory_usage.virtual_size + + Virtual size. + +.. type:: typedef struct os_proc_memory_usage os_proc_memory_usage_t + +--------------------- + +.. function:: bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage) + + Gets memory usage of the current process. + +--------------------- + +.. function:: uint64_t os_get_proc_resident_size(void) + + Returns the resident memory size of the current process. + +--------------------- + +.. function:: uint64_t os_get_proc_virtual_size(void) + + Returns the virtual memory size of the current process. diff --git a/docs/sphinx/reference-libobs-util-profiler.rst b/docs/sphinx/reference-libobs-util-profiler.rst new file mode 100644 index 000000000..b749bf5f2 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-profiler.rst @@ -0,0 +1,327 @@ +Profiler +======== + +The profiler is used to get information about program performance and +efficiency. + +.. type:: typedef struct profiler_snapshot profiler_snapshot_t +.. type:: typedef struct profiler_snapshot_entry profiler_snapshot_entry_t +.. type:: typedef struct profiler_name_store profiler_name_store_t +.. type:: typedef struct profiler_time_entry profiler_time_entry_t + +.. code:: cpp + + #include + + +Profiler Structures +------------------- + +.. type:: struct profiler_time_entry +.. member:: uint64_t profiler_time_entry.time_delta +.. member:: uint64_t profiler_time_entry.count + + +Profiler Control Functions +-------------------------- + +.. function:: void profiler_start(void) + + Starts the profiler. + +---------------------- + +.. function:: void profiler_stop(void) + + Stops the profiler. + +---------------------- + +.. function:: void profiler_print(profiler_snapshot_t *snap) + + Creates a profiler snapshot and saves it within *snap*. + + :param snap: A profiler snapshot object + +---------------------- + +.. function:: void profiler_print_time_between_calls(profiler_snapshot_t *snap) + + Creates a profiler snapshot of time between calls and saves it within + *snap*. + + :param snap: A profiler snapshot object + +---------------------- + +.. function:: void profiler_free(void) + + Frees the profiler. + +---------------------- + + +Profiling Functions +------------------- + +.. function:: void profile_register_root(const char *name, uint64_t expected_time_between_calls) + + Registers a root profile node. + + :param name: Name of the root profile node + :param expected_time_between_calls: The expected time between calls + of the profile root node, or 0 if + none. + +---------------------- + +.. function:: void profile_start(const char *name) + + Starts a profile node. This profile node will be a child of the last + node that was started. + + :param name: Name of the profile node + +---------------------- + +.. function:: void profile_end(const char *name) + + :param name: Name of the profile node + +---------------------- + +.. function:: void profile_reenable_thread(void) + + Because :c:func:`profiler_start()` can be called in a different + thread than the current thread, this is used to specify a point where + it's safe to re-enable profiling in the calling thread. Call this + when you have looped root profile nodes and need to specify a safe + point where the root profile node isn't active and the profiler can + start up in the current thread again. + +---------------------- + + +Profiler Name Storage Functions +------------------------------- + +.. function:: profiler_name_store_t *profiler_name_store_create(void) + + Creates a profiler name storage object. + + :return: Profiler name store object + +---------------------- + +.. function:: void profiler_name_store_free(profiler_name_store_t *store) + + Frees a profiler name storage object. + + :param store: Profiler name storage object + +---------------------- + +.. function:: const char *profile_store_name(profiler_name_store_t *store, const char *format, ...) + + Creates a formatted string and stores it within a profiler name + storage object. + + :param store: Profiler name storage object + :param format: Formatted string + :return: The string created from format specifications + +---------------------- + + +Profiler Data Access Functions +------------------------------ + +.. function:: profiler_snapshot_t *profile_snapshot_create(void) + + Creates a profile snapshot. Profiler snapshots are used to obtain + data about how the active profiles performed. + + :return: A profiler snapshot object + +---------------------- + +.. function:: void profile_snapshot_free(profiler_snapshot_t *snap) + + Frees a profiler snapshot object. + + :param snap: A profiler snapshot + +---------------------- + +.. function:: bool profiler_snapshot_dump_csv(const profiler_snapshot_t *snap, const char *filename) + + Creates a CSV file of the profiler snapshot. + + :param snap: A profiler snapshot + :param filename: The path to the CSV file to save + :return: *true* if successfuly written, *false* otherwise + +---------------------- + +.. function:: bool profiler_snapshot_dump_csv_gz(const profiler_snapshot_t *snap, const char *filename) + + Creates a gzipped CSV file of the profiler snapshot. + + :param snap: A profiler snapshot + :param filename: The path to the gzipped CSV file to save + :return: *true* if successfuly written, *false* otherwise + +---------------------- + +.. function:: size_t profiler_snapshot_num_roots(profiler_snapshot_t *snap) + + :param snap: A profiler snapshot + :return: Number of root profiler nodes in the snapshot + +---------------------- + +.. type:: typedef bool (*profiler_entry_enum_func)(void *context, profiler_snapshot_entry_t *entry) + + Profiler snapshot entry numeration callback + + :param context: Private data passed to this callback + :param entry: Profiler snapshot entry + :return: *true* to continue enumeration, *false* otherwise + +---------------------- + +.. function:: void profiler_snapshot_enumerate_roots(profiler_snapshot_t *snap, profiler_entry_enum_func func, void *context) + + Enumerates root profile nodes. + + :param snap: A profiler snapshot + :param func: Enumeration callback + :param context: Private data to pass to the callback + +---------------------- + +.. type:: typedef bool (*profiler_name_filter_func)(void *data, const char *name, bool *remove) + + Callback used to determine what profile nodes are removed/filtered. + + :param data: Private data passed to this callback + :param name: Profile node name to be filtered + :param remove: Used to determined whether the node should be removed + or not + :return: *true* to continue enumeration, *false* otherwise + +---------------------- + +.. function:: void profiler_snapshot_filter_roots(profiler_snapshot_t *snap, profiler_name_filter_func func, void *data) + + Removes/filters profile roots based upon their names. + + :param snap: A profiler snapshot + :param func: Enumeration callback to filter with + :param data: Private data to pass to the callback + +---------------------- + +.. function:: size_t profiler_snapshot_num_children(profiler_snapshot_entry_t *entry) + + :param entry: A profiler snapshot entry + :return: Number of children for the entry + +---------------------- + +.. function:: void profiler_snapshot_enumerate_children(profiler_snapshot_entry_t *entry, profiler_entry_enum_func func, void *context) + + Enumerates child entries of a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :param func: Enumeration callback + :param context: Private data passed to the callback + +---------------------- + +.. function:: const char *profiler_snapshot_entry_name(profiler_snapshot_entry_t *entry) + + :param entry: A profiler snapshot entry + :return: The name of the profiler snapshot entry + +---------------------- + +.. function:: profiler_time_entries_t *profiler_snapshot_entry_times(profiler_snapshot_entry_t *entry) + + Gets the time entries for a snapshot entry. + + :param entry: A profiler snapshot entry + :return: An array of profiler time entries + +---------------------- + +.. function:: uint64_t profiler_snapshot_entry_min_time(profiler_snapshot_entry_t *entry) + + Gets the minimum time for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: The minimum time value for the snapshot entry + +---------------------- + +.. function:: uint64_t profiler_snapshot_entry_max_time(profiler_snapshot_entry_t *entry) + + Gets the maximum time for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: The maximum time value for the snapshot entry + +---------------------- + +.. function:: uint64_t profiler_snapshot_entry_overall_count(profiler_snapshot_entry_t *entry) + + Gets the overall count for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: The overall count value for the snapshot entry + +---------------------- + +.. function:: profiler_time_entries_t *profiler_snapshot_entry_times_between_calls(profiler_snapshot_entry_t *entry) + + Gets an array of time between calls for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: An array of profiler time entries + +---------------------- + +.. function:: uint64_t profiler_snapshot_entry_expected_time_between_calls(profiler_snapshot_entry_t *entry) + + Gets the expected time between calls for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: The expected time between calls for the snapshot entry, + or 0 if not set + +---------------------- + +.. function:: uint64_t profiler_snapshot_entry_min_time_between_calls(profiler_snapshot_entry_t *entry) + + Gets the minimum time seen between calls for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: The minimum time seen between calls for the snapshot entry + +---------------------- + +.. function:: uint64_t profiler_snapshot_entry_max_time_between_calls(profiler_snapshot_entry_t *entry) + + Gets the maximum time seen between calls for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: The maximum time seen between calls for the snapshot entry + +---------------------- + +.. function:: uint64_t profiler_snapshot_entry_overall_between_calls_count(profiler_snapshot_entry_t *entry) + + Gets the overall time between calls for a profiler snapshot entry. + + :param entry: A profiler snapshot entry + :return: The overall time between calls for the snapshot entry diff --git a/docs/sphinx/reference-libobs-util-serializers.rst b/docs/sphinx/reference-libobs-util-serializers.rst new file mode 100644 index 000000000..51c6a5f0c --- /dev/null +++ b/docs/sphinx/reference-libobs-util-serializers.rst @@ -0,0 +1,171 @@ +Serializer +========== + +General programmable serialization functions. (A shared interface to +various reading/writing to/from different inputs/outputs) + +.. code:: cpp + + #include + +Serializer Structure (struct serializer) +---------------------------------------- + +.. type:: struct serializer +.. member:: void *serializer.data +.. member:: size_t (*serializer.read)(void *, void *, size_t) +.. member:: size_t (*serializer.write)(void *, const void *, size_t) +.. member:: int64_t (*serializer.seek)(void *, int64_t, enum serialize_seek_type) +.. member:: int64_t (*serializer.get_pos)(void *) + +Serializer Inline Functions +--------------------------- + +.. function:: size_t s_read(struct serializer *s, void *data, size_t size) + +--------------------- + +.. function:: size_t s_write(struct serializer *s, const void *data, size_t size) + +--------------------- + +.. function:: size_t serialize(struct serializer *s, void *data, size_t len) + +--------------------- + +.. function:: int64_t serializer_seek(struct serializer *s, int64_t offset, enum serialize_seek_type seek_type) + +--------------------- + +.. function:: int64_t serializer_get_pos(struct serializer *s) + +--------------------- + +.. function:: void s_w8(struct serializer *s, uint8_t u8) + +--------------------- + +.. function:: void s_wl16(struct serializer *s, uint16_t u16) + +--------------------- + +.. function:: void s_wl32(struct serializer *s, uint32_t u32) + +--------------------- + +.. function:: void s_wl64(struct serializer *s, uint64_t u64) + +--------------------- + +.. function:: void s_wlf(struct serializer *s, float f) + +--------------------- + +.. function:: void s_wld(struct serializer *s, double d) + +--------------------- + +.. function:: void s_wb16(struct serializer *s, uint16_t u16) + +--------------------- + +.. function:: void s_wb24(struct serializer *s, uint32_t u24) + +--------------------- + +.. function:: void s_wb32(struct serializer *s, uint32_t u32) + +--------------------- + +.. function:: void s_wb64(struct serializer *s, uint64_t u64) + +--------------------- + +.. function:: void s_wbf(struct serializer *s, float f) + +--------------------- + +.. function:: void s_wbd(struct serializer *s, double d) + +--------------------- + + +Array Output Serializer +======================= + +Provides an output serializer used with dynamic arrays. + +.. code:: cpp + + #include + +Array Output Serializer Structure (struct array_output_data) +------------------------------------------------------------ + +.. type:: struct array_output_data + +.. member:: DARRAY(uint8_t) array_output_data.bytes + +Array Output Serializer Functions +--------------------------------- + +.. function:: void array_output_serializer_init(struct serializer *s, struct array_output_data *data) + +--------------------- + +.. function:: void array_output_serializer_free(struct array_output_data *data) + +--------------------- + + +File Input/Output Serializers +============================= + +Provides file reading/writing serializers. + +.. code:: cpp + + #include + + +File Input Serializer Functions +------------------------------- + +.. function:: bool file_input_serializer_init(struct serializer *s, const char *path) + + Initializes a file input serializer. + + :return: *true* if file opened successfully, *false* otherwise + +--------------------- + +.. function:: void file_input_serializer_free(struct serializer *s) + + Frees a file input serializer. + +--------------------- + + +File Output Serializer Functions +-------------------------------- + +.. function:: bool file_output_serializer_init(struct serializer *s, const char *path) + + Initializes a file output serializer. + + :return: *true* if file created successfully, *false* otherwise + +--------------------- + +.. function:: bool file_output_serializer_init_safe(struct serializer *s, const char *path, const char *temp_ext) + + Initializes and safely writes to a temporary file (determined by the + temporary extension) until freed. + + :return: *true* if file created successfully, *false* otherwise + +--------------------- + +.. function:: void file_output_serializer_free(struct serializer *s) + + Frees the file output serializer and saves the file. diff --git a/docs/sphinx/reference-libobs-util-text-lookup.rst b/docs/sphinx/reference-libobs-util-text-lookup.rst new file mode 100644 index 000000000..5b5f1530d --- /dev/null +++ b/docs/sphinx/reference-libobs-util-text-lookup.rst @@ -0,0 +1,56 @@ +Text Lookup Interface +===================== + +Used for storing and looking up localized strings. Uses an ini-file +like file format for localization lookup. + +.. type:: struct text_lookup lookup_t + +.. code:: cpp + + #include + + +Text Lookup Functions +--------------------- + +.. function:: lookup_t *text_lookup_create(const char *path) + + Creates a text lookup object from a text lookup file. + + :param path: Path to the localization file + :return: New lookup object, or *NULL* if an error occurred + +--------------------- + +.. function:: bool text_lookup_add(lookup_t *lookup, const char *path) + + Adds text lookup from a text lookup file and replaces any values. + For example, you would load a default fallback language such as + english with :c:func:`text_lookup_create()`, and then call this + function to load the actual desired language in case the desired + language isn't fully translated. + + :param lookup: Lookup object + :param path: Path to the localization file + :return: *true* if successful, *false* otherwise + +--------------------- + +.. function:: void text_lookup_destroy(lookup_t *lookup) + + Destroys a text lookup object. + + :param lookup: Lookup object + +--------------------- + +.. function:: bool text_lookup_getstr(lookup_t *lookup, const char *lookup_val, const char **out) + + Gets a localized text string. + + :param lookup: Lookup object + :param lookup_val: Value to look up + :param out: Pointer that receives the translated string + pointer + :return: *true* if the value exists, *false* otherwise diff --git a/docs/sphinx/reference-libobs-util-threading.rst b/docs/sphinx/reference-libobs-util-threading.rst new file mode 100644 index 000000000..badd029f8 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-threading.rst @@ -0,0 +1,193 @@ +Threading +========= + +Libobs provides a number of helper functions/types specifically for +threading. The threading header will additionally provide access to +pthread functions even on windows. + +.. code:: cpp + + #include + + +Threading Types +--------------- + +.. type:: os_event_t +.. type:: os_sem_t + + +General Thread Functions +------------------------ + +.. function:: void os_set_thread_name(const char *name) + + Sets the name of the current thread. + +---------------------- + + +Event Functions +--------------- + +.. function:: int os_event_init(os_event_t **event, enum os_event_type type) + + Creates an event object. + + :param event: Pointer that receives a pointer to a new event object + :param type: Can be one of the following values: + + - OS_EVENT_TYPE_AUTO - Automatically resets when + signaled + - OS_EVENT_TYPE_MANUAL - Stays signaled until the + :c:func:`os_event_reset()` function is called + + :return: 0 if successful, negative otherwise + +---------------------- + +.. function:: void os_event_destroy(os_event_t *event) + + Destroys an event. + + :param event: An event object + +---------------------- + +.. function:: int os_event_wait(os_event_t *event) + + Waits for an event to signal. + + :param event: An event object + :return: 0 if successful, negative otherwise + +---------------------- + +.. function:: int os_event_timedwait(os_event_t *event, unsigned long milliseconds) + + Waits a specific duration for an event to signal. + + :param event: An event object + :param milliseconds: Milliseconds to wait + :return: Can be one of the following values: + + - 0 - successful + - ETIMEDOUT - Timed out + - EINVAL - An unexpected error occured + +---------------------- + +.. function:: int os_event_try(os_event_t *event) + + Checks for a signaled state without waiting. + + :param event: An event object + :return: Can be one of the following values: + + - 0 - successful + - EAGAIN - The event is not signaled + - EINVAL - An unexpected error occured + +---------------------- + +.. function:: int os_event_signal(os_event_t *event) + + Signals the event. + + :param event: An event object + :return: 0 if successful, negative otherwise + +---------------------- + +.. function:: void os_event_reset(os_event_t *event) + + Resets the signaled state of the event. + + :param event: An event object + +---------------------- + + +Semaphore Functions +------------------- + +.. function:: int os_sem_init(os_sem_t **sem, int value) + + Creates a semaphore object. + + :param sem: Pointer that receives a pointer to the semaphore object + :param value: Initial value of the semaphore + :return: 0 if successful, negative otherwise + +---------------------- + +.. function:: void os_sem_destroy(os_sem_t *sem) + + Destroys a sempahore object. + + :param sem: Semaphore object + +---------------------- + +.. function:: int os_sem_post(os_sem_t *sem) + + Increments the semaphore. + + :param sem: Semaphore object + :return: 0 if successful, negative otherwise + +---------------------- + +.. function:: int os_sem_wait(os_sem_t *sem) + + Decrements the semphore or waits until the semaphore has been + incremented. + + :param sem: Semaphore object + :return: 0 if successful, negative otherwise + +--------------------- + + +Atomic Inline Functions +----------------------- + +.. function:: long os_atomic_inc_long(volatile long *val) + + Increments a long variable atomically. + +--------------------- + +.. function:: long os_atomic_dec_long(volatile long *val) + + Decrements a long variable atomically. + +--------------------- + +.. function:: long os_atomic_set_long(volatile long *ptr, long val) + + Sets the value of a long variable atomically. + +--------------------- + +.. function:: long os_atomic_load_long(const volatile long *ptr) + + Gets the value of a long variable atomically. + +--------------------- + +.. function:: bool os_atomic_compare_swap_long(volatile long *val, long old_val, long new_val) + + Swaps the value of a long variable atomically if its value matches. + +--------------------- + +.. function:: bool os_atomic_set_bool(volatile bool *ptr, bool val) + + Sets the value of a boolean variable atomically. + +--------------------- + +.. function:: bool os_atomic_load_bool(const volatile bool *ptr) + + Gets the value of a boolean variable atomically. diff --git a/docs/sphinx/reference-libobs-util.rst b/docs/sphinx/reference-libobs-util.rst new file mode 100644 index 000000000..1490bc205 --- /dev/null +++ b/docs/sphinx/reference-libobs-util.rst @@ -0,0 +1,17 @@ +Platform/Utility API Reference (libobs/util) +============================================ + +.. toctree:: + :maxdepth: 2 + + reference-libobs-util-base + reference-libobs-util-bmem + reference-libobs-util-circlebuf + reference-libobs-util-config-file + reference-libobs-util-darray + reference-libobs-util-dstr + reference-libobs-util-platform + reference-libobs-util-profiler + reference-libobs-util-serializers + reference-libobs-util-text-lookup + reference-libobs-util-threading diff --git a/docs/sphinx/reference-modules.rst b/docs/sphinx/reference-modules.rst new file mode 100644 index 000000000..da569a39d --- /dev/null +++ b/docs/sphinx/reference-modules.rst @@ -0,0 +1,310 @@ +Module API Reference +==================== + +Modules add custom functionality to libobs: typically +:ref:`plugins_sources`, :ref:`plugins_outputs`, :ref:`plugins_encoders`, +and :ref:`plugins_services`. + +.. type:: obs_module_t + + A module object (not reference counted). + +.. code:: cpp + + #include + + +Module Macros +------------- + +These macros are used within custom plugin modules. + +.. function:: OBS_DECLARE_MODULE() + + Declares a libobs module. Exports important core module functions + related to the module itself, OBS version, etc. + +--------------------- + +.. function:: OBS_MODULE_USE_DEFAULT_LOCALE(module_name, default_locale) + + Helper macro that uses the standard ini file format for localization. + Automatically initializes and destroys localization data, and + automatically provides module externs such as + :c:func:`obs_module_text()` to be able to get a localized string with + little effort. + +--------------------- + +Module Exports +-------------- + +These are functions that plugin modules can optionally export in order +to communicate with libobs and front-ends. + +.. function:: bool obs_module_load(void) + + Required: Called when the module is loaded. Implement this function + to load all the sources/encoders/outputs/services for your module, or + anything else that may need loading. + + :return: Return true to continue loading the module, otherwise + false to indicate failure and unload the module + +--------------------- + +.. function:: void obs_module_unload(void) + + Optional: Called when the module is unloaded. + +--------------------- + +.. function:: void obs_module_post_load(void) + + Optional: Called when all modules have finished loading. + +--------------------- + +.. function:: void obs_module_set_locale(const char *locale) + + Called to set the locale language and load the locale data for the + module. + +--------------------- + +.. function:: void obs_module_free_locale(void) + + Called on module destruction to free locale data. + +--------------------- + +.. function:: const char *obs_module_name(void) + + (Optional) + + :return: The full name of the module + +--------------------- + +.. function:: const char *obs_module_description(void) + + (Optional) + + :return: A description of the module + +--------------------- + + +Module Externs +-------------- + +These functions are externs that are useable throughout the module. + +.. function:: const char *obs_module_text(const char *lookup_string) + + :return: A localized string + +--------------------- + +.. function:: bool obs_module_get_string(const char *lookup_string, const char **translated_string) + + Helper function for looking up locale. + + :return: *true* if text found, otherwise *false* + +--------------------- + +.. function:: obs_module_t *obs_current_module(void) + + :return: The current module + +--------------------- + +.. function:: char *obs_module_file(const char *file) + + Returns the location to a module data file associated with the + current module. Free with :c:func:`bfree()` when complete. + + Equivalent to: + +.. code:: cpp + + obs_find_module_file(obs_current_module(), file); + +--------------------- + +.. function:: char *obs_module_config_path(const char *file) + + Returns the location to a module config file associated with the + current module. Free with :c:func:`bfree()` when complete. Will + return NULL if configuration directory is not set. + + Equivalent to: + +.. code:: cpp + + obs_module_get_config_path(obs_current_module(), file); + +--------------------- + + +Frontend Module Functions +-------------------------- + +These are functions used by frontends to load and get information about +plugin modules. + +.. function:: int obs_open_module(obs_module_t **module, const char *path, const char *data_path) + + Opens a plugin module directly from a specific path. + + If the module already exists then the function will return successful, and + the module parameter will be given the pointer to the existing + module. + + This does not initialize the module, it only loads the module image. To + initialize the module, call :c:func:`obs_init_module()`. + + :param module: The pointer to the created module + :param path: Specifies the path to the module library file. If the + extension is not specified, it will use the extension + appropriate to the operating system + :param data_path: Specifies the path to the directory where the module's + data files are stored (or *NULL* if none) + :returns: | MODULE_SUCCESS - Successful + | MODULE_ERROR - A generic error occurred + | MODULE_FILE_NOT_FOUND - The module was not found + | MODULE_MISSING_EXPORTS - Required exports are missing + | MODULE_INCOMPATIBLE_VER - Incompatible version + +--------------------- + +.. function:: bool obs_init_module(obs_module_t *module) + + Initializes the module, which calls its obs_module_load export. + + :return: *true* if the module was loaded successfully + +--------------------- + +.. function:: void obs_log_loaded_modules(void) + + Logs loaded modules. + +--------------------- + +.. function:: const char *obs_get_module_file_name(obs_module_t *module) + + :return: The module file name + +--------------------- + +.. function:: const char *obs_get_module_name(obs_module_t *module) + + :return: The module full name (or *NULL* if none) + +--------------------- + +.. function:: void obs_get_module_author(obs_module_t *module) + + :return: The module author(s) + +--------------------- + +.. function:: const char *obs_get_module_description(obs_module_t *module) + + :return: The module description + +--------------------- + +.. function:: const char *obs_get_module_binary_path(obs_module_t *module) + + :return: The module binary path + +--------------------- + +.. function:: const char *obs_get_module_data_path(obs_module_t *module) + + :return: The module data path + +--------------------- + +.. function:: void obs_add_module_path(const char *bin, const char *data) + + Adds a module search path to be used with obs_find_modules. If the search + path strings contain %module%, that text will be replaced with the module + name when used. + + :param bin: Specifies the module's binary directory search path + :param data: Specifies the module's data directory search path + +--------------------- + +.. function:: void obs_load_all_modules(void) + + Automatically loads all modules from module paths (convenience function). + +--------------------- + +.. function:: void obs_post_load_modules(void) + + Notifies modules that all modules have been loaded. + +--------------------- + +.. function:: void obs_find_modules(obs_find_module_callback_t callback, void *param) + + Finds all modules within the search paths added by + :c:func:`obs_add_module_path()`. + + Relevant data types used with this function: + +.. code:: cpp + + struct obs_module_info { + const char *bin_path; + const char *data_path; + }; + + typedef void (*obs_find_module_callback_t)(void *param, + const struct obs_module_info *info); + +--------------------- + +.. function:: void obs_enum_modules(obs_enum_module_callback_t callback, void *param) + + Enumerates all loaded modules. + + Relevant data types used with this function: + +.. code:: cpp + + typedef void (*obs_enum_module_callback_t)(void *param, obs_module_t *module); + +--------------------- + +.. function:: char *obs_find_module_file(obs_module_t *module, const char *file) + + Returns the location of a plugin module data file. + + Note: Modules should use obs_module_file function defined in obs-module.h + as a more elegant means of getting their files without having to + specify the module parameter. + + :param module: The module associated with the file to locate + :param file: The file to locate + :return: Path string, or NULL if not found. Use bfree to free string + +--------------------- + +.. function:: char *obs_module_get_config_path(obs_module_t *module, const char *file) + + Returns the path of a plugin module config file (whether it exists or not). + + Note: Modules should use obs_module_config_path function defined in + obs-module.h as a more elegant means of getting their files without + having to specify the module parameter. + + :param module: The module associated with the path + :param file: The file to get a path to + :return: Path string, or NULL if not found. Use bfree to free string diff --git a/docs/sphinx/reference-outputs.rst b/docs/sphinx/reference-outputs.rst new file mode 100644 index 000000000..404dd23ca --- /dev/null +++ b/docs/sphinx/reference-outputs.rst @@ -0,0 +1,785 @@ +Output API Reference (obs_output_t) +=================================== + +Outputs allow the ability to output the currently rendering audio/video. +Streaming and recording are two common examples of outputs, but not the +only types of outputs. Outputs can receive the raw data or receive +encoded data. The `libobs/obs-output.h`_ file is the dedicated header +for implementing outputs + +.. type:: obs_output_t + + A reference-counted output object. + +.. type:: obs_weak_output_t + + A weak reference to an output object. + +.. code:: cpp + + #include + + +Output Definition Structure (obs_output_info) +--------------------------------------------- + +.. type:: struct obs_output_info + + Output definition structure. + +.. member:: const char *obs_output_info.id + + Unique string identifier for the source (required). + +.. member:: uint32_t obs_output_info.flags + + Output capability flags (required). + + (Author's note: This should be renamed to "capability_flags") + + A bitwise OR combination of one or more of the following values: + + - **OBS_OUTPUT_VIDEO** - Can output video. + + - **OBS_OUTPUT_AUDIO** - Can output audio. + + - **OBS_OUTPUT_AV** - Combines OBS_OUTPUT_VIDEO and OBS_OUTPUT_AUDIO. + + - **OBS_OUTPUT_ENCODED** - Output is encoded. + + When this capability flag is used, the output must have encoders + assigned to it via the :c:func:`obs_output_set_video_encoder()` + and/or :c:func:`obs_output_set_audio_encoder()` functions in order + to be started. + + - **OBS_OUTPUT_SERVICE** - Output requires a service object. + + When this capability flag is used, the output must have a service + assigned to it via the :c:func:`obs_output_set_service()` function + in order to be started. + + This is usually used with live streaming outputs that stream to + specific services. + + - **OBS_OUTPUT_MULTI_TRACK** - Output supports multiple audio tracks. + + When this capability flag is used, specifies that this output + supports multiple encoded audio tracks simultaneously. + +.. member:: const char *(*obs_output_info.get_name)(void *type_data) + + Get the translated name of the output type. + + :param type_data: The type_data variable of this structure + :return: The translated name of the output type + +.. member:: void *(*obs_output_info.create)(obs_data_t *settings, obs_output_t *output) + + Creates the implementation data for the output. + + :param settings: Settings to initialize the output with + :param output: Output that this data is associated with + :return: The implementation data associated with this output + +.. member:: void (*obs_output_info.destroy)(void *data) + + Destroys the implementation data for the output. + +.. member:: bool (*obs_output_info.start)(void *data) + + Starts the output. If needed, this function can spawn a thread, + return *true* immediately, and then signal for failure later. + + :return: *true* if successful or deferring to a signal to indicate + failure, *false* on failure to start + +.. member:: void (*obs_output_info.stop)(void *data, uint64_t ts) + + Requests an output to stop at a specified time. The *ts* parameter + indicates when the stop should occur. Output will actually stop when + either the :c:func:`obs_output_end_data_capture()` or + :c:func:`obs_output_signal_stop()` functions are called. If *ts* is + 0, an immediate stop was requested. + + :param ts: The timestamp to stop. If 0, the output should attempt to + stop immediately rather than wait for any more data to + process + +.. member:: void (*obs_output_info.raw_video)(void *data, struct video_data *frame) + + This is called when the output receives raw video data. Only applies + to outputs that are not encoded. + + :param frame: The raw video frame + +.. member:: void (*obs_output_info.raw_audio)(void *data, struct audio_data *frames) + + This is called when the output recieves raw audio data. Only applies + to outputs that are not encoded. + + :param frames: The raw audio frames + +.. member:: void (*obs_output_info.encoded_packet)(void *data, struct encoder_packet *packet) + + This is called when the output receives encoded video/audio data. + Only applies to outputs that are encoded. Packets will always be + given in monotonic timestamp order. + + :param packet: The video or audio packet + +.. member:: void (*obs_output_info.update)(void *data, obs_data_t *settings) + + Updates the settings for this output. + + (Optional) + + :param settings: New settings for this output + +.. member:: void (*obs_output_info.get_defaults)(obs_data_t *settings) + void (*obs_output_info.get_defaults2)(void *type_data, obs_data_t *settings) + + Sets the default settings for this output. + + (Optional) + + :param settings: Default settings. Call obs_data_set_default* + functions on this object to set default setting + values + +.. member:: obs_properties_t *(*obs_output_info.get_properties)(void *data) + obs_properties_t *(*obs_output_info.get_properties2)(void *data, void *type_data) + + Gets the property information of this output. + + (Optional) + + :return: The properties of the output + +.. member:: void (*obs_output_info.pause)(void *data) + + Pauses the output (if the output supports pausing). + + (Author's note: This is currently unimplemented) + + (Optional) + +.. member:: uint64_t (*obs_output_info.get_total_bytes)(void *data) + + Returns the number of total bytes processed by this output. + + (Optional) + + :return: Total bytes processed by this output since it started + +.. member:: int (*obs_output_info.get_dropped_frames)(void *data) + + Returns the number of dropped frames. + + (Optional) + + :return: Number of dropped frames due to network congestion by this + output since it started + +.. member:: void *obs_output_info.type_data + void (*obs_output_info.free_type_data)(void *type_data) + + Private data associated with this entry. Note that this is not the + same as the implementation data; this is used to differentiate + between two different types if the same callbacks are used for more + than one different type. + + (Optional) + +.. member:: float (*obs_output_info.get_congestion)(void *data) + + This function is used to indicate how currently congested the output + is. Useful for visualizing how much data is backed up on streaming + outputs. + + (Optional) + + :return: Current congestion value (0.0f..1.0f) + +.. member:: int (*obs_output_info.get_connect_time_ms)(void *data) + + This function is used to determine how many milliseconds it took to + connect to its current server. + + (Optional) + + :return: Milliseconds it took to connect to its current server + +.. member:: const char *obs_output_info.encoded_video_codecs + const char *obs_output_info.encoded_audio_codecs + + This variable specifies which codecs are supported by an encoded + output, separated by semicolon. + + (Optional, though recommended) + +.. _output_signal_handler_reference: + +Output Signals +-------------- + +**start** (ptr output) + + Called when the output starts. + +**stop** (ptr output, int code) + + Called when the output stops. + + :Parameters: - **code** - Can be one of the following values: + + | OBS_OUTPUT_SUCCESS - Successfuly stopped + | OBS_OUTPUT_BAD_PATH - The specified path was invalid + | OBS_OUTPUT_CONNECT_FAILED - Failed to connect to a server + | OBS_OUTPUT_INVALID_STREAM - Invalid stream path + | OBS_OUTPUT_ERROR - Generic error + | OBS_OUTPUT_DISCONNECTED - Unexpectedly disconnected + | OBS_OUTPUT_UNSUPPORTED - The settings, video/audio format, or codecs are unsupported by this output + | OBS_OUTPUT_NO_SPACE - Ran out of disk space + +**starting** (ptr output) + + Called when the output is starting. + +**stopping** (ptr output) + + Called when the output is stopping. + +**activate** (ptr output) + + Called when the output activates (starts capturing data). + +**deactivate** (ptr output) + + Called when the output deactivates (stops capturing data). + +**reconnect** (ptr output) + + Called when the output is reconnecting. + +**reconnect_success** (ptr output) + + Called when the output has successfully reconnected. + +General Output Functions +------------------------ + +.. function:: void obs_register_output(struct obs_output_info *info) + + Registers an output type. Typically used in + :c:func:`obs_module_load()` or in the program's initialization phase. + +--------------------- + +.. function:: const char *obs_output_get_display_name(const char *id) + + Calls the :c:member:`obs_output_info.get_name` callback to get the + translated display name of an output type. + + :param id: The output type string identifier + :return: The translated display name of an output type + +--------------------- + +.. function:: obs_output_t *obs_output_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data) + + Creates an output with the specified settings. + + The "output" context is used for anything related to outputting the + final video/audio mix (E.g. streaming or recording). Use + obs_output_release to release it. + + :param id: The output type string identifier + :param name: The desired name of the output. If this is + not unique, it will be made to be unique + :param settings: The settings for the output, or *NULL* if + none + :param hotkey_data: Saved hotkey data for the output, or *NULL* + if none + :return: A reference to the newly created output, or + *NULL* if failed + +--------------------- + +.. function:: void obs_output_addref(obs_output_t *output) + void obs_output_release(obs_output_t *output) + + Adds/releases a reference to an output. When the last reference is + released, the output is destroyed. + +--------------------- + +.. function:: obs_weak_output_t *obs_output_get_weak_output(obs_output_t *output) + obs_output_t *obs_weak_output_get_output(obs_weak_output_t *weak) + + These functions are used to get a weak reference from a strong output + reference, or a strong output reference from a weak reference. If + the output is destroyed, *obs_weak_output_get_output* will return + *NULL*. + +--------------------- + +.. function:: void obs_weak_output_addref(obs_weak_output_t *weak) + void obs_weak_output_release(obs_weak_output_t *weak) + + Adds/releases a weak reference to an output. + +--------------------- + +.. function:: const char *obs_output_get_name(const obs_output_t *output) + + :return: The name of the output + +--------------------- + +.. function:: bool obs_output_start(obs_output_t *output) + + Starts the output. + + :return: *true* if output successfuly started, *false* otherwise. If + the output failed to start, + :c:func:`obs_output_get_last_error()` may contain a specific + error string related to the reason + +--------------------- + +.. function:: void obs_output_stop(obs_output_t *output) + + Requests the output to stop. The output will wait until all data is + sent up until the time the call was made, then when the output has + successfully stopped, it will send the "stop" signal. See + :ref:`output_signal_handler_reference` for more information on output + signals. + +--------------------- + +.. function:: void obs_output_set_delay(obs_output_t *output, uint32_t delay_sec, uint32_t flags) + + Sets the current output delay, in seconds (if the output supports delay) + + If delay is currently active, it will set the delay value, but will not + affect the current delay, it will only affect the next time the output is + activated. + + :param delay_sec: Amount to delay the output, in seconds + :param flags: | Can be 0 or a combination of one of the following values: + | OBS_OUTPUT_DELAY_PRESERVE - On reconnection, start where it left of on reconnection. Note however that this option will consume extra memory to continually increase delay while waiting to reconnect + +--------------------- + +.. function:: uint32_t obs_output_get_delay(const obs_output_t *output) + + Gets the currently set delay value, in seconds. + +--------------------- + +.. function:: uint32_t obs_output_get_active_delay(const obs_output_t *output) + + If delay is active, gets the currently active delay value, in + seconds. The active delay can increase if the + OBS_OUTPUT_DELAY_PRESERVE flag was set when setting a delay. + +--------------------- + +.. function:: void obs_output_force_stop(obs_output_t *output) + + Attempts to get the output to stop immediately without waiting for + data to send. + +--------------------- + +.. function:: bool obs_output_active(const obs_output_t *output) + + :return: *true* if the output is currently active, *false* otherwise + +--------------------- + +.. function:: obs_data_t *obs_output_defaults(const char *id) + + :return: An incremented reference to the output's default settings + +--------------------- + +.. function:: obs_properties_t *obs_output_properties(const obs_output_t *output) + obs_properties_t *obs_get_output_properties(const char *id) + + Use these functions to get the properties of an output or output + type. Properties are optionally used (if desired) to automatically + generate user interface widgets to allow users to update settings. + + :return: The properties list for a specific existing output. Free + with :c:func:`obs_properties_destroy()` + +--------------------- + +.. function:: void obs_output_update(obs_output_t *output, obs_data_t *settings) + + Updates the settings for this output context. + +--------------------- + +.. function:: bool obs_output_can_pause(const obs_output_t *output) + + :return: *true* if the output can be paused, *false* otherwise + +--------------------- + +.. function:: void obs_output_pause(obs_output_t *output) + + Pause an output (if supported by the output). + + (Author's Note: Not yet implemented) + +--------------------- + +.. function:: obs_data_t *obs_output_get_settings(const obs_output_t *output) + + :return: An incremented reference to the output's settings + +--------------------- + +.. function:: signal_handler_t *obs_output_get_signal_handler(const obs_output_t *output) + + :return: The signal handler of the output + +--------------------- + +.. function:: proc_handler_t *obs_output_get_proc_handler(const obs_output_t *output) + + :return: The procedure handler of the output + +--------------------- + +.. function:: void obs_output_set_media(obs_output_t *output, video_t *video, audio_t *audio) + + Sets the current video/audio handlers for the output (typically + :c:func:`obs_get_video()` and :c:func:`obs_get_audio()`). Only used + with raw outputs so they can catch the raw video/audio frames. + +--------------------- + +.. function:: video_t *obs_output_video(const obs_output_t *output) + audio_t *obs_output_audio(const obs_output_t *output) + + Gets the current video/audio handlers for the output. + +--------------------- + +.. function:: void obs_output_set_mixer(obs_output_t *output, size_t mixer_idx) + size_t obs_output_get_mixer(const obs_output_t *output) + + Sets/gets the current audio mixer for non-encoded outputs. + +--------------------- + +.. function:: void obs_output_set_video_encoder(obs_output_t *output, obs_encoder_t *encoder) + void obs_output_set_audio_encoder(obs_output_t *output, obs_encoder_t *encoder, size_t idx) + + Sets the video/audio encoders for an encoded output. + + :param encoder: The video/audio encoder + :param idx: The audio encoder index if the output supports + multiple audio streams at once + +--------------------- + +.. function:: obs_encoder_t *obs_output_get_video_encoder(const obs_output_t *output) + obs_encoder_t *obs_output_get_audio_encoder(const obs_output_t *output, size_t idx) + + Gets the video/audio encoders for an encoded output. + + :param idx: The audio encoder index if the output supports + multiple audio streams at once + :return: The video/audio encoder. The reference is not + incremented + +--------------------- + +.. function:: void obs_output_set_service(obs_output_t *output, obs_service_t *service) + obs_service_t *obs_output_get_service(const obs_output_t *output) + + Sets/gets the service for outputs that require services (such as RTMP + outputs). *obs_output_get_service* does not return an incremented + reference. + +--------------------- + +.. function:: void obs_output_set_reconnect_settings(obs_output_t *output, int retry_count, int retry_sec); + + Sets the auto-reconnect settings for outputs that support it. The + retry time will double on each retry to prevent overloading services. + + :param retry_count: Maximum retry count. Set to 0 to disable + reconnecting + :param retry_sec: Starting retry wait duration, in seconds + +--------------------- + +.. function:: uint64_t obs_output_get_total_bytes(const obs_output_t *output) + + :return: Total bytes sent/processed + +--------------------- + +.. function:: int obs_output_get_frames_dropped(const obs_output_t *output) + + :return: Number of frames that were dropped due to network congestion + +--------------------- + +.. function:: int obs_output_get_total_frames(const obs_output_t *output) + + :return: Total frames sent/processed + +--------------------- + +.. function:: void obs_output_set_preferred_size(obs_output_t *output, uint32_t width, uint32_t height) + + Sets the preferred scaled resolution for this output. Set width and height + to 0 to disable scaling. + + If this output uses an encoder, it will call obs_encoder_set_scaled_size on + the encoder before the stream is started. If the encoder is already active, + then this function will trigger a warning and do nothing. + +--------------------- + +.. function:: uint32_t obs_output_get_width(const obs_output_t *output) + uint32_t obs_output_get_height(const obs_output_t *output) + + :return: The width/height of the output + +--------------------- + +.. function:: float obs_output_get_congestion(obs_output_t *output) + + :return: The congestion value. This value is used to visualize the + current congestion of a network output. For example, if + there is no congestion, the value will be 0.0f, if it's + fully congested, the value will be 1.0f + +--------------------- + +.. function:: int obs_output_get_connect_time_ms(obs_output_t *output) + + :return: How long the output took to connect to a server, in + milliseconds + +--------------------- + +.. function:: bool obs_output_reconnecting(const obs_output_t *output) + + :return: *true* if the output is currently reconnecting to a server, + *false* otherwise + +--------------------- + +.. function:: const char *obs_output_get_supported_video_codecs(const obs_output_t *output) + const char *obs_output_get_supported_audio_codecs(const obs_output_t *output) + + :return: Supported video/audio codecs of an encoded output, separated + by semicolen + +--------------------- + +.. function:: uint32_t obs_output_get_flags(const obs_output_t *output) + uint32_t obs_get_output_flags(const char *id) + + :return: The output capability flags + +--------------------- + +Functions used by outputs +------------------------- + +.. function:: void obs_output_set_last_error(obs_output_t *output, const char *message) + const char *obs_output_get_last_error(obs_output_t *output) + + Sets/gets the translated error message that is presented to a user in + case of disconnection, inability to connect, etc. + +--------------------- + +.. function:: void obs_output_set_video_conversion(obs_output_t *output, const struct video_scale_info *conversion) + + Optionally sets the video conversion information. Only used by raw + outputs. + + Relevant data types used with this function: + +.. code:: cpp + + enum video_format { + VIDEO_FORMAT_NONE, + + /* planar 420 format */ + VIDEO_FORMAT_I420, /* three-plane */ + VIDEO_FORMAT_NV12, /* two-plane, luma and packed chroma */ + + /* packed 422 formats */ + VIDEO_FORMAT_YVYU, + VIDEO_FORMAT_YUY2, /* YUYV */ + VIDEO_FORMAT_UYVY, + + /* packed uncompressed formats */ + VIDEO_FORMAT_RGBA, + VIDEO_FORMAT_BGRA, + VIDEO_FORMAT_BGRX, + VIDEO_FORMAT_Y800, /* grayscale */ + + /* planar 4:4:4 */ + VIDEO_FORMAT_I444, + }; + + enum video_colorspace { + VIDEO_CS_DEFAULT, + VIDEO_CS_601, + VIDEO_CS_709, + }; + + enum video_range_type { + VIDEO_RANGE_DEFAULT, + VIDEO_RANGE_PARTIAL, + VIDEO_RANGE_FULL + }; + + struct video_scale_info { + enum video_format format; + uint32_t width; + uint32_t height; + enum video_range_type range; + enum video_colorspace colorspace; + }; + +--------------------- + +.. function:: void obs_output_set_audio_conversion(obs_output_t *output, const struct audio_convert_info *conversion) + + Optionally sets the audio conversion information. Only used by raw + outputs. + + Relevant data types used with this function: + +.. code:: cpp + + enum audio_format { + AUDIO_FORMAT_UNKNOWN, + + AUDIO_FORMAT_U8BIT, + AUDIO_FORMAT_16BIT, + AUDIO_FORMAT_32BIT, + AUDIO_FORMAT_FLOAT, + + AUDIO_FORMAT_U8BIT_PLANAR, + AUDIO_FORMAT_16BIT_PLANAR, + AUDIO_FORMAT_32BIT_PLANAR, + AUDIO_FORMAT_FLOAT_PLANAR, + }; + + enum speaker_layout { + SPEAKERS_UNKNOWN, + SPEAKERS_MONO, + SPEAKERS_STEREO, + SPEAKERS_2POINT1, + SPEAKERS_QUAD, + SPEAKERS_4POINT1, + SPEAKERS_5POINT1, + SPEAKERS_5POINT1_SURROUND, + SPEAKERS_7POINT1, + SPEAKERS_7POINT1_SURROUND, + SPEAKERS_SURROUND, + }; + + struct audio_convert_info { + uint32_t samples_per_sec; + enum audio_format format; + enum speaker_layout speakers; + }; + +--------------------- + +.. function:: bool obs_output_can_begin_data_capture(const obs_output_t *output, uint32_t flags) + + Determines whether video/audio capture (encoded or raw) is able to + start. Call this before initializing any output data to ensure that + the output can start. + + :param flags: Set to 0 to initialize both audio/video, otherwise a + bitwise OR combination of OBS_OUTPUT_VIDEO and/or + OBS_OUTPUT_AUDIO + :return: *true* if data capture can begin + +--------------------- + +.. function:: bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags) + + Initializes any encoders/services associated with the output. This + must be called for encoded outputs before calling + :c:func:`obs_output_begin_data_capture()`. + + :param flags: Set to 0 to initialize both audio/video, otherwise a + bitwise OR combination of OBS_OUTPUT_VIDEO and/or + OBS_OUTPUT_AUDIO + :return: *true* if successful, *false* otherwise + +--------------------- + +.. function:: bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags) + + Begins data capture from raw media or encoders. This is typically + when the output actually activates (starts) internally. Video/audio + data will start being sent to the callbacks of the output. + + :param flags: Set to 0 to initialize both audio/video, otherwise a + bitwise OR combination of OBS_OUTPUT_VIDEO and/or + OBS_OUTPUT_AUDIO + :return: *true* if successful, *false* otherwise. Typically the + return value does not need to be checked if + :c:func:`obs_output_can_begin_data_capture()` was + called + +--------------------- + +.. function:: void obs_output_end_data_capture(obs_output_t *output) + + Ends data capture of an output. This is typically when the output + actually intentionally deactivates (stops). Video/audio data will + stop being sent to the callbacks of the output. The output will + trigger the "stop" signal with the OBS_OUTPUT_SUCCESS code to + indicate that the output has stopped successfully. See + :ref:`output_signal_handler_reference` for more information on output + signals. + +--------------------- + +.. function:: void obs_output_signal_stop(obs_output_t *output, int code) + + Ends data capture of an output with an output code, indicating that + the output stopped unexpectedly. This is typically used if for + example the server was disconnected for some reason, or if there was + an error saving to file. The output will trigger the "stop" signal + with the the desired code to indicate that the output has stopped + successfully. See :ref:`output_signal_handler_reference` for more + information on output signals. + + :c:func:`obs_output_set_last_error()` may be used in conjunction with + these error codes to optionally relay more detailed error information + to the user + + :param code: | Can be one of the following values: + | OBS_OUTPUT_SUCCESS - Successfuly stopped + | OBS_OUTPUT_BAD_PATH - The specified path was invalid + | OBS_OUTPUT_CONNECT_FAILED - Failed to connect to a server + | OBS_OUTPUT_INVALID_STREAM - Invalid stream path + | OBS_OUTPUT_ERROR - Generic error + | OBS_OUTPUT_DISCONNECTED - Unexpectedly disconnected + | OBS_OUTPUT_UNSUPPORTED - The settings, video/audio format, or codecs are unsupported by this output + | OBS_OUTPUT_NO_SPACE - Ran out of disk space + +.. --------------------------------------------------------------------------- + +.. _libobs/obs-output.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-output.h diff --git a/docs/sphinx/reference-properties.rst b/docs/sphinx/reference-properties.rst new file mode 100644 index 000000000..e851ded35 --- /dev/null +++ b/docs/sphinx/reference-properties.rst @@ -0,0 +1,597 @@ +Properties API Reference (obs_properties_t) +=========================================== + +Properties are used to enumerate available settings for a libobs object. +Typically this is used to automatically generate user interface widgets, +though can be used to enumerate available and/or valid values for +specific settings as well. + +.. type:: obs_properties_t + + Properties object (not reference counted). + +.. type:: obs_property_t + + A property object (not reference counted). + +.. code:: cpp + + #include + + +General Functions +----------------- + +.. function:: obs_properties_t *obs_properties_create(void) + + :return: A new properties object. + +--------------------- + +.. function:: obs_properties_t *obs_properties_create_param(void *param, void (*destroy)(void *param)) + + Creates a new properties object with specific private data *param* + associated with the object, and is automatically freed with the + object when the properties are destroyed via the *destroy* function. + + :return: A new properties object. + +--------------------- + +.. function:: void obs_properties_destroy(obs_properties_t *props) + +--------------------- + +.. function:: void obs_properties_set_flags(obs_properties_t *props, uint32_t flags) + uint32_t obs_properties_get_flags(obs_properties_t *props) + + :param flags: 0 or a bitwise OR combination of one of the following + values: + + - OBS_PROPERTIES_DEFER_UPDATE - A hint that tells the + front-end to defers updating the settings until the + user has finished editing all properties rather than + immediately updating any settings + +--------------------- + +.. function:: void obs_properties_set_param(obs_properties_t *props, void *param, void (*destroy)(void *param)) + void *obs_properties_get_param(obs_properties_t *props) + + Sets custom data associated with this properties object. If private + data is already associated with the object, that private data will be + destroyed before assigning new private data to it. + +--------------------- + +.. function:: void obs_properties_apply_settings(obs_properties_t *props, obs_data_t *settings) + + Applies settings to the properties by calling all the necessary + modification callbacks + +--------------------- + + +Property Object Functions +------------------------- + +.. function:: obs_property_t *obs_properties_add_bool(obs_properties_t *props, const char *name, const char *description) + + Adds a boolean property. + + :param name: Setting identifier string + :param description: Localized name shown to user + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_int(obs_properties_t *props, const char *name, const char *description, int min, int max, int step) + + Adds an integer property. + + :param name: Setting identifier string + :param description: Localized name shown to user + :param min: Minimum value + :param max: Maximum value + :param step: Step value + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_float(obs_properties_t *props, const char *name, const char *description, double min, double max, double step) + + :param name: Setting identifier string + :param description: Localized name shown to user + :param min: Minimum value + :param max: Maximum value + :param step: Step value + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_int_slider(obs_properties_t *props, const char *name, const char *description, int min, int max, int step) + + :param name: Setting identifier string + :param description: Localized name shown to user + :param min: Minimum value + :param max: Maximum value + :param step: Step value + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_float_slider(obs_properties_t *props, const char *name, const char *description, double min, double max, double step) + + :param name: Setting identifier string + :param description: Localized name shown to user + :param min: Minimum value + :param max: Maximum value + :param step: Step value + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_text(obs_properties_t *props, const char *name, const char *description, enum obs_text_type type) + + :param name: Setting identifier string + :param description: Localized name shown to user + :param type: Can be one of the following values: + + - **OBS_TEXT_DEFAULT** - Single line of text + - **OBS_TEXT_PASSWORD** - Single line of text (passworded) + - **OBS_TEXT_MULTILINE** - Multi-line text + + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_path(obs_properties_t *props, const char *name, const char *description, enum obs_path_type type, const char *filter, const char *default_path) + + Adds a 'path' property. Can be a directory or a file. + + If target is a file path, the filters should be this format, separated by + double semi-colens, and extensions separated by space:: + + "Example types 1 and 2 (*.ex1 *.ex2);;Example type 3 (*.ex3)" + + :param name: Setting identifier string + :param description: Localized name shown to user + :param type: Can be one of the following values: + + - **OBS_PATH_FILE** - File (for reading) + - **OBS_PATH_FILE_SAVE** - File (for writing) + - **OBS_PATH_DIRECTORY** - Directory + + :param filter: If type is a file path, then describes the file filter + that the user can browse. Items are separated via + double semi-colens. If multiple file types in a + filter, separate with space. + :param default_path: The default path to start in, or *NULL* + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_list(obs_properties_t *props, const char *name, const char *description, enum obs_combo_type type, enum obs_combo_format format) + + Adds an integer/string/floating point item list. This would be + implemented as a combo box in user interface. + + :param name: Setting identifier string + :param description: Localized name shown to user + :param type: Can be one of the following values: + + - **OBS_COMBO_TYPE_EDITABLE** - Can be edited. + Only used with string lists. + - **OBS_COMBO_TYPE_LIST** - Not ediable. + + :param format: Can be one of the following values: + + - **OBS_COMBO_FORMAT_INT** - Integer list + - **OBS_COMBO_FORMAT_FLOAT** - Floating point + list + - **OBS_COMBO_FORMAT_STRING** - String list + + :return: The property + + Important Related Functions: + + - :c:func:`obs_property_list_add_string` + - :c:func:`obs_property_list_add_int` + - :c:func:`obs_property_list_add_float` + - :c:func:`obs_property_list_insert_string` + - :c:func:`obs_property_list_insert_int` + - :c:func:`obs_property_list_insert_float` + - :c:func:`obs_property_list_item_remove` + - :c:func:`obs_property_list_clear` + +--------------------- + +.. function:: obs_property_t *obs_properties_add_color(obs_properties_t *props, const char *name, const char *description) + + Adds a color property. + + :param name: Setting identifier string + :param description: Localized name shown to user + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_button(obs_properties_t *props, const char *name, const char *text, obs_property_clicked_t callback) + + Adds a button property. This property does not actually store any + settings; it's used to implement a button in user interface if the + properties are used to generate user interface. + + :param name: Setting identifier string + :param description: Localized name shown to user + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_font(obs_properties_t *props, const char *name, const char *description) + + Adds a font property. + + :param name: Setting identifier string + :param description: Localized name shown to user + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_editable_list(obs_properties_t *props, const char *name, const char *description, enum obs_editable_list_type type, const char *filter, const char *default_path) + + Adds a list in which the user can add/insert/remove items. + + :param name: Setting identifier string + :param description: Localized name shown to user + :param type: Can be one of the following values: + + - **OBS_EDITABLE_LIST_TYPE_STRINGS** - An + editable list of strings. + - **OBS_EDITABLE_LIST_TYPE_FILES** - An + editable list of files. + - **OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS** - + An editable list of files and URLs. + + :param filter: File filter to use if a file list + :param default_path: Default path if a file list + :return: The property + +--------------------- + +.. function:: obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props, const char *name, const char *description) + + Adds a frame rate property. + + :param name: Setting identifier string + :param description: Localized name shown to user + :return: The property + + Important Related Functions: + + - :c:func:`obs_property_frame_rate_option_add` + - :c:func:`obs_property_frame_rate_fps_range_add` + - :c:func:`obs_property_frame_rate_option_insert` + - :c:func:`obs_property_frame_rate_fps_range_insert` + +--------------------- + + +Property Enumeration Functions +------------------------------ + +.. function:: obs_property_t *obs_properties_first(obs_properties_t *props) + + :return: The first property in the properties object. + +--------------------- + +.. function:: obs_property_t *obs_properties_get(obs_properties_t *props, const char *property) + + :param property: The name of the property to get + :return: A specific property or *NULL* if not found + +--------------------- + +.. function:: bool obs_property_next(obs_property_t **p) + + :param p: Pointer to the pointer of the next property + :return: *true* if successful, *false* if no more properties + +--------------------- + +.. function:: const char * obs_property_name(obs_property_t *p) + + :return: The setting identifier string of the property + + *(Author's Note: "name" was a bad name to use here. Should have been + "setting")* + +--------------------- + +.. function:: const char * obs_property_description(obs_property_t *p) + + :return: The actual localized display name of the property + + *(Author's note: This one should have been the "name")* + +--------------------- + +.. function:: const char * obs_property_long_description(obs_property_t *p) + + :return: A detailed description of what the setting is used for. + Usually used with things like tooltips. + +--------------------- + +.. function:: enum obs_property_type obs_property_get_type(obs_property_t *p) + + :return: One of the following values: + + - OBS_PROPERTY_INVALID + - OBS_PROPERTY_BOOL + - OBS_PROPERTY_INT + - OBS_PROPERTY_FLOAT + - OBS_PROPERTY_TEXT + - OBS_PROPERTY_PATH + - OBS_PROPERTY_LIST + - OBS_PROPERTY_COLOR + - OBS_PROPERTY_BUTTON + - OBS_PROPERTY_FONT + - OBS_PROPERTY_EDITABLE_LIST + - OBS_PROPERTY_FRAME_RATE + +--------------------- + +.. function:: bool obs_property_enabled(obs_property_t *p) + +--------------------- + +.. function:: bool obs_property_visible(obs_property_t *p) + +--------------------- + +.. function:: int obs_property_int_min(obs_property_t *p) + +--------------------- + +.. function:: int obs_property_int_max(obs_property_t *p) + +--------------------- + +.. function:: int obs_property_int_step(obs_property_t *p) + +--------------------- + +.. function:: enum obs_number_type obs_property_int_type(obs_property_t *p) + +--------------------- + +.. function:: double obs_property_float_min(obs_property_t *p) + +--------------------- + +.. function:: double obs_property_float_max(obs_property_t *p) + +--------------------- + +.. function:: double obs_property_float_step(obs_property_t *p) + +--------------------- + +.. function:: enum obs_number_type obs_property_float_type(obs_property_t *p) + +--------------------- + +.. function:: enum obs_text_type obs_property_text_type(obs_property_t *p) + +--------------------- + +.. function:: enum obs_path_type obs_property_path_type(obs_property_t *p) + +--------------------- + +.. function:: const char * obs_property_path_filter(obs_property_t *p) + +--------------------- + +.. function:: const char * obs_property_path_default_path(obs_property_t *p) + +--------------------- + +.. function:: enum obs_combo_type obs_property_list_type(obs_property_t *p) + +--------------------- + +.. function:: enum obs_combo_format obs_property_list_format(obs_property_t *p) + +--------------------- + +.. function:: bool obs_property_list_item_disabled(obs_property_t *p, size_t idx) + +--------------------- + +.. function:: size_t obs_property_list_item_count(obs_property_t *p) + +--------------------- + +.. function:: const char *obs_property_list_item_name(obs_property_t *p, size_t idx) + +--------------------- + +.. function:: const char *obs_property_list_item_string(obs_property_t *p, size_t idx) + +--------------------- + +.. function:: long long obs_property_list_item_int(obs_property_t *p, size_t idx) + +--------------------- + +.. function:: double obs_property_list_item_float(obs_property_t *p, size_t idx) + +--------------------- + +.. function:: enum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p) + +--------------------- + +.. function:: const char *obs_property_editable_list_filter(obs_property_t *p) + +--------------------- + +.. function:: const char *obs_property_editable_list_default_path(obs_property_t *p) + +--------------------- + +.. function:: size_t obs_property_frame_rate_options_count(obs_property_t *p) + +--------------------- + +.. function:: const char *obs_property_frame_rate_option_name(obs_property_t *p, size_t idx) + +--------------------- + +.. function:: const char *obs_property_frame_rate_option_description( obs_property_t *p, size_t idx) + +--------------------- + +.. function:: size_t obs_property_frame_rate_fps_ranges_count(obs_property_t *p) + +--------------------- + +.. function:: struct media_frames_per_second obs_property_frame_rate_fps_range_min( obs_property_t *p, size_t idx) + +--------------------- + +.. function:: struct media_frames_per_second obs_property_frame_rate_fps_range_max( obs_property_t *p, size_t idx) + +--------------------- + + +Property Modification Functions +------------------------------- + +.. function:: void obs_property_set_modified_callback(obs_property_t *p, obs_property_modified_t modified) + + Allows the ability to change the properties depending on what + settings are used by the user. + + Relevant data types used with this function: + +.. code:: cpp + + typedef bool (*obs_property_clicked_t)(obs_properties_t *props, + obs_property_t *property, void *data); + +--------------------- + +.. function:: bool obs_property_modified(obs_property_t *p, obs_data_t *settings) + +--------------------- + +.. function:: bool obs_property_button_clicked(obs_property_t *p, void *obj) + +--------------------- + +.. function:: void obs_property_set_visible(obs_property_t *p, bool visible) + +--------------------- + +.. function:: void obs_property_set_enabled(obs_property_t *p, bool enabled) + +--------------------- + +.. function:: void obs_property_set_description(obs_property_t *p, const char *description) + + Sets the displayed localized name of the property, shown to the user. + +--------------------- + +.. function:: void obs_property_set_long_description(obs_property_t *p, const char *long_description) + + Sets the localized long description of the property, usually shown to + a user via tooltip. + +--------------------- + +.. function:: void obs_property_int_set_limits(obs_property_t *p, int min, int max, int step) + +--------------------- + +.. function:: void obs_property_float_set_limits(obs_property_t *p, double min, double max, double step) + +--------------------- + +.. function:: void obs_property_list_clear(obs_property_t *p) + +--------------------- + +.. function:: size_t obs_property_list_add_string(obs_property_t *p, const char *name, const char *val) + + Adds a string to a string list. + +--------------------- + +.. function:: size_t obs_property_list_add_int(obs_property_t *p, const char *name, long long val) + + Adds an integer to a integer list. + +--------------------- + +.. function:: size_t obs_property_list_add_float(obs_property_t *p, const char *name, double val) + + Adds a floating point to a floating point list. + +--------------------- + +.. function:: void obs_property_list_insert_string(obs_property_t *p, size_t idx, const char *name, const char *val) + + Inserts a string in to a string list. + +--------------------- + +.. function:: void obs_property_list_insert_int(obs_property_t *p, size_t idx, const char *name, long long val) + + Inserts an integer in to an integer list. + +--------------------- + +.. function:: void obs_property_list_insert_float(obs_property_t *p, size_t idx, const char *name, double val) + + Inserts a floating point in to a floating point list. + +--------------------- + +.. function:: void obs_property_list_item_disable(obs_property_t *p, size_t idx, + +--------------------- + +.. function:: void obs_property_list_item_remove(obs_property_t *p, size_t idx) + +--------------------- + +.. function:: size_t obs_property_frame_rate_option_add(obs_property_t *p, const char *name, const char *description) + +--------------------- + +.. function:: size_t obs_property_frame_rate_fps_range_add(obs_property_t *p, struct media_frames_per_second min, struct media_frames_per_second max) + +--------------------- + +.. function:: void obs_property_frame_rate_clear(obs_property_t *p) + +--------------------- + +.. function:: void obs_property_frame_rate_options_clear(obs_property_t *p) + +--------------------- + +.. function:: void obs_property_frame_rate_fps_ranges_clear(obs_property_t *p) + +--------------------- + +.. function:: void obs_property_frame_rate_option_insert(obs_property_t *p, size_t idx, const char *name, const char *description) + +--------------------- + +.. function:: void obs_property_frame_rate_fps_range_insert(obs_property_t *p, size_t idx, struct media_frames_per_second min, struct media_frames_per_second max) diff --git a/docs/sphinx/reference-scenes.rst b/docs/sphinx/reference-scenes.rst new file mode 100644 index 000000000..883b67ef6 --- /dev/null +++ b/docs/sphinx/reference-scenes.rst @@ -0,0 +1,559 @@ +Scene API Reference (obs_scene_t) +================================= + +A scene is a source which contains and renders other sources using +specific transforms and/or filtering + +.. type:: obs_scene_t + + A reference-counted scene object. + +.. type:: obs_sceneitem_t + + A reference-counted scene item object. + +.. code:: cpp + + #include + + +Scene Item Transform Structure (obs_transform_info) +--------------------------------------------------- + +.. type:: struct obs_transform_info + + Scene item transform structure. + +.. member:: struct vec2 obs_transform_info.pos + + Position. + +.. member:: float obs_transform_info.rot + + Rotation (degrees). + +.. member:: struct vec2 obs_transform_info.scale + + Scale. + +.. member:: uint32_t obs_transform_info.alignment + + The alignment of the scene item relative to its position. + + Can be 0 or a bitwise OR combination of one of the following values: + + - **OBS_ALIGN_CENTER** + - **OBS_ALIGN_LEFT** + - **OBS_ALIGN_RIGHT** + - **OBS_ALIGN_TOP** + - **OBS_ALIGN_BOTTOM** + +.. member:: enum obs_bounds_type obs_transform_info.bounds_type + + Can be one of the following values: + + - **OBS_BOUNDS_NONE** - No bounding box + - **OBS_BOUNDS_STRETCH** - Stretch to the bounding box without preserving aspect ratio + - **OBS_BOUNDS_SCALE_INNER** - Scales with aspect ratio to inner bounding box rectangle + - **OBS_BOUNDS_SCALE_OUTER** - Scales with aspect ratio to outer bounding box rectangle + - **OBS_BOUNDS_SCALE_TO_WIDTH** - Scales with aspect ratio to the bounding box width + - **OBS_BOUNDS_SCALE_TO_HEIGHT** - Scales with aspect ratio to the bounding box height + - **OBS_BOUNDS_MAX_ONLY** - Scales with aspect ratio, but only to the size of the source maximum + +.. member:: uint32_t obs_transform_info.bounds_alignment + + The alignment of the source within the bounding box. + + Can be 0 or a bitwise OR combination of one of the following values: + + - **OBS_ALIGN_CENTER** + - **OBS_ALIGN_LEFT** + - **OBS_ALIGN_RIGHT** + - **OBS_ALIGN_TOP** + - **OBS_ALIGN_BOTTOM** + +.. member:: struct vec2 obs_transform_info.bounds + + The bounding box (if a bounding box is enabled). + + +Scene Item Crop Structure (obs_sceneitem_crop) +---------------------------------------------- + +.. type:: struct obs_sceneitem_crop + + Scene item crop structure. + +.. member:: int obs_sceneitem_crop.left + + Left crop value. + +.. member:: int obs_sceneitem_crop.top + + Top crop value. + +.. member:: int obs_sceneitem_crop.right + + Right crop value. + +.. member:: int obs_sceneitem_crop.bottom + + Bottom crop value. + + +Scene Item Order Info Structure (*obs_sceneitem_order_info) +---------------------------------------------- + +.. type:: struct obs_sceneitem_order_info + + Scene item order info structure. + +.. member:: obs_sceneitem_t *obs_sceneitem_order_info.group + + Specifies the group this scene item belongs to, or *NULL* if none. + +.. member:: obs_sceneitem_t *obs_sceneitem_order_info.item + + Specifies the scene item. + + + +.. _scene_signal_reference: + +Scene Signals +------------- + +**item_add** (ptr scene, ptr item) + + Called when a scene item has been added to the scene. + +**item_remove** (ptr scene, ptr item) + + Called when a scene item has been removed from the scen. + +**reorder** (ptr scene) + + Called when scene items have been reoredered in the scene. + +**item_visible** (ptr scene, ptr item, bool visible) + + Called when a scene item's visibility state changes. + +**item_select** (ptr scene, ptr item) +**item_deselect** (ptr scene, ptr item) + + Called when a scene item has been selected/deselected. + + (Author's note: These should be replaced) + +**item_transform** (ptr scene, ptr item) + + Called when a scene item's transform has changed. + + +General Scene Functions +----------------------- + +.. function:: obs_scene_t *obs_scene_create(const char *name) + + :param name: Name of the scene source. If it's not unique, it will + be made unique + :return: A reference to a scene + +--------------------- + +.. function:: obs_scene_t *obs_scene_create_private(const char *name) + + :param name: Name of the scene source. Does not have to be unique, + or can be *NULL* + :return: A reference to a private scene + +--------------------- + +.. function:: obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, enum obs_scene_duplicate_type type) + + Duplicates a scene. When a scene is duplicated, its sources can be + just referenced, or fully duplicated. + + :param name: Name of the new scene source + + :param type: | Type of duplication: + | OBS_SCENE_DUP_REFS - Duplicates the scene, but scene items are only duplicated with references + | OBS_SCENE_DUP_COPY - Duplicates the scene, and scene items are also fully duplicated when possible + | OBS_SCENE_DUP_PRIVATE_REFS - Duplicates with references, but the scene is a private source + | OBS_SCENE_DUP_PRIVATE_COPY - Fully duplicates scene items when possible, but the scene and duplicates sources are private sources + + :return: A reference to a new scene + +--------------------- + +.. function:: void obs_scene_addref(obs_scene_t *scene) + void obs_scene_release(obs_scene_t *scene) + + Adds/releases a reference to a scene. + +--------------------- + +.. function:: obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) + + :return: A new scene item for a source within a scene. Does not + increment the reference + +--------------------- + +.. function:: obs_source_t *obs_scene_get_source(const obs_scene_t *scene) + + :return: The scene's source. Does not increment the reference + +--------------------- + +.. function:: obs_scene_t *obs_scene_from_source(const obs_source_t *source) + + :return: The scene context, or *NULL* if not a scene. Does not + increase the reference + +--------------------- + +.. function:: obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name) + + :param name: The name of the source to find + :return: The scene item if found, otherwise *NULL* if not found + +--------------------- + +.. function:: obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id) + + :param id: The unique numeric identifier of the scene item + :return: The scene item if found, otherwise *NULL* if not found + +--------------------- + +.. function:: void obs_scene_enum_items(obs_scene_t *scene, bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), void *param) + + Enumerates scene items within a scene. + +--------------------- + +.. function:: bool obs_scene_reorder_items(obs_scene_t *scene, obs_sceneitem_t * const *item_order, size_t item_order_size) + + Reorders items within a scene. + +--------------------- + +.. function:: bool obs_scene_reorder_items2(obs_scene_t *scene, struct obs_sceneitem_order_info *item_order, size_t item_order_size) + + Reorders items within a scene with groups and group sub-items. + +--------------------- + + +.. _scene_item_reference: + +Scene Item Functions +-------------------- + +.. function:: void obs_sceneitem_addref(obs_sceneitem_t *item) + void obs_sceneitem_release(obs_sceneitem_t *item) + + Adds/releases a reference to a scene item. + +--------------------- + +.. function:: void obs_sceneitem_remove(obs_sceneitem_t *item) + + Removes the scene item from the scene. + +--------------------- + +.. function:: obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item) + + :return: The scene associated with the scene item. Does not + increment the reference + +--------------------- + +.. function:: obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item) + + :return: The source associated with the scene item. Does not + increment the reference + +--------------------- + +.. function:: int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item) + + :return: The unique numeric identifier of the scene item. + +--------------------- + +.. function:: void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos) + void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos) + + Sets/gets the position of a scene item. + +--------------------- + +.. function:: void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg) + float obs_sceneitem_get_rot(const obs_sceneitem_t *item) + + Sets/gets the rotation of a scene item. + +--------------------- + +.. function:: void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale) + void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale) + + Sets/gets the scaling of the scene item. + +--------------------- + +.. function:: void obs_sceneitem_set_alignment(obs_sceneitem_t *item, uint32_t alignment) + uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item) + + Sets/gets the alignment of the scene item relative to its position. + + :param alignment: | Can be any bitwise OR combination of: + | OBS_ALIGN_CENTER + | OBS_ALIGN_LEFT + | OBS_ALIGN_RIGHT + | OBS_ALIGN_TOP + | OBS_ALIGN_BOTTOM + +--------------------- + +.. function:: void obs_sceneitem_set_order(obs_sceneitem_t *item, enum obs_order_movement movement) + + Changes the scene item's order relative to the other scene items + within the scene. + + :param movement: | Can be one of the following: + | OBS_ORDER_MOVE_UP + | OBS_ORDER_MOVE_DOWN + | OBS_ORDER_MOVE_TOP + | OBS_ORDER_MOVE_BOTTOM + +--------------------- + +.. function:: void obs_sceneitem_set_order_position(obs_sceneitem_t *item, int position) + + Changes the scene item's order index. + +--------------------- + +.. function:: void obs_sceneitem_set_bounds_type(obs_sceneitem_t *item, enum obs_bounds_type type) + enum obs_bounds_type obs_sceneitem_get_bounds_type(const obs_sceneitem_t *item) + + Sets/gets the bounding box type of a scene item. Bounding boxes are + used to stretch/position the source relative to a specific bounding + box of a specific size. + + :param type: | Can be one of the following values: + | OBS_BOUNDS_NONE - No bounding box + | OBS_BOUNDS_STRETCH - Stretch to the bounding box without preserving aspect ratio + | OBS_BOUNDS_SCALE_INNER - Scales with aspect ratio to inner bounding box rectangle + | OBS_BOUNDS_SCALE_OUTER - Scales with aspect ratio to outer bounding box rectangle + | OBS_BOUNDS_SCALE_TO_WIDTH - Scales with aspect ratio to the bounding box width + | OBS_BOUNDS_SCALE_TO_HEIGHT - Scales with aspect ratio to the bounding box height + | OBS_BOUNDS_MAX_ONLY - Scales with aspect ratio, but only to the size of the source maximum + +--------------------- + +.. function:: void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item, uint32_t alignment) + uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item) + + Sets/gets the alignment of the source within the bounding box. + + :param alignment: | Can be any bitwise OR combination of: + | OBS_ALIGN_CENTER + | OBS_ALIGN_LEFT + | OBS_ALIGN_RIGHT + | OBS_ALIGN_TOP + | OBS_ALIGN_BOTTOM + +--------------------- + +.. function:: void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds) + void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds) + + Sets/gets the bounding box width/height of the scene item. + +--------------------- + +.. function:: void obs_sceneitem_set_info(obs_sceneitem_t *item, const struct obs_transform_info *info) + void obs_sceneitem_get_info(const obs_sceneitem_t *item, struct obs_transform_info *info) + + Sets/gets the transform information of the scene item. + +--------------------- + +.. function:: void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform) + + Gets the transform matrix of the scene item used for drawing the + source. + +--------------------- + +.. function:: void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item, struct matrix4 *transform) + + Gets the transform matrix of the scene item used for the bouding box + or edges of the scene item. + +--------------------- + +.. function:: bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible) + bool obs_sceneitem_visible(const obs_sceneitem_t *item) + + Sets/gets the visibility state of the scene item. + +--------------------- + +.. function:: void obs_sceneitem_set_crop(obs_sceneitem_t *item, const struct obs_sceneitem_crop *crop) + void obs_sceneitem_get_crop(const obs_sceneitem_t *item, struct obs_sceneitem_crop *crop) + + Sets/gets the cropping of the scene item. + +--------------------- + +.. function:: void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item, enum obs_scale_type filter) + enum obs_scale_type obs_sceneitem_get_scale_filter( obs_sceneitem_t *item) + + Sets/gets the scale filter used for the scene item. + + :param filter: | Can be one of the following values: + | OBS_SCALE_DISABLE + | OBS_SCALE_POINT + | OBS_SCALE_BICUBIC + | OBS_SCALE_BILINEAR + | OBS_SCALE_LANCZOS + +--------------------- + +.. function:: void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item) + void obs_sceneitem_defer_update_end(obs_sceneitem_t *item) + + Allows the ability to call any one of the transform functions without + updating the internal matrices until obs_sceneitem_defer_update_end + has been called. + +--------------------- + +.. function:: obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item) + + :return: An incremented reference to the private settings of the + scene item. Allows the front-end to set custom information + which is saved with the scene item + +--------------------- + + +.. _scene_item_group_reference: + +Scene Item Group Functions +-------------------------- + +.. function:: obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name) + + Adds a group with the specified name. + + :param scene: Scene to add the group to + :param name: Name of the group + :return: The new group's scene item + +--------------------- + +.. function:: obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name, obs_sceneitem_t **items, size_t count) + + Creates a group out of the specified scene items. The group will be + inserted at the top scene item. + + :param scene: Scene to add the group to + :param name: Name of the group + :param items: Array of scene items to put in a group + :param count: Number of scene items in the array + :return: The new group's scene item + +--------------------- + +.. function:: obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, const char *name) + + Finds a group within a scene by its name. + + :param scene: Scene to find the group within + :param name: The name of the group to find + :return: The group scene item, or *NULL* if not found + +--------------------- + +.. function:: bool obs_sceneitem_is_group(obs_sceneitem_t *item) + + :param item: Scene item + :return: *true* if scene item is a group, *false* otherwise + +--------------------- + +.. function:: obs_scene_t *obs_sceneitem_group_get_scene(const obs_sceneitem_t *group) + + :param group: Group scene item + :return: Scene of the group, or *NULL* if not a group + +--------------------- + +.. function:: void obs_sceneitem_group_ungroup(obs_sceneitem_t *group) + + Ungroups the specified group. Scene items within the group will be + placed where the group was. + +--------------------- + +.. function:: void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item) + + Adds a scene item to a group. + +--------------------- + +.. function:: void obs_sceneitem_group_remove_item(obs_sceneitem_t *item) + + Rmoves a scene item from a group. The item will be placed before the + group in the main scene. + +--------------------- + +.. function:: obs_sceneitem_t *obs_sceneitem_get_group(obs_sceneitem_t *item) + + Returns the parent group of a scene item. + + :param item: Scene item to get the group of + :return: The parent group of the scene item, or *NULL* if not in + a group + +--------------------- + +.. function:: obs_sceneitem_t *obs_sceneitem_group_from_scene(obs_scene_t *scene) + + :return: The group associated with the scene, or *NULL* if the + specified scene is not a group. + +--------------------- + +.. function:: obs_sceneitem_t *obs_sceneitem_group_from_source(obs_source_t *source) + + :return: The group associated with the scene's source, or *NULL* if + the specified source is not a group. + +--------------------- + +.. function:: void obs_sceneitem_group_enum_items(obs_sceneitem_t *group, bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), void *param) + + Enumerates scene items within a group. + +--------------------- + +.. function:: void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item) +.. function:: void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item) + + Allows the ability to call any one of the transform functions on + scene items within a group without updating the internal matrices of + the group until obs_sceneitem_defer_group_resize_end has been called. + + This is necessary if the user is resizing items while they are within + a group, as the group's transform will automatically update its + transform every frame otherwise. diff --git a/docs/sphinx/reference-services.rst b/docs/sphinx/reference-services.rst new file mode 100644 index 000000000..fdfb85e41 --- /dev/null +++ b/docs/sphinx/reference-services.rst @@ -0,0 +1,276 @@ +Service API Reference (obs_service_t) +===================================== + +Services are custom implementations of streaming services, which are +used with outputs that stream. For example, you could have a custom +implementation for streaming to Twitch, and another for YouTube to allow +the ability to log in and use their APIs to do things such as get the +RTMP servers or control the channel. The `libobs/obs-service.h`_ file +is the dedicated header for implementing services. + +*(Author's note: the service API is incomplete as of this writing)* + +.. type:: obs_service_t + + A reference-counted service object. + +.. type:: obs_weak_service_t + + A weak reference to a service object. + +.. code:: cpp + + #include + + +Service Definition Structure +---------------------------- + +.. type:: struct obs_service_info + + Service definition structure. + +.. member:: const char *obs_service_info.id + + Unique string identifier for the service (required). + +.. member:: const char *(*obs_service_info.get_name)(void *type_data) + + Get the translated name of the service type. + + :param type_data: The type_data variable of this structure + :return: The translated name of the service type + +.. member:: void *(*obs_service_info.create)(obs_data_t *settings, obs_service_t *service) + + Creates the implementation data for the service. + + :param settings: Settings to initialize the service with + :param service: Source that this data is associated with + :return: The implementation data associated with this service + +.. member:: void (*obs_service_info.destroy)(void *data) + + Destroys the implementation data for the service. + +.. member:: void (*obs_service_info.get_defaults)(obs_data_t *settings) + void (*obs_service_info.get_defaults2)(void *type_data, obs_data_t *settings) + + Sets the default settings for this service. + + :param settings: Default settings. Call obs_data_set_default* + functions on this object to set default setting + values + +.. member:: obs_properties_t *(*obs_service_info.get_properties)(void *data) + obs_properties_t *(*obs_service_info.get_properties2)(void *data, void *type_data) + + Gets the property information of this service. + + (Optional) + + :return: The properties of the service + +.. member:: void (*obs_service_info.update)(void *data, obs_data_t *settings) + + Updates the settings for this service. + + (Optional) + + :param settings: New settings for this service + +.. member:: bool (*obs_service_info.initialize)(void *data, obs_output_t *output) + + Called when getting ready to start up an output, before the encoders + and output are initialized. + + (Optional) + + :param output: Output context to use this service with + :return: *true* to allow the output to start up, + *false* to prevent output from starting up + +.. member:: const char *(*obs_service_info.get_url)(void *data) + + :return: The stream URL + +.. member:: const char *(*obs_service_info.get_key)(void *data) + + :return: The stream key + +.. member:: const char *(*obs_service_info.get_username)(void *data) + + (Optional) + + :return: The username + +.. member:: const char *(*obs_service_info.get_password)(void *data) + + (Optional) + + :return: The password + +.. member:: void (*obs_service_info.apply_encoder_settings)(void *data, obs_data_t *video_encoder_settings, obs_data_t *audio_encoder_settings) + + This function is called to apply custom encoder settings specific to + this service. For example, if a service requires a specific keyframe + interval, or has a bitrate limit, the settings for the video and + audio encoders can be optionally modified if the front-end optionally + calls :c:func:`obs_service_apply_encoder_settings()`. + + (Optional) + + :param video_encoder_settings: The audio encoder settings to change + :param audio_encoder_settings: The video encoder settings to change + +.. member:: void *obs_service_info.type_data + void (*obs_service_info.free_type_data)(void *type_data) + + Private data associated with this entry. Note that this is not the + same as the implementation data; this is used to differentiate + between two different types if the same callbacks are used for more + than one different type. + + (Optional) + +.. member:: const char *(*obs_service_info.get_output_type)(void *data) + + (Optional) + + :return: The output type that should be used with this service + + +General Service Functions +------------------------- + +.. function:: void obs_register_service(struct obs_service_info *info) + + Registers a service type. Typically used in + :c:func:`obs_module_load()` or in the program's initialization phase. + +--------------------- + +.. function:: const char *obs_service_get_display_name(const char *id) + + Calls the :c:member:`obs_service_info.get_name` callback to get the + translated display name of a service type. + + :param id: The service type string identifier + :return: The translated display name of a service type + +--------------------- + +.. function:: obs_service_t *obs_service_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data) + + Creates a service with the specified settings. + + The "service" context is used for encoding video/audio data. Use + obs_service_release to release it. + + :param id: The service type string identifier + :param name: The desired name of the service. If this is + not unique, it will be made to be unique + :param settings: The settings for the service, or *NULL* if + none + :param hotkey_data: Saved hotkey data for the service, or *NULL* + if none + :return: A reference to the newly created service, or + *NULL* if failed + +--------------------- + +.. function:: void obs_service_addref(obs_service_t *service) + void obs_service_release(obs_service_t *service) + + Adds/releases a reference to a service. When the last reference is + released, the service is destroyed. + +--------------------- + +.. function:: obs_weak_service_t *obs_service_get_weak_service(obs_service_t *service) + obs_service_t *obs_weak_service_get_service(obs_weak_service_t *weak) + + These functions are used to get a weak reference from a strong service + reference, or a strong service reference from a weak reference. If + the service is destroyed, *obs_weak_service_get_service* will return + *NULL*. + +--------------------- + +.. function:: void obs_weak_service_addref(obs_weak_service_t *weak) + void obs_weak_service_release(obs_weak_service_t *weak) + + Adds/releases a weak reference to a service. + +--------------------- + +.. function:: const char *obs_service_get_name(const obs_service_t *service) + + :return: The name of the service + +--------------------- + +.. function:: obs_data_t *obs_service_defaults(const char *id) + + :return: An incremented reference to the service's default settings + +--------------------- + +.. function:: obs_properties_t *obs_service_properties(const obs_service_t *service) + obs_properties_t *obs_get_service_properties(const char *id) + + Use these functions to get the properties of a service or service + type. Properties are optionally used (if desired) to automatically + generate user interface widgets to allow users to update settings. + + :return: The properties list for a specific existing service. Free + with :c:func:`obs_properties_destroy()` + +--------------------- + +.. function:: obs_data_t *obs_service_get_settings(const obs_service_t *service) + + :return: An incremented reference to the service's settings + +--------------------- + +.. function:: void obs_service_update(obs_service_t *service, obs_data_t *settings) + + Updates the settings for this service context. + +--------------------- + +.. function:: const char *obs_service_get_url(const obs_service_t *service) + + :return: The URL currently used for this service + +--------------------- + +.. function:: const char *obs_service_get_key(const obs_service_t *service) + + :return: Stream key (if any) currently used for this service + +--------------------- + +.. function:: const char *obs_service_get_username(const obs_service_t *service) + + :return: User name (if any) currently used for this service + +--------------------- + +.. function:: const char *obs_service_get_password(const obs_service_t *service) + + :return: Password (if any) currently used for this service + +--------------------- + +.. function:: void obs_service_apply_encoder_settings(obs_service_t *service, obs_data_t *video_encoder_settings, obs_data_t *audio_encoder_settings) + + Applies service-specific video encoder settings. + + :param video_encoder_settings: Video encoder settings. Can be *NULL* + :param audio_encoder_settings: Audio encoder settings. Can be *NULL* + +.. --------------------------------------------------------------------------- + +.. _libobs/obs-service.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-service.h diff --git a/docs/sphinx/reference-settings.rst b/docs/sphinx/reference-settings.rst new file mode 100644 index 000000000..91cd4ba87 --- /dev/null +++ b/docs/sphinx/reference-settings.rst @@ -0,0 +1,284 @@ +Data Settings API Reference (obs_data_t) +======================================== + +Data settings objects are reference-counted objects that store values in +a string-table or array. They're similar to Json objects, but +additionally allow additional functionality such as default or +auto-selection values. Data is saved/loaded to/from Json text and Json +text files. + +.. type:: obs_data_t + + A reference-counted data object. + +.. type:: obs_data_array_t + + A reference-counted data array object. + +.. code:: cpp + + #include + + +General Functions +----------------- + +.. function:: obs_data_t *obs_data_create() + + :return: A new reference to a data object. + +--------------------- + +.. function:: obs_data_t *obs_data_create_from_json(const char *json_string) + + Creates a data object from a Json string. + + :param json_string: Json string + :return: A new reference to a data object + +--------------------- + +.. function:: obs_data_t *obs_data_create_from_json_file(const char *json_file) + + Creates a data object from a Json file. + + :param json_file: Json file path + :return: A new reference to a data object + +--------------------- + +.. function:: obs_data_t *obs_data_create_from_json_file_safe(const char *json_file, const char *backup_ext) + + Creates a data object from a Json file, with a backup file in case + the original is corrupted or fails to load. + + :param json_file: Json file path + :param backup_ext: Backup file extension + :return: A new reference to a data object + +--------------------- + +.. function:: void obs_data_addref(obs_data_t *data) + void obs_data_release(obs_data_t *data) + + Adds/releases a reference to a data object. + +--------------------- + +.. function:: const char *obs_data_get_json(obs_data_t *data) + + :return: Json string for this object + +--------------------- + +.. function:: bool obs_data_save_json(obs_data_t *data, const char *file) + + Saves the data to a file as Json text. + + :param file: The file to save to + :return: *true* if successful, *false* otherwise + +--------------------- + +.. function:: bool obs_data_save_json_safe(obs_data_t *data, const char *file, const char *temp_ext, const char *backup_ext) + + Saves the data to a file as Json text, and if overwriting an old + file, backs up that old file to help prevent potential file + corruption. + + :param file: The file to save to + :param backup_ext: The backup extension to use for the overwritten + file if it exists + :return: *true* if successful, *false* otherwise + +--------------------- + +.. function:: void obs_data_apply(obs_data_t *target, obs_data_t *apply_data) + + Merges the data of *apply_data* in to *target*. + +--------------------- + +.. function:: void obs_data_erase(obs_data_t *data, const char *name) + + Erases the user data for item *name* within the data object. + +--------------------- + +.. function:: void obs_data_clear(obs_data_t *data) + + Clears all user data in the data object. + +--------------------- + + +Set Functions +------------- + +.. function:: void obs_data_set_string(obs_data_t *data, const char *name, const char *val) + +--------------------- + +.. function:: void obs_data_set_int(obs_data_t *data, const char *name, long long val) + +--------------------- + +.. function:: void obs_data_set_double(obs_data_t *data, const char *name, double val) + +--------------------- + +.. function:: void obs_data_set_bool(obs_data_t *data, const char *name, bool val) + +--------------------- + +.. function:: void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj) + +--------------------- + +.. function:: void obs_data_set_array(obs_data_t *data, const char *name, obs_data_array_t *array) + +--------------------- + + +.. _obs_data_get_funcs: + +Get Functions +------------- + +.. function:: const char *obs_data_get_string(obs_data_t *data, const char *name) + +--------------------- + +.. function:: long long obs_data_get_int(obs_data_t *data, const char *name) + +--------------------- + +.. function:: double obs_data_get_double(obs_data_t *data, const char *name) + +--------------------- + +.. function:: bool obs_data_get_bool(obs_data_t *data, const char *name) + +--------------------- + +.. function:: obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name) + + :return: An incremented reference to a data object. + +--------------------- + +.. function:: obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name) + + :return: An incremented reference to a data array object. + +--------------------- + + +.. _obs_data_default_funcs: + +Default Value Functions +----------------------- + +Default values are used to determine what value will be given if a value +is not set. + +.. function:: void obs_data_set_default_string(obs_data_t *data, const char *name, const char *val) + const char *obs_data_get_default_string(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_default_int(obs_data_t *data, const char *name, long long val) + long long obs_data_get_default_int(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_default_double(obs_data_t *data, const char *name, double val) + double obs_data_get_default_double(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_default_bool(obs_data_t *data, const char *name, bool val) + bool obs_data_get_default_bool(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_default_obj(obs_data_t *data, const char *name, obs_data_t *obj) + obs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name) + + :return: An incremented reference to a data object. + +--------------------- + + +Autoselect Functions +-------------------- + +Autoselect values are optionally used to determine what values should be +used to ensure functionality if the currently set values are +inappropriate or invalid. + +.. function:: void obs_data_set_autoselect_string(obs_data_t *data, const char *name, const char *val) + const char *obs_data_get_autoselect_string(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_autoselect_int(obs_data_t *data, const char *name, long long val) + long long obs_data_get_autoselect_int(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_autoselect_double(obs_data_t *data, const char *name, double val) + double obs_data_get_autoselect_double(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_autoselect_bool(obs_data_t *data, const char *name, bool val) + bool obs_data_get_autoselect_bool(obs_data_t *data, const char *name) + +--------------------- + +.. function:: void obs_data_set_autoselect_obj(obs_data_t *data, const char *name, obs_data_t *obj) + obs_data_t *obs_data_get_autoselect_obj(obs_data_t *data, const char *name) + + :return: An incremented reference to a data object. + +--------------------- + + +Array Functions +--------------- + +.. function:: obs_data_array_t *obs_data_array_create() + + :return: A new reference to a data array object. + +--------------------- + +.. function:: void obs_data_array_addref(obs_data_array_t *array) + +--------------------- + +.. function:: void obs_data_array_release(obs_data_array_t *array) + +--------------------- + +.. function:: size_t obs_data_array_count(obs_data_array_t *array) + +--------------------- + +.. function:: obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx) + + :return: An incremented reference to the data object associated with + this array entry. + +--------------------- + +.. function:: size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj) + +--------------------- + +.. function:: void obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj) + +--------------------- + +.. function:: void obs_data_array_erase(obs_data_array_t *array, size_t idx) diff --git a/docs/sphinx/reference-sources.rst b/docs/sphinx/reference-sources.rst new file mode 100644 index 000000000..2b7233bc0 --- /dev/null +++ b/docs/sphinx/reference-sources.rst @@ -0,0 +1,1363 @@ +Source API Reference (obs_source_t) +=================================== + +Sources are used to render video and/or audio on stream. Things such as +capturing displays/games/audio, playing a video, showing an image, or +playing audio. Sources can also be used to implement audio and video +filters as well as transitions. The `libobs/obs-source.h`_ file is the +dedicated header for implementing sources. + +.. type:: obs_source_t + + A reference-counted video/audio input source. + +.. type:: obs_weak_source_t + + A weak reference to an video/audio input source. + +.. code:: cpp + + #include + + +Source Definition Structure (obs_source_info) +--------------------------------------------- + +.. type:: struct obs_source_info + + Source definition structure. + +.. member:: const char *obs_source_info.id + + Unique string identifier for the source (required). + +.. member:: enum obs_source_type obs_source_info.type + + Type of source. + + - **OBS_SOURCE_TYPE_INPUT** - Video/Audio Input + - **OBS_SOURCE_TYPE_FILTER** - Filter + - **OBS_SOURCE_TYPE_TRANSITION** - Transition + +.. member:: uint32_t obs_source_info.output_flags + + Source output capability flags (required). + + (Author's note: This should be renamed to "capability_flags") + + A bitwise OR combination of one or more of the following values: + + - **OBS_SOURCE_VIDEO** - Source has video + + Unless SOURCE_ASYNC_VIDEO is specified, the source must include the + :c:member:`obs_source_info.video_render` callback in the source + definition structure. + + - **OBS_SOURCE_AUDIO** - Source has audio + + Use the :c:func:`obs_source_output_audio()` function to pass raw + audio data, which will be automatically converted and uploaded. If + used with OBS_SOURCE_ASYNC_VIDEO, audio will automatically be + synced up to the video output based upon their mutual timestamps. + + - **OBS_SOURCE_ASYNC** - Video is asynchronous (use + OBS_SOURCE_ASYNC_VIDEO instead to automatically combine this flag + with the OBS_SOURCE_VIDEO flag). + + - **OBS_SOURCE_ASYNC_VIDEO** - Source passes raw video data via RAM + + Use the :c:func:`obs_source_output_video()` function to pass raw + video data, which will be automatically drawn at a timing relative + to the provided timestamp. + + If audio is also present on the source, the audio will + automatically be synced to the video based upon their mutual + timestamps. + + - **OBS_SOURCE_CUSTOM_DRAW** - Source uses custom graphics calls, + rather than just rendering a single texture. + + This capability flag must be used if the source does not use + :c:func:`obs_source_draw()` to render a single texture. + + This capability flag is an important hint to turn off a specific + optimization that allows the first effect filter in the filter + chain to render the source directly with that effect filter. The + optimization does not work if there are custom graphics calls, and + the source must be rendered to a texture first before being sent to + the first filter in the filter chain. + + (Author's note: Ironically, not many sources render with that + optimization. I should have made it so that the optimization isn't + used by default, and a flag should have been used to turn on the + optimization -- not turn it off). + + - **OBS_SOURCE_INTERACTION** - Source can be interacted with by the + user. + + When this is used, the source will receive interaction events if + theese callbacks are provided: + :c:member:`obs_source_info.mouse_click`, + :c:member:`obs_source_info.mouse_move`, + :c:member:`obs_source_info.mouse_wheel`, + :c:member:`obs_source_info.focus`, and + :c:member:`obs_source_info.key_click`. + + - **OBS_SOURCE_COMPOSITE** - Source composites child sources + + When used, specifies that the source composites one or more child + sources. Scenes and transitions are examples of sources that + contain and render child sources. + + Sources that render sub-sources must implement the audio_render + callback in order to perform custom audio mixing of child sources. + + This capability flag is always set for transitions. + + - **OBS_SOURCE_DO_NOT_DUPLICATE** - Source should not be fully + duplicated. + + When this is used, specifies that the source should not be fully + duplicated, and should prefer to duplicate via holding references + rather than full duplication. + + When functions such as :c:func:`obs_source_duplicate()` or + :c:func:`obs_scene_duplicate()` are called, sources or child + sources with this flag will never be fully duplicated, and will + instead only be referenced. + + An example of the type of sources that should not be fully + duplicated are video devices, browsers, and video/audio captures, + as they will either not function correctly or will cause + performance or resource issues when duplicated. + + - **OBS_SOURCE_DEPRECATED** - Source is deprecated and should not be + used. + + - **OBS_SOURCE_DO_NOT_SELF_MONITOR** - Audio of this source should + not allow monitoring if the current monitoring device is the same + device being captured by the source. + + This flag is used as a hint to the back-end to prevent the source + from creating an audio feedback loop. This is primarily only used + with desktop audio capture sources. + +.. member:: const char *(*obs_source_info.get_name)(void *type_data) + + Get the translated name of the source type. + + :param type_data: The type_data variable of this structure + :return: The translated name of the source type + +.. member:: void *(*obs_source_info.create)(obs_data_t *settings, obs_source_t *source) + + Creates the implementation data for the source. + + :param settings: Settings to initialize the source with + :param source: Source that this data is associated with + :return: The implementation data associated with this source + +.. member:: void (*obs_source_info.destroy)(void *data) + + Destroys the implementation data for the source. + + Async sources must not call obs_source_output_video after returning + from destroy. + +.. member:: uint32_t (*obs_source_info.get_width)(void *data) + uint32_t (*obs_source_info.get_height)(void *data); + + Returns the width/height of the source. These callbacks are required + if this is a video source and is synchronous. + + (Author's note: These should really be consolidated in to one + function, not two) + + :return: The width/height of the video + +.. member:: void (*obs_source_info.get_defaults)(obs_data_t *settings) + void (*obs_source_info.get_defaults2)(void *type_data, obs_data_t *settings) + + Sets the default settings for this source. + + :param settings: Default settings. Call obs_data_set_default* + functions on this object to set default setting + values + +.. member:: obs_properties_t *(*obs_source_info.get_properties)(void *data) + obs_properties_t *(*obs_source_info.get_properties2)(void *data, void *type_data) + + Gets the property information of this source. + + (Optional) + + :return: The properties of the source + +.. member:: void (*obs_source_info.update)(void *data, obs_data_t *settings) + + Updates the settings for this source. + + (Optional) + + :param settings: New settings for this source + +.. member:: void (*obs_source_info.activate)(void *data) + + Called when the source has been activated in the main view (visible + on stream/recording). + + (Optional) + +.. member:: void (*obs_source_info.deactivate)(void *data) + + Called when the source has been deactivated from the main view (no + longer visible on stream/recording). + + (Optional) + +.. member:: void (*obs_source_info.show)(void *data) + + Called when the source is visible on any display and/or on the main + view. + + (Optional) + +.. member:: void (*obs_source_info.hide)(void *data) + + Called when the source is no longer visible on any display and/or on + the main view. + + (Optional) + +.. member:: void (*obs_source_info.video_tick)(void *data, float seconds) + + Called each video frame with the time elapsed. + + (Optional) + + :param seconds: Seconds elapsed since the last frame + +.. member:: void (*obs_source_info.video_render)(void *data, gs_effect_t *effect) + + Called when rendering the source with the graphics subsystem. + + If this is an input/transition source, this is called to draw the + source texture with the graphics subsystem. + + If this is a filter source, it wraps source draw calls (for example + applying a custom effect with custom parameters to a source). In + this case, it's highly recommended to use the + :c:func:`obs_source_process_filter_begin()` and + :c:func:`obs_source_process_filter_end()` functions to automatically + handle effect-based filter processing. However, you can implement + custom draw handling as desired as well. + + If the source output capability flags do not include + OBS_SOURCE_CUSTOM_DRAW, the source must use + :c:func:`obs_source_draw()` to render the source's texture. + + :param effect: This parameter is no longer used. Instead, call + :c:func:`obs_source_draw()` + +.. member:: struct obs_source_frame *(*obs_source_info.filter_video)(void *data, struct obs_source_frame *frame) + + Called to filter raw async video data. This function is only used + with asynchronous video filters. + + :param frame: Video frame to filter + :return: New video frame data. This can defer video data to + be drawn later if time is needed for processing + +.. member:: struct obs_audio_data *(*obs_source_info.filter_audio)(void *data, struct obs_audio_data *audio) + + Called to filter raw audio data. This function is only used with + audio filters. + + :param audio: Audio data to filter + :return: Modified or new audio data. You can directly modify + the data passed and return it, or you can defer audio + data for later if time is needed for processing. If + you are returning new data, that data must exist until + the next call to the + :c:member:`obs_source_info.filter_audio` callback or + until the filter is removed/destroyed + +.. member:: void (*obs_source_info.enum_active_sources)(void *data, obs_source_enum_proc_t enum_callback, void *param) + + Called to enumerate all active sources being used within this + source. If the source has children that render audio/video it must + implement this callback. Only used with sources that have tha + OBS_SOURCE_COMPOSITE output capability flag. + + :param enum_callback: Enumeration callback + :param param: User data to pass to callback + +.. member:: void (*obs_source_info.save)(void *data, obs_data_t *settings) + + Called when saving custom data for a source. This is a separate + function because sometimes a source needs to know when it is being + saved so it doesn't always have to update the current settings until + a certain point. + + (Optional) + + :param settings: Settings object to save data to + +.. member:: void (*obs_source_info.load)(void *data, obs_data_t *settings) + + Called when loading custom data from saved source data. This is + called after all the loading sources have actually been created, + allowing the ability to reference other sources if desired. + + (Optional) + + :param settings: Settings object to load data from + +.. member:: void (*obs_source_info.mouse_click)(void *data, const struct obs_mouse_event *event, int32_t type, bool mouse_up, uint32_t click_count) + + Called when interacting with a source and a mouse-down or mouse-up + occurs. Only used with sources that have the OBS_SOURCE_INTERACTION + output capability flag. + + (Optional) + + :param event: Mouse event properties + :param type: Mouse button pushed + :param mouse_up: Mouse event type (true if mouse-up) + :param click_count: Mouse click count (1 for single click, etc.) + +.. member:: void (*obs_source_info.mouse_move)(void *data, const struct obs_mouse_event *event, bool mouse_leave) + + Called when interacting with a source and a mouse-move occurs. Only + used with sources that have the OBS_SOURCE_INTERACTION output + capability flag. + + (Optional) + + :param event: Mouse event properties + :param mouse_leave: Mouse leave state (true if mouse left source) + +.. member:: void (*obs_source_info.mouse_wheel)(void *data, const struct obs_mouse_event *event, int x_delta, int y_delta) + + Called when interacting with a source and a mouse-wheel occurs. Only + used with sources that have the OBS_SOURCE_INTERACTION output + capability flag. + + (Optional) + + :param event: Mouse event properties + :param x_delta: Movement delta in the horizontal direction + :param y_delta: Movement delta in the vertical direction + + +.. member:: void (*obs_source_info.focus)(void *data, bool focus) + + Called when interacting with a source and gain focus/lost focus event + occurs. Only used with sources that have the OBS_SOURCE_INTERACTION + output capability flag. + + (Optional) + + :param focus: Focus state (true if focus gained) + +.. member:: void (*obs_source_info.key_click)(void *data, const struct obs_key_event *event, bool key_up) + + Called when interacting with a source and a key-up or key-down + occurs. Only used with sources that have the OBS_SOURCE_INTERACTION + output capability flag. + + (Optional) + + :param event: Key event properties + :param focus: Key event type (true if mouse-up) + +.. member:: void (*obs_source_info.filter_remove)(void *data, obs_source_t *source) + + Called when the filter is removed from a source. + + (Optional) + + :param data: Filter data + :param source: Source that the filter being removed from + +.. member:: void *obs_source_info.type_data + void (*obs_source_info.free_type_data)(void *type_data) + + Private data associated with this entry. Note that this is not the + same as the implementation data; this is used to differentiate + between two different types if the same callbacks are used for more + than one different type. + +.. member:: bool (*obs_source_info.audio_render)(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio_output, uint32_t mixers, size_t channels, size_t sample_rate) + + Called to render audio of composite sources. Only used with sources + that have tha OBS_SOURCE_COMPOSITE output capability flag. + +.. member:: void (*obs_source_info.enum_all_sources)(void *data, obs_source_enum_proc_t enum_callback, void *param) + + Called to enumerate all active and inactive sources being used + within this source. If this callback isn't implemented, + enum_active_sources will be called instead. Only used with sources + that have tha OBS_SOURCE_COMPOSITE output capability flag. + + This is typically used if a source can have inactive child sources. + + :param enum_callback: Enumeration callback + :param param: User data to pass to callback + +.. member:: void (*obs_source_info.transition_start)(void *data) + void (*obs_source_info.transition_stop)(void *data) + + Called on transition sources when the transition starts/stops. + + (Optional) + + +.. _source_signal_handler_reference: + +Source Signals +-------------- + +**destroy** (ptr *source*) + + This signal is called when the source is about to be destroyed. Do + not increment any references when using this signal. + +**remove** (ptr source) + + Called when the :c:func:`obs_source_remove()` function is called on + the source. + +**save** (ptr source) + + Called when the source is being saved. + +**load** (ptr source) + + Called when the source is being loaded. + +**activate** (ptr source) + + Called when the source has been activated in the main view (visible + on stream/recording). + +**deactivate** (ptr source) + + Called when the source has been deactivated from the main view (no + longer visible on stream/recording). + +**show** (ptr source) + + Called when the source is visible on any display and/or on the main + view. + +**hide** (ptr source) + + Called when the source is no longer visible on any display and/or on + the main view. + +**mute** (ptr source, bool muted) + + Called when the source is muted/unmuted. + +**push_to_mute_changed** (ptr source, bool enabled) + + Called when push-to-mute has been enabled/disabled. + +**push_to_mute_delay** (ptr source, int delay) + + Called when the push-to-mute delay value has changed. + +**push_to_talk_changed** (ptr source, bool enabled) + + Called when push-to-talk has been enabled/disabled. + +**push_to_talk_delay** (ptr source, int delay) + + Called when the push-to-talk delay value has changed. + +**enable** (ptr source, bool enabled) + + Called when the source has been disabled/enabled. + +**rename** (ptr source, string new_name, string prev_name) + + Called when the source has been renamed. + +**volume** (ptr source, in out float volume) + + Called when the volume of the source has changed. + +**update_properties** (ptr source) + + Called when the properties of the source have been updated. + +**update_flags** (ptr source, int flags) + + Called when the flags of the source have been changed. + +**audio_sync** (ptr source, int out int offset) + + Called when the audio sync offset has changed. + +**audio_mixers** (ptr source, in out int mixers) + + Called when the audio mixers have changed. + +**filter_add** (ptr source, ptr filter) + + Called when a filter has been added to the source. + +**filter_remove** (ptr source, ptr filter) + + Called when a filter has been removed from the source. + +**reorder_filters** (ptr source) + + Called when filters have been reordered. + +**transition_start** (ptr source) + + Called when a transition is starting. + +**transition_video_stop** (ptr source) + + Called when a transition's video transitioning has stopped. + +**transition_stop** (ptr source) + + Called when a transition has stopped. + + +General Source Functions +------------------------ + +.. function:: void obs_register_source(struct obs_source_info *info) + + Registers a source type. Typically used in + :c:func:`obs_module_load()` or in the program's initialization phase. + +--------------------- + +.. function:: const char *obs_source_get_display_name(const char *id) + + Calls the :c:member:`obs_source_info.get_name` callback to get the + translated display name of a source type. + + :param id: The source type string identifier + :return: The translated display name of a source type + +--------------------- + +.. function:: obs_source_t *obs_source_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data) + + Creates a source of the specified type with the specified settings. + + The "source" context is used for anything related to presenting + or modifying video/audio. Use obs_source_release to release it. + + :param id: The source type string identifier + :param name: The desired name of the source. If this is + not unique, it will be made to be unique + :param settings: The settings for the source, or *NULL* if + none + :param hotkey_data: Saved hotkey data for the source, or *NULL* + if none + :return: A reference to the newly created source, or + *NULL* if failed + +--------------------- + +.. function:: obs_source_t *obs_source_create_private(const char *id, const char *name, obs_data_t *settings) + + Creates a 'private' source which is not enumerated by + :c:func:`obs_enum_sources()`, and is not saved by + :c:func:`obs_save_sources()`. + + Author's Note: The existence of this function is a result of design + flaw: the front-end should control saving/loading of sources, and + functions like :c:func:`obs_enum_sources()` and + :c:func:`obs_save_sources()` should not exist in the back-end. + + :param id: The source type string identifier + :param name: The desired name of the source. For private + sources, this does not have to be unique, + and can additionally be *NULL* if desired + :param settings: The settings for the source, or *NULL* if + none + :return: A reference to the newly created source, or + *NULL* if failed + +--------------------- + +.. function:: obs_source_t *obs_source_duplicate(obs_source_t *source, const char *desired_name, bool create_private) + + Duplicates a source. If the source has the + OBS_SOURCE_DO_NOT_DUPLICATE output flag set, this only returns a + new reference to the same source. + + :param source: The source to duplicate + :param desired_name: The desired name of the new source. If this is + not a private source and the name is not unique, + it will be made to be unique + :param create_private: If *true*, the new source will be a private + source if fully duplicated + :return: A new source reference + +--------------------- + +.. function:: void obs_source_addref(obs_source_t *source) + void obs_source_release(obs_source_t *source) + + Adds/releases a reference to a source. When the last reference is + released, the source is destroyed. + +--------------------- + +.. function:: obs_weak_source_t *obs_source_get_weak_source(obs_source_t *source) + obs_source_t *obs_weak_source_get_source(obs_weak_source_t *weak) + + These functions are used to get a weak reference from a strong source + reference, or a strong source reference from a weak reference. If + the source is destroyed, *obs_weak_source_get_source* will return + *NULL*. + +--------------------- + +.. function:: void obs_weak_source_addref(obs_weak_source_t *weak) + void obs_weak_source_release(obs_weak_source_t *weak) + + Adds/releases a weak reference to a source. + +--------------------- + +.. function:: void obs_source_remove(obs_source_t *source) + + Notifies all reference holders of the source (via + :c:func:`obs_source_removed()`) that the source should be released. + +--------------------- + +.. function:: bool obs_source_removed(const obs_source_t *source) + + :return: *true* if the source should be released + +--------------------- + +.. function:: uint32_t obs_source_get_output_flags(const obs_source_t *source) + uint32_t obs_get_source_output_flags(const char *id) + + :return: Capability flags of a source + + Author's Note: "Output flags" is poor wording in retrospect; this + should have been named "Capability flags", and the OBS_SOURCE_* + macros should really be OBS_SOURCE_CAP_* macros instead. + + See :c:member:`obs_source_info.output_flags` for more information. + +--------------------- + +.. function:: obs_data_t *obs_get_source_defaults(const char *id) + + Calls :c:member:`obs_source_info.get_defaults` to get the defaults + settings of the source type. + + :return: The default settings for a source type + +--------------------- + +.. function:: obs_properties_t *obs_source_properties(const obs_source_t *source) + obs_properties_t *obs_get_source_properties(const char *id) + + Use these functions to get the properties of a source or source type. + Properties are optionally used (if desired) to automatically generate + user interface widgets to allow users to update settings. + + :return: The properties list for a specific existing source. Free with + :c:func:`obs_properties_destroy()` + +--------------------- + +.. function:: bool obs_source_configurable(const obs_source_t *source) + bool obs_is_source_configurable(const char *id) + + :return: *true* if the the source has custom properties, *false* + otherwise + +--------------------- + +.. function:: void obs_source_update(obs_source_t *source, obs_data_t *settings) + + Updates the settings for a source and calls the + :c:member:`obs_source_info.update` callback of the source. If the + source is a video source, the :c:member:`obs_source_info.update` will + be not be called immediately; instead, it will be deferred to the + video thread to prevent threading issues. + +--------------------- + +.. function:: void obs_source_video_render(obs_source_t *source) + + Renders a video source. This will call the + :c:member:`obs_source_info.video_render` callback of the source. + +--------------------- + +.. function:: uint32_t obs_source_get_width(obs_source_t *source) + uint32_t obs_source_get_height(obs_source_t *source) + + Calls the :c:member:`obs_source_info.get_width` or + :c:member:`obs_source_info.get_height` of the source to get its width + and/or height. + + Author's Note: These functions should be consolidated in to a single + function/callback rather than having a function for both width and + height. + + :return: The width or height of the source + +--------------------- + +.. function:: obs_data_t *obs_source_get_settings(const obs_source_t *source) + + :return: The settings string for a source. The reference counter of the + returned settings data is incremented, so + :c:func:`obs_data_release()` must be called when the + settings are no longer used + +--------------------- + +.. function:: const char *obs_source_get_name(const obs_source_t *source) + + :return: The name of the source + +--------------------- + +.. function:: void obs_source_set_name(obs_source_t *source, const char *name) + + Sets the name of a source. If the source is not private and the name + is not unique, it will automatically be given a unique name. + +--------------------- + +.. function:: enum obs_source_type obs_source_get_type(const obs_source_t *source) + + :return: | OBS_SOURCE_TYPE_INPUT for inputs + | OBS_SOURCE_TYPE_FILTER for filters + | OBS_SOURCE_TYPE_TRANSITION for transitions + | OBS_SOURCE_TYPE_SCENE for scenes + +--------------------- + +.. function:: const char *obs_source_get_id(const obs_source_t *source) + + :return: The source's type identifier string + +--------------------- + +.. function:: signal_handler_t *obs_source_get_signal_handler(const obs_source_t *source) + + :return: The source's signal handler + + See the :ref:`source_signal_handler_reference` for more information + on signals that are available for sources. + +--------------------- + +.. function:: proc_handler_t *obs_source_get_proc_handler(const obs_source_t *source) + + :return: The procedure handler for a source + +--------------------- + +.. function:: void obs_source_set_volume(obs_source_t *source, float volume) + float obs_source_get_volume(const obs_source_t *source) + + Sets/gets the user volume for a source that has audio output. + +--------------------- + +.. function:: bool obs_source_muted(const obs_source_t *source) + void obs_source_set_muted(obs_source_t *source, bool muted) + + Sets/gets whether the source's audio is muted. + +--------------------- + +.. function:: bool obs_source_push_to_mute_enabled(const obs_source_t *source) + void obs_source_enable_push_to_mute(obs_source_t *source, bool enabled) + + Sets/gets whether push-to-mute is enabled. + +--------------------- + +.. function:: uint64_t obs_source_get_push_to_mute_delay(const obs_source_t *source) + void obs_source_set_push_to_mute_delay(obs_source_t *source, uint64_t delay) + + Sets/gets the push-to-mute delay. + +--------------------- + +.. function:: bool obs_source_push_to_talk_enabled(const obs_source_t *source) + void obs_source_enable_push_to_talk(obs_source_t *source, bool enabled) + + Sets/gets whether push-to-talk is enabled. + +--------------------- + +.. function:: uint64_t obs_source_get_push_to_talk_delay(const obs_source_t *source) + void obs_source_set_push_to_talk_delay(obs_source_t *source, uint64_t delay) + + Sets/gets the push-to-talk delay. + +--------------------- + +.. function:: void obs_source_set_sync_offset(obs_source_t *source, int64_t offset) + int64_t obs_source_get_sync_offset(const obs_source_t *source) + + Sets/gets the audio sync offset (in nanoseconds) for a source. + +--------------------- + +.. function:: void obs_source_enum_active_sources(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param) + void obs_source_enum_active_tree(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param) + + Enumerates active child sources or source tree used by this source. + + Relevant data types used with this function: + +.. code:: cpp + + typedef void (*obs_source_enum_proc_t)(obs_source_t *parent, + obs_source_t *child, void *param); + +--------------------- + +.. function:: bool obs_source_active(const obs_source_t *source) + + :return: *true* if active, *false* if not. A source is only + consdiered active if it's being shown on the final mix + +--------------------- + +.. function:: bool obs_source_showing(const obs_source_t *source) + + :return: *true* if showing, *false* if not. A source is considered + showing if it's being displayed anywhere at all, whether on + a display context or on the final output + +--------------------- + +.. function:: void obs_source_inc_showing(obs_source_t *source) + void obs_source_dec_showing(obs_source_t *source) + + Increments/decrements a source's "showing" state. Typically used + when drawing a source on a display manually. + +--------------------- + +.. function:: void obs_source_set_flags(obs_source_t *source, uint32_t flags) + uint32_t obs_source_get_flags(const obs_source_t *source) + + :param flags: OBS_SOURCE_FLAG_FORCE_MONO Forces audio to mono + +--------------------- + +.. function:: void obs_source_set_audio_mixers(obs_source_t *source, uint32_t mixers) + uint32_t obs_source_get_audio_mixers(const obs_source_t *source) + + Sets/gets the audio mixer channels that a source outputs to + (depending on what bits are set). Audio mixers allow filtering + specific using multiple audio encoders to mix different sources + together depending on what mixer channel they're set to. + + For example, to output to mixer 1 and 3, you would perform a bitwise + OR on bits 0 and 2: (1<<0) | (1<<2), or 0x5. + +--------------------- + +.. function:: void obs_source_enum_filters(obs_source_t *source, obs_source_enum_proc_t callback, void *param) + + Enumerates active filters on a source. + + Relevant data types used with this function: + +.. code:: cpp + + typedef void (*obs_source_enum_proc_t)(obs_source_t *parent, + obs_source_t *child, void *param); + +--------------------- + +.. function:: obs_source_t *obs_source_get_filter_by_name(obs_source_t *source, const char *name) + + :return: The desired filter, or *NULL* if not found. The reference + of the filter is incremented + +--------------------- + +.. function:: void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src) + + Copies filters from the source to the destination. If filters by the + same name already exist in the destination source, the newer filters + will be given unique names. + +--------------------- + +.. function:: bool obs_source_enabled(const obs_source_t *source) + void obs_source_set_enabled(obs_source_t *source, bool enabled) + + Enables/disables a source, or returns the enabled state. + +--------------------- + +.. function:: void obs_source_add_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback, void *param) + void obs_source_remove_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback, void *param) + + Adds/removes an audio capture callback for a source. This allows the + ability to get the raw audio data of a source as it comes in. + + Relevant data types used with this function: + +.. code:: cpp + + typedef void (*obs_source_audio_capture_t)(void *param, obs_source_t *source, + const struct audio_data *audio_data, bool muted); + +--------------------- + +.. function:: void obs_source_set_deinterlace_mode(obs_source_t *source, enum obs_deinterlace_mode mode) + enum obs_deinterlace_mode obs_source_get_deinterlace_mode(const obs_source_t *source) + + Sets/gets the deinterlace mode. + + :param mode: | OBS_DEINTERLACE_MODE_DISABLE - Disables deinterlacing + | OBS_DEINTERLACE_MODE_DISCARD - Discard + | OBS_DEINTERLACE_MODE_RETRO - Retro + | OBS_DEINTERLACE_MODE_BLEND - Blend + | OBS_DEINTERLACE_MODE_BLEND_2X - Blend 2x + | OBS_DEINTERLACE_MODE_LINEAR - Linear + | OBS_DEINTERLACE_MODE_LINEAR_2X - Linear 2x + | OBS_DEINTERLACE_MODE_YADIF - Yadif + | OBS_DEINTERLACE_MODE_YADIF_2X - Yadif 2x + + +--------------------- + +.. function:: void obs_source_set_deinterlace_field_order(obs_source_t *source, enum obs_deinterlace_field_order order) + enum obs_deinterlace_field_order obs_source_get_deinterlace_field_order(const obs_source_t *source) + + Sets/gets the deinterlace field order. + + :param order: | OBS_DEINTERLACE_FIELD_ORDER_TOP - Start from top + | OBS_DEINTERLACE_FIELD_ORDER_BOTTOM - Start from bottom + +--------------------- + +.. function:: obs_data_t *obs_source_get_private_settings(obs_source_t *item) + + Gets private front-end settings data. This data is saved/loaded + automatically. Returns an incremented reference. + +--------------------- + +.. function:: void obs_source_send_mouse_click(obs_source_t *source, const struct obs_mouse_event *event, int32_t type, bool mouse_up, uint32_t click_count) + + Used for interacting with sources: sends a mouse down/up event to a + source. + +--------------------- + +.. function:: void obs_source_send_mouse_move(obs_source_t *source, const struct obs_mouse_event *event, bool mouse_leave) + + Used for interacting with sources: sends a mouse move event to a + source. + +--------------------- + +.. function:: void obs_source_send_mouse_wheel(obs_source_t *source, const struct obs_mouse_event *event, int x_delta, int y_delta) + + Used for interacting with sources: sends a mouse wheel event to a + source. + +--------------------- + +.. function:: void obs_source_send_focus(obs_source_t *source, bool focus) + + Used for interacting with sources: sends a got-focus or lost-focus + event to a source. + +--------------------- + +.. function:: void obs_source_send_key_click(obs_source_t *source, const struct obs_key_event *event, bool key_up) + + Used for interacting with sources: sends a key up/down event to a + source. + +--------------------- + + +Functions used by sources +------------------------- + +.. function:: void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix, const struct vec3 *color_range_min, const struct vec3 *color_range_max) + + Helper function to set the color matrix information when drawing the + source. + + :param color_matrix: The color matrix. Assigns to the 'color_matrix' + effect variable. + :param color_range_min: The minimum color range. Assigns to the + 'color_range_min' effect variable. If NULL, + {0.0f, 0.0f, 0.0f} is used. + :param color_range_max: The maximum color range. Assigns to the + 'color_range_max' effect variable. If NULL, + {1.0f, 1.0f, 1.0f} is used. + +--------------------- + +.. function:: void obs_source_draw(gs_texture_t *image, int x, int y, uint32_t cx, uint32_t cy, bool flip) + + Helper function to draw sprites for a source (synchronous video). + + :param image: The sprite texture to draw. Assigns to the 'image' variable + of the current effect. + :param x: X position of the sprite. + :param y: Y position of the sprite. + :param cx: Width of the sprite. If 0, uses the texture width. + :param cy: Height of the sprite. If 0, uses the texture height. + :param flip: Specifies whether to flip the image vertically. + +--------------------- + +.. function:: void obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame) + + Outputs asynchronous video data. Set to NULL to deactivate the texture. + + Relevant data types used with this function: + +.. code:: cpp + + enum video_format { + VIDEO_FORMAT_NONE, + + /* planar 420 format */ + VIDEO_FORMAT_I420, /* three-plane */ + VIDEO_FORMAT_NV12, /* two-plane, luma and packed chroma */ + + /* packed 422 formats */ + VIDEO_FORMAT_YVYU, + VIDEO_FORMAT_YUY2, /* YUYV */ + VIDEO_FORMAT_UYVY, + + /* packed uncompressed formats */ + VIDEO_FORMAT_RGBA, + VIDEO_FORMAT_BGRA, + VIDEO_FORMAT_BGRX, + VIDEO_FORMAT_Y800, /* grayscale */ + + /* planar 4:4:4 */ + VIDEO_FORMAT_I444, + }; + + struct obs_source_frame { + uint8_t *data[MAX_AV_PLANES]; + uint32_t linesize[MAX_AV_PLANES]; + uint32_t width; + uint32_t height; + uint64_t timestamp; + + enum video_format format; + float color_matrix[16]; + bool full_range; + float color_range_min[3]; + float color_range_max[3]; + bool flip; + }; + +--------------------- + +.. function:: void obs_source_preload_video(obs_source_t *source, const struct obs_source_frame *frame) + + Preloads a video frame to ensure a frame is ready for playback as + soon as video playback starts. + +--------------------- + +.. function:: void obs_source_show_preloaded_video(obs_source_t *source) + + Shows any preloaded video frame. + +--------------------- + +.. function:: void obs_source_output_audio(obs_source_t *source, const struct obs_source_audio *audio) + + Outputs audio data. + +--------------------- + +.. function:: void obs_source_update_properties(obs_source_t *source) + + Signal an update to any currently used properties. + +--------------------- + +.. function:: bool obs_source_add_active_child(obs_source_t *parent, obs_source_t *child) + + Adds an active child source. Must be called by parent sources on child + sources when the child is added and active. This ensures that the source is + properly activated if the parent is active. + + :return: *true* if source can be added, *false* if it causes recursion + +--------------------- + +.. function:: void obs_source_remove_active_child(obs_source_t *parent, obs_source_t *child) + + Removes an active child source. Must be called by parent sources on child + sources when the child is removed or inactive. This ensures that the source + is properly deactivated if the parent is no longer active. + +--------------------- + + +Filters +------- + +.. function:: obs_source_t *obs_filter_get_parent(const obs_source_t *filter) + + If the source is a filter, returns the parent source of the filter. + The parent source is the source being filtered. + + Only guaranteed to be valid inside of the video_render, filter_audio, + filter_video, and filter_remove callbacks. + +--------------------- + +.. function:: obs_source_t *obs_filter_get_target(const obs_source_t *filter) + + If the source is a filter, returns the target source of the filter. + The target source is the next source in the filter chain. + + Only guaranteed to be valid inside of the video_render, filter_audio, + filter_video, and filter_remove callbacks. + +--------------------- + +.. function:: void obs_source_default_render(obs_source_t *source) + + Can be used by filters to directly render a non-async parent source + without any filter processing. + +--------------------- + +.. function:: void obs_source_filter_add(obs_source_t *source, obs_source_t *filter) + void obs_source_filter_remove(obs_source_t *source, obs_source_t *filter) + + Adds/removes a filter to/from a source. + +--------------------- + +.. function:: void obs_source_filter_set_order(obs_source_t *source, obs_source_t *filter, enum obs_order_movement movement) + + Modifies the order of a specific filter. + + :param movement: | Can be one of the following: + | OBS_ORDER_MOVE_UP + | OBS_ORDER_MOVE_DOWN + | OBS_ORDER_MOVE_TOP + | OBS_ORDER_MOVE_BOTTOM + +--------------------- + + +Functions used by filters +------------------------- + +.. function:: bool obs_source_process_filter_begin(obs_source_t *filter, enum gs_color_format format, enum obs_allow_direct_render allow_direct) + + Default RGB filter handler for generic effect filters. Processes the + filter chain and renders them to texture if needed, then the filter is + drawn with. + + After calling this, set your parameters for the effect, then call + obs_source_process_filter_end to draw the filter. + + :return: *true* if filtering should continue, *false* if the filter + is bypassed for whatever reason + +--------------------- + +.. function:: void obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height) + + Draws the filter using the effect's "Draw" technique. + + Before calling this function, first call obs_source_process_filter_begin and + then set the effect parameters, and then call this function to finalize the + filter. + +--------------------- + +.. function:: void obs_source_process_filter_tech_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height, const char *tech_name) + + Draws the filter with a specific technique in the effect. + + Before calling this function, first call obs_source_process_filter_begin and + then set the effect parameters, and then call this function to finalize the + filter. + +--------------------- + +.. function:: void obs_source_skip_video_filter(obs_source_t *filter) + + Skips the filter if the filter is invalid and cannot be rendered. + +--------------------- + + +.. _transitions: + +Transitions +----------- + +.. function:: obs_source_t *obs_transition_get_source(obs_source_t *transition, enum obs_transition_target target) + + :param target: | OBS_TRANSITION_SOURCE_A - Source being transitioned from, or the current source if not transitioning + | OBS_TRANSITION_SOURCE_B - Source being transitioned to + :return: An incremented reference to the source or destination + sources of the transition + +--------------------- + +.. function:: void obs_transition_clear(obs_source_t *transition) + + Clears the transition. + +--------------------- + +.. function:: obs_source_t *obs_transition_get_active_source(obs_source_t *transition) + + :return: An incremented reference to the currently active source of + the transition + +--------------------- + +.. function:: bool obs_transition_start(obs_source_t *transition, enum obs_transition_mode mode, uint32_t duration_ms, obs_source_t *dest) + + Starts the transition with the desired destination source. + + :param mode: Currently only OBS_TRANSITION_MODE_AUTO + :param duration_ms: Duration in milliseconds. If the transition has + a fixed duration set by + :c:func:`obs_transition_enable_fixed`, this + parameter will have no effect + :param dest: The destination source to transition to + +--------------------- + +.. function:: void obs_transition_set_size(obs_source_t *transition, uint32_t cx, uint32_t cy) + void obs_transition_get_size(const obs_source_t *transition, uint32_t *cx, uint32_t *cy) + + Sets/gets the dimensions of the transition. + +--------------------- + +.. function:: void obs_transition_set_scale_type(obs_source_t *transition, enum obs_transition_scale_type type) + enum obs_transition_scale_type obs_transition_get_scale_type( const obs_source_t *transition) + + Sets/gets the scale type for sources within the transition. + + :param type: | OBS_TRANSITION_SCALE_MAX_ONLY - Scale to aspect ratio, but only to the maximum size of each source + | OBS_TRANSITION_SCALE_ASPECT - Alwasy scale the sources, but keep aspect ratio + | OBS_TRANSITION_SCALE_STRETCH - Scale and stretch the sources to the size of the transision + +--------------------- + +.. function:: void obs_transition_set_alignment(obs_source_t *transition, uint32_t alignment) + uint32_t obs_transition_get_alignment(const obs_source_t *transition) + + Sets/gets the alignment used to draw the two sources within + transition the transition. + + :param alignment: | Can be any bitwise OR combination of: + | OBS_ALIGN_CENTER + | OBS_ALIGN_LEFT + | OBS_ALIGN_RIGHT + | OBS_ALIGN_TOP + | OBS_ALIGN_BOTTOM + +--------------------- + + +Functions used by transitions +----------------------------- + +.. function:: void obs_transition_enable_fixed(obs_source_t *transition, bool enable, uint32_t duration_ms) + bool obs_transition_fixed(obs_source_t *transition) + + Sets/gets whether the transition uses a fixed duration. Useful for + certain types of transitions such as stingers. If this is set, the + *duration_ms* parameter of :c:func:`obs_transition_start()` has no + effect. + +--------------------- + +.. function:: float obs_transition_get_time(obs_source_t *transition) + + :return: The current transition time value (0.0f..1.0f) + +--------------------- + +.. function:: void obs_transition_video_render(obs_source_t *transition, obs_transition_video_render_callback_t callback) + + Helper function used for rendering transitions. This function will + render two distinct textures for source A and source B of the + transition, allowing the ability to blend them together with a pixel + shader in a desired manner. + + The *a* and *b* parameters of *callback* are automatically rendered + textures of source A and source B, *t* is the time value + (0.0f..1.0f), *cx* and *cy* are the current dimensions of the + transition, and *data* is the implementation's private data. + + Relevant data types used with this function: + +.. code:: cpp + + typedef void (*obs_transition_video_render_callback_t)(void *data, + gs_texture_t *a, gs_texture_t *b, float t, + uint32_t cx, uint32_t cy); + +--------------------- + +.. function:: bool obs_transition_audio_render(obs_source_t *transition, uint64_t *ts_out, struct obs_source_audio_mix *audio, uint32_t mixers, size_t channels, size_t sample_rate, obs_transition_audio_mix_callback_t mix_a_callback, obs_transition_audio_mix_callback_t mix_b_callback) + + Helper function used for transitioning audio. Typically you'd call + this in the obs_source_info.audio_render callback with its + parameters, and use the mix_a_callback and mix_b_callback to + determine the the audio fading of source A and source B. + + Relevant data types used with this function: + +.. code:: cpp + + typedef float (*obs_transition_audio_mix_callback_t)(void *data, float t); + +--------------------- + +.. function:: void obs_transition_swap_begin(obs_source_t *tr_dest, obs_source_t *tr_source) + void obs_transition_swap_end(obs_source_t *tr_dest, obs_source_t *tr_source) + + Swaps two transitions. Call obs_transition_swap_begin, swap the + source, then call obs_transition_swap_end when complete. This allows + the ability to seamlessly swap two different transitions without it + affecting the output. + + For example, if a transition is assigned to output channel 0, you'd + call obs_transition_swap_begin, then you'd call obs_set_output_source + with the new transition, then call + :c:func:`obs_transition_swap_begin()`. + +.. --------------------------------------------------------------------------- + +.. _libobs/obs-source.h: https://github.com/jp9000/obs-studio/blob/master/libobs/obs-source.h diff --git a/docs/sphinx/scripting.rst b/docs/sphinx/scripting.rst new file mode 100644 index 000000000..855abd728 --- /dev/null +++ b/docs/sphinx/scripting.rst @@ -0,0 +1,320 @@ +Python/Lua Scripting +==================== + +Scripting (21.0+) adds support for Python 3 and Luajit 2 (which is +roughly equivalent to Lua 5.2), allowing the ability to quickly extend, +add features, or automate the program without needing to build a native +plugin module. + +Scripting can be accessed in OBS Studio via the Tools menu -> Scripts +option, which will bring up the scripting dialog. Scripts can be added, +removed, and reloaded in real time while the program is running. + +**NOTE:** On windows, currently only Python 3.6 is supported. To use +Python on windows, you must download and install Python 3.6.x "x86-64" +for OBS 64bit (64bit is the default), or Python 3.6.x "x86" if using OBS +32bit. Then, in the scripting dialog, you must set the path to the +Python 3.6.x install in the "Python Settings" tab. + +All API bindings are provided through the **obspython** module in +Python, and the **obslua** module in Lua. + +Certain functions have been changed/replaced in order to provide script +callbacks, see :ref:`other_script_differences` for more information. + +**WARNING:** Because bindings to the entire API are provided, it is +possible to leak memory or crash the program with an improperly-written +script. Please exercise caution when making scripts and check the +memory leak counter in your log file to make sure scripts you write +aren't leaking memory. **Please treat the API bindings as though you +were writing a C program: read the documentation for functions you use, +and release/destroy objects you reference or create via the API.** + + +Script Function Exports +----------------------- + +There are a number of global functions that scripts can optionally +provide: + +.. py:function:: script_load(settings) + + Called on script startup with specific settings associated with the + script. The *settings* parameter provided is not typically used for + settings that are set by the user; instead the parameter is used for + any extra internal settings data that may be used in the script. + + :param settings: Settings associated with the script. + +.. py:function:: script_unload() + + Called when the script is being unloaded. + +.. py:function:: script_save(settings) + + Called when the script is being saved. This is not necessary for + settings that are set by the user; instead this is used for any + extra internal settings data that may be used in the script. + + :param settings: Settings associated with the script. + +.. py:function:: script_defaults(settings) + + Called to set default settings (if any) associated with the script. + You would typically call :ref:`obs_data_default_funcs` for the + on the settings in order to set its default values. + + :param settings: Settings associated with the script. + +.. py:function:: script_update(settings) + + Called when the script's settings (if any) have been changed by the + user. + + :param settings: Settings associated with the script. + +.. py:function:: script_properties() + + Called to define user properties associated with the script. These + properties are used to define how to show settings properties to a + user. + + :return: obs_properties_t object created via + :c:func:`obs_properties_create()`. + +.. py:function:: script_tick(seconds) + + Called every frame in case per-frame processing is needed. If a + timer is needed, please use :ref:`scripting_timers` instead, as + timers are more efficient if all that's needed is basic timer + functionality. Using this function in Python is not recommended due + to the global interpreter lock of Python. + + :param seconds: Seconds passed since previous frame. + + +Getting the Current Script's Path +--------------------------------- + +There is a function you can use to get the current script's path. This +function is automatically implemented in to each script before the +script is loaded, and is part of the script's namespace, not +obslua/obspython: + +.. py:function:: script_path() + + :return: The path to the script. + + +.. _scripting_timers: + +Script Timers +------------- + +Script timers provide an efficient means of providing timer callbacks +without necessarily having to lock scripts/interpreters every frame. +(These functions are part of the obspython/obslua modules/namespaces). + +.. py:function:: timer_add(callback, milliseconds) + + Adds an timer callback which triggers every *millseconds*. + +.. py:function:: timer_remove(callback) + + Removes a timer callback. (Note: You can also use + :py:func:`remove_current_callback()` to terminate the timer from the + timer callback) + + +Script Sources (Lua Only) +------------------------- + +It is possible to register sources in Lua. To do so, create a table, +and define its keys the same way you would define an +:c:type:`obs_source_info` structure: + +.. code:: lua + + local info = {} + info.id = "my_source_id" + info.type = obslua.OBS_SOURCE_TYPE_INPUT + info.output_flags = obslua.OBS_SOURCE_VIDEO + + info.get_name = function() + return "My Source" + end + + info.create = function(settings, source) + -- typically source data would be stored as a table + local my_source_data = {} + + [...] + + return my_source_data + end + + info.video_render = function(my_source_data, effect) + [...] + end + + info.get_width = function(my_source_data) + [...] + + -- assuming the source data contains a 'width' key + return my_source_data.width + end + + info.get_height = function(my_source_data) + [...] + + -- assuming the source data contains a 'height' key + return my_source_data.height + end + + -- register the source + obs_register_source(info) + + +.. _other_script_differences: + +Other Differences From the C API +-------------------------------- + +Certain functions are implemented differently from the C API due to how +callbacks work. (These functions are part of the obspython/obslua +modules/namespaces). + +.. py:function:: obs_enum_sources() + + Enumerates all sources. + + :return: An array of reference-incremented sources. Release with + :py:func:`source_list_release()`. + +.. py:function:: obs_scene_enum_items(scene) + + Enumerates scene items within a scene. + + :param scene: obs_scene_t object to enumerate items from. + :return: List of scene items. Release with + :py:func:`sceneitem_list_release()`. + +.. py:function:: obs_add_main_render_callback(callback) + + **Lua only:** Adds a primary output render callback. This callback + has no parameters. + + :param callback: Render callback. Use + :py:func:`obs_remove_main_render_callback()` or + :py:func:`remove_current_callback()` to remove the + callback. + +.. py:function:: obs_remove_main_render_callback(callback) + + **Lua only:** Removes a primary output render callback. + + :param callback: Render callback. + +.. py:function:: signal_handler_connect(handler, signal, callback) + + Adds a callback to a specific signal on a signal handler. This + callback has one parameter: the calldata_t object. + + :param handler: A signal_handler_t object. + :param signal: The signal on the signal handler (string) + :param callback: The callback to connect to the signal. Use + :py:func:`signal_handler_disconnect()` or + :py:func:`remove_current_callback()` to remove the + callback. + +.. py:function:: signal_handler_disconnect(handler, signal, callback) + + Removes a callback from a specific signal of a signal handler. + + :param handler: A signal_handler_t object. + :param signal: The signal on the signal handler (string) + :param callback: The callback to disconnect from the signal. + +.. py:function:: signal_handler_connect_global(handler, callback) + + Adds a global callback to a signal handler. This callback has two + parameters: the first parameter is the signal string, and the second + parameter is the calldata_t object. + + :param handler: A signal_handler_t object. + :param callback: The callback to connect. Use + :py:func:`signal_handler_disconnect_global()` or + :py:func:`remove_current_callback()` to remove the + callback. + +.. py:function:: signal_handler_disconnect_global(handler, callback) + + Removes a global callback from a signal handler. + + :param handler: A signal_handler_t object. + :param callback: The callback to disconnect. + +.. py:function:: obs_hotkey_register_frontend(name, description, callback) + + Adds a frontend hotkey. The callback takes one parameter: a boolean + 'pressed' parameter. + + :param name: Unique name identifier string of the hotkey. + :param description: Hotkey description shown to the user. + :param callback: Callback for the hotkey. Use + :py:func:`obs_hotkey_unregister()` or + :py:func:`remove_current_callback()` to remove + the callback. + +.. py:function:: obs_hotkey_unregister(callback) + + Unregisters the hotkey associated with the specified callback. + + :param callback: Callback of the hotkey to unregister. + +.. py:function:: obs_properties_add_button(properties, setting_name, text, callback) + + Adds a button properties to an obs_properties_t object. The callback + takes no parameters. + + :param properties: An obs_properties_t object. + :param setting_name: A setting identifier string. + :param text: Button text. + :param callback: Button callback. This callback is automatically + cleaned up. + +.. py:function:: remove_current_callback() + + Removes the current callback being executed. Does nothing if not + within a callback. + +.. py:function:: source_list_release(source_list) + + Releases the references of a source list. + + :param source_list: Array of sources to release. + + +.. py:function:: sceneitem_list_release(item_list) + + Releases the references of a scene item list. + + :param item_list: Array of scene items to release. + +.. py:function:: calldata_source(calldata, name) + + Casts a pointer parameter of a calldata_t object to an obs_source_t + object. + + :param calldata: A calldata_t object. + :param name: Name of the parameter. + :return: A borrowed reference to an obs_source_t object. + +.. py:function:: calldata_sceneitem(calldata, name) + + Casts a pointer parameter of a calldata_t object to an + obs_sceneitem_t object. + + :param calldata: A calldata_t object. + :param name: Name of the parameter. + :return: A borrowed reference to an obs_sceneitem_t object. diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..2079b9a01 --- /dev/null +++ b/install.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +export QTDIR=/usr/local/Cellar/qt/5.11.1 +./CI/before-script-osx.sh +cd build && make -j 8 && sudo make install && cd .. +echo "OBS installed in /opt/obs" \ No newline at end of file diff --git a/libobs-d3d11/d3d11-shader.cpp b/libobs-d3d11/d3d11-shader.cpp index 453ff24ea..1a710486b 100644 --- a/libobs-d3d11/d3d11-shader.cpp +++ b/libobs-d3d11/d3d11-shader.cpp @@ -137,8 +137,11 @@ void gs_shader::BuildConstantBuffer() case GS_SHADER_PARAM_BOOL: case GS_SHADER_PARAM_INT: case GS_SHADER_PARAM_FLOAT: size = sizeof(float); break; + case GS_SHADER_PARAM_INT2: case GS_SHADER_PARAM_VEC2: size = sizeof(vec2); break; + case GS_SHADER_PARAM_INT3: case GS_SHADER_PARAM_VEC3: size = sizeof(float)*3; break; + case GS_SHADER_PARAM_INT4: case GS_SHADER_PARAM_VEC4: size = sizeof(vec4); break; case GS_SHADER_PARAM_MATRIX4X4: size = sizeof(float)*4*4; diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 41f5730f0..aa335aa65 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -166,6 +166,9 @@ gs_swap_chain::gs_swap_chain(gs_device *device, const gs_init_data *data) if (FAILED(hr)) throw HRError("Failed to create swap chain", hr); + /* Ignore Alt+Enter */ + device->factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); + Init(); } diff --git a/libobs-d3d11/d3d11-texture2d.cpp b/libobs-d3d11/d3d11-texture2d.cpp index 985546688..0fb25f720 100644 --- a/libobs-d3d11/d3d11-texture2d.cpp +++ b/libobs-d3d11/d3d11-texture2d.cpp @@ -123,10 +123,12 @@ void gs_texture_2d::InitResourceView() if (type == GS_TEXTURE_CUBE) { resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - resourceDesc.TextureCube.MipLevels = genMipmaps ? -1 : 1; + resourceDesc.TextureCube.MipLevels = + genMipmaps || !levels ? -1 : levels; } else { resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - resourceDesc.Texture2D.MipLevels = genMipmaps ? -1 : 1; + resourceDesc.Texture2D.MipLevels = + genMipmaps || !levels ? -1 : levels; } hr = device->device->CreateShaderResourceView(texture, &resourceDesc, diff --git a/libobs-opengl/gl-helpers.c b/libobs-opengl/gl-helpers.c index 8a7a75708..c6e9012e7 100644 --- a/libobs-opengl/gl-helpers.c +++ b/libobs-opengl/gl-helpers.c @@ -57,13 +57,11 @@ bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels, return success; } -static bool gl_copy_fbo(struct gs_device *device, - GLuint dst, GLenum dst_target, uint32_t dst_x, uint32_t dst_y, - GLuint src, GLenum src_target, uint32_t src_x, uint32_t src_y, - uint32_t width, uint32_t height, - enum gs_color_format format) +static bool gl_copy_fbo(struct gs_texture *dst, uint32_t dst_x, uint32_t dst_y, + struct gs_texture *src, uint32_t src_x, uint32_t src_y, + uint32_t width, uint32_t height) { - struct fbo_info *fbo = get_fbo(device, width, height, format); + struct fbo_info *fbo = get_fbo(src, width, height); GLint last_fbo; bool success = false; @@ -74,11 +72,11 @@ static bool gl_copy_fbo(struct gs_device *device, return false; if (!gl_bind_framebuffer(GL_READ_FRAMEBUFFER, fbo->fbo)) return false; - if (!gl_bind_texture(dst_target, dst)) + if (!gl_bind_texture(dst->gl_target, dst->texture)) goto fail; glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, - src_target, src, 0); + src->gl_target, src->texture, 0); if (!gl_success("glFrameBufferTexture2D")) goto fail; @@ -86,7 +84,7 @@ static bool gl_copy_fbo(struct gs_device *device, if (!gl_success("glReadBuffer")) goto fail; - glCopyTexSubImage2D(dst_target, 0, dst_x, dst_y, src_x, src_y, + glCopyTexSubImage2D(dst->gl_target, 0, dst_x, dst_y, src_x, src_y, width, height); if (!gl_success("glCopyTexSubImage2D")) goto fail; @@ -94,7 +92,7 @@ static bool gl_copy_fbo(struct gs_device *device, success = true; fail: - if (!gl_bind_texture(dst_target, 0)) + if (!gl_bind_texture(dst->gl_target, 0)) success = false; if (!gl_bind_framebuffer(GL_READ_FRAMEBUFFER, last_fbo)) success = false; @@ -102,29 +100,28 @@ static bool gl_copy_fbo(struct gs_device *device, return success; } -bool gl_copy_texture(struct gs_device *device, - GLuint dst, GLenum dst_target, uint32_t dst_x, uint32_t dst_y, - GLuint src, GLenum src_target, uint32_t src_x, uint32_t src_y, - uint32_t width, uint32_t height, enum gs_color_format format) +bool gl_copy_texture(struct gs_device *device, struct gs_texture *dst, + uint32_t dst_x, uint32_t dst_y, struct gs_texture *src, + uint32_t src_x, uint32_t src_y, uint32_t width, + uint32_t height) { bool success = false; if (device->copy_type == COPY_TYPE_ARB) { - glCopyImageSubData(src, src_target, 0, src_x, src_y, 0, - dst, dst_target, 0, dst_x, dst_y, 0, - width, height, 1); + glCopyImageSubData(src->texture, src->gl_target, 0, src_x, + src_y, 0, dst->texture, dst->gl_target, 0, + dst_x, dst_y, 0, width, height, 1); success = gl_success("glCopyImageSubData"); } else if (device->copy_type == COPY_TYPE_NV) { - glCopyImageSubDataNV(src, src_target, 0, src_x, src_y, 0, - dst, dst_target, 0, dst_x, dst_y, 0, - width, height, 1); + glCopyImageSubDataNV(src->texture, src->gl_target, 0, src_x, + src_y, 0, dst->texture, dst->gl_target, 0, + dst_x, dst_y, 0, width, height, 1); success = gl_success("glCopyImageSubDataNV"); } else if (device->copy_type == COPY_TYPE_FBO_BLIT) { - success = gl_copy_fbo(device, dst, dst_target, dst_x, dst_y, - src, src_target, src_x, src_y, - width, height, format); + success = gl_copy_fbo(dst, dst_x, dst_y, src, src_x, src_y, + width, height); if (!success) blog(LOG_ERROR, "gl_copy_texture failed"); } diff --git a/libobs-opengl/gl-helpers.h b/libobs-opengl/gl-helpers.h index f1f912215..fe9959a3e 100644 --- a/libobs-opengl/gl-helpers.h +++ b/libobs-opengl/gl-helpers.h @@ -148,11 +148,10 @@ extern bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels, uint32_t width, uint32_t height, uint32_t size, const uint8_t ***p_data); -extern bool gl_copy_texture(struct gs_device *device, - GLuint dst, GLenum dst_target, uint32_t dst_x, uint32_t dst_y, - GLuint src, GLenum src_target, uint32_t src_x, uint32_t src_y, - uint32_t width, uint32_t height, - enum gs_color_format format); +extern bool gl_copy_texture(struct gs_device *device, struct gs_texture *dst, + uint32_t dst_x, uint32_t dst_y, struct gs_texture *src, + uint32_t src_x, uint32_t src_y, uint32_t width, + uint32_t height); extern bool gl_create_buffer(GLenum target, GLuint *buffer, GLsizeiptr size, const GLvoid *data, GLenum usage); diff --git a/libobs-opengl/gl-shader.c b/libobs-opengl/gl-shader.c index 1ffcce424..e51783860 100644 --- a/libobs-opengl/gl-shader.c +++ b/libobs-opengl/gl-shader.c @@ -455,6 +455,24 @@ static void program_set_param_data(struct gs_program *program, gl_success("glUniform1iv"); } + } else if (pp->param->type == GS_SHADER_PARAM_INT2) { + if (validate_param(pp, sizeof(int) * 2)) { + glUniform2iv(pp->obj, 1, (int*)array); + gl_success("glUniform2iv"); + } + + } else if (pp->param->type == GS_SHADER_PARAM_INT3) { + if (validate_param(pp, sizeof(int) * 3)) { + glUniform3iv(pp->obj, 1, (int*)array); + gl_success("glUniform3iv"); + } + + } else if (pp->param->type == GS_SHADER_PARAM_INT4) { + if (validate_param(pp, sizeof(int) * 4)) { + glUniform4iv(pp->obj, 1, (int*)array); + gl_success("glUniform4iv"); + } + } else if (pp->param->type == GS_SHADER_PARAM_FLOAT) { if (validate_param(pp, sizeof(float))) { glUniform1fv(pp->obj, 1, (float*)array); @@ -698,6 +716,9 @@ void gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size) case GS_SHADER_PARAM_FLOAT: expected_size = sizeof(float); break; case GS_SHADER_PARAM_BOOL: case GS_SHADER_PARAM_INT: expected_size = sizeof(int); break; + case GS_SHADER_PARAM_INT2: expected_size = sizeof(int) * 2; break; + case GS_SHADER_PARAM_INT3: expected_size = sizeof(int) * 3; break; + case GS_SHADER_PARAM_INT4: expected_size = sizeof(int) * 4; break; case GS_SHADER_PARAM_VEC2: expected_size = sizeof(float)*2; break; case GS_SHADER_PARAM_VEC3: expected_size = sizeof(float)*3; break; case GS_SHADER_PARAM_VEC4: expected_size = sizeof(float)*4; break; diff --git a/libobs-opengl/gl-shaderparser.c b/libobs-opengl/gl-shaderparser.c index 61f19dc4c..2c607acf7 100644 --- a/libobs-opengl/gl-shaderparser.c +++ b/libobs-opengl/gl-shaderparser.c @@ -108,6 +108,10 @@ static void gl_write_var(struct gl_shader_parser *glsp, struct shader_var *var) dstr_cat(&glsp->gl_string, "uniform "); else if (var->var_type == SHADER_VAR_CONST) dstr_cat(&glsp->gl_string, "const "); + else if (var->var_type == SHADER_VAR_INOUT) + dstr_cat(&glsp->gl_string, "inout "); + else if (var->var_type == SHADER_VAR_OUT) + dstr_cat(&glsp->gl_string, "out "); gl_write_type(glsp, var->type); dstr_cat(&glsp->gl_string, " "); diff --git a/libobs-opengl/gl-stagesurf.c b/libobs-opengl/gl-stagesurf.c index aa1637cb4..35dd21258 100644 --- a/libobs-opengl/gl-stagesurf.c +++ b/libobs-opengl/gl-stagesurf.c @@ -124,7 +124,7 @@ void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, if (!gl_bind_buffer(GL_PIXEL_PACK_BUFFER, dst->pack_buffer)) goto failed; - fbo = get_fbo(device, dst->width, dst->height, dst->format); + fbo = get_fbo(src, dst->width, dst->height); if (!gl_get_integer_v(GL_READ_FRAMEBUFFER_BINDING, &last_fbo)) goto failed_unbind_buffer; @@ -152,6 +152,8 @@ void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, failed: if (!success) blog(LOG_ERROR, "device_stage_texture (GL) failed"); + + UNUSED_PARAMETER(device); } #else diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index 063e1fdb7..65b9e3e4c 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -259,16 +259,10 @@ int device_create(gs_device_t **p_device, uint32_t adapter) void device_destroy(gs_device_t *device) { if (device) { - size_t i; - - for (i = 0; i < device->fbos.num; i++) - fbo_info_destroy(device->fbos.array[i]); - while (device->first_program) gs_program_destroy(device->first_program); da_free(device->proj_stack); - da_free(device->fbos); gl_platform_destroy(device->plat); bfree(device); } @@ -658,46 +652,37 @@ static bool get_tex_dimensions(gs_texture_t *tex, uint32_t *width, * This automatically manages FBOs so that render targets are always given * an FBO that matches their width/height/format to maximize optimization */ -struct fbo_info *get_fbo(struct gs_device *device, - uint32_t width, uint32_t height, enum gs_color_format format) +struct fbo_info *get_fbo(gs_texture_t *tex, uint32_t width, uint32_t height) { - size_t i; - GLuint fbo; - struct fbo_info *ptr; - - for (i = 0; i < device->fbos.num; i++) { - ptr = device->fbos.array[i]; - - if (ptr->width == width && ptr->height == height && - ptr->format == format) - return ptr; - } + if (tex->fbo && tex->fbo->width == width && + tex->fbo->height == height && + tex->fbo->format == tex->format) + return tex->fbo; + GLuint fbo; glGenFramebuffers(1, &fbo); if (!gl_success("glGenFramebuffers")) return NULL; - ptr = bmalloc(sizeof(struct fbo_info)); - ptr->fbo = fbo; - ptr->width = width; - ptr->height = height; - ptr->format = format; - ptr->cur_render_target = NULL; - ptr->cur_render_side = 0; - ptr->cur_zstencil_buffer = NULL; + tex->fbo = bmalloc(sizeof(struct fbo_info)); + tex->fbo->fbo = fbo; + tex->fbo->width = width; + tex->fbo->height = height; + tex->fbo->format = tex->format; + tex->fbo->cur_render_target = NULL; + tex->fbo->cur_render_side = 0; + tex->fbo->cur_zstencil_buffer = NULL; - da_push_back(device->fbos, &ptr); - return ptr; + return tex->fbo; } -static inline struct fbo_info *get_fbo_by_tex(struct gs_device *device, - gs_texture_t *tex) +static inline struct fbo_info *get_fbo_by_tex(gs_texture_t *tex) { uint32_t width, height; if (!get_tex_dimensions(tex, &width, &height)) return NULL; - return get_fbo(device, width, height, tex->format); + return get_fbo(tex, width, height); } static bool set_current_fbo(gs_device_t *device, struct fbo_info *fbo) @@ -783,7 +768,7 @@ static bool set_target(gs_device_t *device, gs_texture_t *tex, int side, if (!tex) return set_current_fbo(device, NULL); - fbo = get_fbo_by_tex(device, tex); + fbo = get_fbo_by_tex(tex); if (!fbo) return false; @@ -885,9 +870,8 @@ void device_copy_texture_region(gs_device_t *device, goto fail; } - if (!gl_copy_texture(device, dst->texture, dst->gl_target, dst_x, dst_y, - src->texture, src->gl_target, src_x, src_y, - nw, nh, src->format)) + if (!gl_copy_texture(device, dst, dst_x, dst_y, src, src_x, src_y, nw, + nh)) goto fail; return; diff --git a/libobs-opengl/gl-subsystem.h b/libobs-opengl/gl-subsystem.h index 446787646..ba6cf98d6 100644 --- a/libobs-opengl/gl-subsystem.h +++ b/libobs-opengl/gl-subsystem.h @@ -409,6 +409,7 @@ struct gs_texture { bool gen_mipmaps; gs_samplerstate_t *cur_sampler; + struct fbo_info *fbo; }; struct gs_texture_2d { @@ -501,12 +502,11 @@ struct gs_device { DARRAY(struct matrix4) proj_stack; - DARRAY(struct fbo_info*) fbos; struct fbo_info *cur_fbo; }; -extern struct fbo_info *get_fbo(struct gs_device *device, - uint32_t width, uint32_t height, enum gs_color_format format); +extern struct fbo_info *get_fbo(gs_texture_t *tex, uint32_t width, + uint32_t height); extern void gl_update(gs_device_t *device); diff --git a/libobs-opengl/gl-texture2d.c b/libobs-opengl/gl-texture2d.c index 1ad6c922f..da18b9f5c 100644 --- a/libobs-opengl/gl-texture2d.c +++ b/libobs-opengl/gl-texture2d.c @@ -138,6 +138,9 @@ void gs_texture_destroy(gs_texture_t *tex) if (tex->texture) gl_delete_textures(1, &tex->texture); + if (tex->fbo) + fbo_info_destroy(tex->fbo); + bfree(tex); } diff --git a/libobs-opengl/gl-texturecube.c b/libobs-opengl/gl-texturecube.c index f15d6b201..6086675e4 100644 --- a/libobs-opengl/gl-texturecube.c +++ b/libobs-opengl/gl-texturecube.c @@ -92,10 +92,11 @@ void gs_cubetexture_destroy(gs_texture_t *tex) if (!tex) return; - if (tex->texture) { - glDeleteTextures(1, &tex->texture); - gl_success("glDeleteTextures"); - } + if (tex->texture) + gl_delete_textures(1, &tex->texture); + + if (tex->fbo) + fbo_info_destroy(tex->fbo); bfree(tex); } diff --git a/libobs-opengl/gl-x11.c b/libobs-opengl/gl-x11.c index 058db976c..697ef3806 100644 --- a/libobs-opengl/gl-x11.c +++ b/libobs-opengl/gl-x11.c @@ -59,6 +59,7 @@ static int ctx_visual_attribs[] = { GLX_STENCIL_SIZE, 0, GLX_DEPTH_SIZE, 0, GLX_BUFFER_SIZE, 32, + GLX_ALPHA_SIZE, 8, GLX_DOUBLEBUFFER, true, GLX_X_RENDERABLE, true, None @@ -322,10 +323,16 @@ static Display *open_windowless_display(void) static int x_error_handler(Display *display, XErrorEvent *error) { - char str[512]; - XGetErrorText(display, error->error_code, str, sizeof(str)); - - blog(LOG_ERROR, "X Error: %s", str); + char str1[512]; + char str2[512]; + char str3[512]; + XGetErrorText(display, error->error_code, str1, sizeof(str1)); + XGetErrorText(display, error->request_code, str2, sizeof(str2)); + XGetErrorText(display, error->minor_code, str3, sizeof(str3)); + + blog(LOG_ERROR, "X Error: %s, Major opcode: %s, " + "Minor opcode: %s, Serial: %lu", + str1, str2, str3, error->serial); return 0; } @@ -513,8 +520,10 @@ extern void gl_getclientsize(const struct gs_swap_chain *swap, xcb_window_t window = swap->wi->window; xcb_get_geometry_reply_t *geometry = get_window_geometry(xcb_conn, window); - *width = geometry->width; - *height = geometry->height; + if (geometry) { + *width = geometry->width; + *height = geometry->height; + } free(geometry); } diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 00c0ebceb..23ce82f5a 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -13,6 +13,13 @@ endif() if(UNIX) if (NOT APPLE) + find_package(X11_XCB REQUIRED) + find_package(XCB OPTIONAL_COMPONENTS XINPUT) + if (XCB_XINPUT_FOUND) + set(USE_XINPUT "1") + else() + set(USE_XINPUT "0") + endif() find_package(PulseAudio) if (NOT "${PULSEAUDIO_LIBRARY}" STREQUAL "") message(STATUS "Found PulseAudio - Audio Monitor enabled") @@ -22,14 +29,13 @@ if(UNIX) endif() else() set(HAVE_PULSEAUDIO "0") + set(USE_XINPUT "0") endif() find_package(DBus QUIET) - if (NOT APPLE) - find_package(X11_XCB REQUIRED) - endif() else() set(HAVE_DBUS "0") set(HAVE_PULSEAUDIO "0") + set(USE_XINPUT "0") endif() find_package(ImageMagick QUIET COMPONENTS MagickCore) @@ -110,7 +116,8 @@ elseif(APPLE) util/platform-nix.c util/platform-cocoa.m) set(libobs_PLATFORM_HEADERS - util/threading-posix.h) + util/threading-posix.h + util/apple/cfstring-utils.h) set(libobs_audio_monitoring_SOURCES audio-monitoring/osx/coreaudio-enum-devices.c audio-monitoring/osx/coreaudio-output.c @@ -199,6 +206,16 @@ elseif(UNIX) ${libobs_PLATFORM_DEPS} ${X11_XCB_LIBRARIES}) + if(USE_XINPUT) + include_directories( + ${XCB_XINPUT_INCLUDE_DIR}) + add_definitions( + ${XCB_DEFINITIONS}) + set(libobs_PLATFORM_DEPS + ${XCB_XINPUT_LIBRARY} + ${libobs_PLATFORM_DEPS}) + endif() + if(HAVE_PULSEAUDIO) set(libobs_PLATFORM_DEPS ${libobs_PLATFORM_DEPS} @@ -430,6 +447,14 @@ if(BUILD_CAPTIONS) endif() add_library(libobs SHARED ${libobs_SOURCES} ${libobs_HEADERS}) +if(UNIX AND NOT APPLE) + set(DEST_DIR "${CMAKE_INSTALL_PREFIX}") + foreach(LIB "obs" "rt") + set(PRIVATE_LIBS "${PRIVATE_LIBS} -l${LIB}") + endforeach() + CONFIGURE_FILE("libobs.pc.in" "libobs.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libobs.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") +endif() set_target_properties(libobs PROPERTIES OUTPUT_NAME obs diff --git a/libobs/audio-monitoring/osx/coreaudio-enum-devices.c b/libobs/audio-monitoring/osx/coreaudio-enum-devices.c index 52be3f462..8e6ea9e53 100644 --- a/libobs/audio-monitoring/osx/coreaudio-enum-devices.c +++ b/libobs/audio-monitoring/osx/coreaudio-enum-devices.c @@ -3,57 +3,52 @@ #include "../../obs-internal.h" #include "../../util/dstr.h" +#include "../../util/apple/cfstring-utils.h" #include "mac-helpers.h" -static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size) -{ - if (!ref) return false; - return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8); -} - static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, void *data, AudioDeviceID id, bool allow_inputs) { UInt32 size = 0; CFStringRef cf_name = NULL; CFStringRef cf_uid = NULL; - char name[1024]; - char uid[1024]; + char *name = NULL; + char *uid = NULL; OSStatus stat; bool cont = true; AudioObjectPropertyAddress addr = { kAudioDevicePropertyStreams, - kAudioDevicePropertyScopeInput, + kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster }; - /* check to see if it's a mac input device */ - if (!allow_inputs) { - AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size); - if (!size) - return true; - } + /* Check if the device is capable of audio output. */ + AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size); + if (!allow_inputs && !size) + return true; size = sizeof(CFStringRef); addr.mSelector = kAudioDevicePropertyDeviceUID; stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid); if (!success(stat, "get audio device UID")) - return true; + goto fail; addr.mSelector = kAudioDevicePropertyDeviceNameCFString; stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name); if (!success(stat, "get audio device name")) goto fail; - if (!cf_to_cstr(cf_name, name, sizeof(name))) { + name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8); + if (!name) { blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__); goto fail; } - if (!cf_to_cstr(cf_uid, uid, sizeof(uid))) { + uid = cfstr_copy_cstr(cf_uid, kCFStringEncodingUTF8); + if (!uid) { blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__); goto fail; } @@ -61,6 +56,8 @@ static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, cont = cb(data, name, uid); fail: + bfree(name); + bfree(uid); if (cf_name) CFRelease(cf_name); if (cf_uid) diff --git a/libobs/audio-monitoring/osx/coreaudio-output.c b/libobs/audio-monitoring/osx/coreaudio-output.c index 3e2bcd1cd..af2d7a063 100644 --- a/libobs/audio-monitoring/osx/coreaudio-output.c +++ b/libobs/audio-monitoring/osx/coreaudio-output.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -193,14 +193,14 @@ static bool audio_monitor_init(struct audio_monitor *monitor, } if (strcmp(uid, "default") != 0) { - CFStringRef cf_uid = CFStringCreateWithBytesNoCopy(NULL, + CFStringRef cf_uid = CFStringCreateWithBytes(NULL, (const UInt8*)uid, strlen(uid), kCFStringEncodingUTF8, - false, NULL); + false); stat = AudioQueueSetProperty(monitor->queue, kAudioQueueProperty_CurrentDevice, - cf_uid, sizeof(cf_uid)); + &cf_uid, sizeof(cf_uid)); CFRelease(cf_uid); if (!success(stat, "set current device")) { diff --git a/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c b/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c index fc41d7a2a..4bbbc9b8f 100644 --- a/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c +++ b/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c @@ -307,14 +307,15 @@ int_fast32_t pulseaudio_connect_playback(pa_stream *s, const char *name, return -1; size_t dev_len = strlen(name) - 8; - char device[dev_len]; + char *device = bzalloc(dev_len + 1); memcpy(device, name, dev_len); - device[dev_len] = '\0'; pulseaudio_lock(); int_fast32_t ret = pa_stream_connect_playback(s, device, attr, flags, NULL, NULL); pulseaudio_unlock(); + + bfree(device); return ret; } diff --git a/libobs/audio-monitoring/win32/wasapi-output.h b/libobs/audio-monitoring/win32/wasapi-output.h index 439fd544d..c3523b51d 100644 --- a/libobs/audio-monitoring/win32/wasapi-output.h +++ b/libobs/audio-monitoring/win32/wasapi-output.h @@ -3,10 +3,16 @@ #include +#ifndef KSAUDIO_SPEAKER_2POINT1 #define KSAUDIO_SPEAKER_2POINT1 (KSAUDIO_SPEAKER_STEREO|SPEAKER_LOW_FREQUENCY) +#endif + #define KSAUDIO_SPEAKER_SURROUND_AVUTIL \ (KSAUDIO_SPEAKER_STEREO|SPEAKER_FRONT_CENTER) + +#ifndef KSAUDIO_SPEAKER_4POINT1 #define KSAUDIO_SPEAKER_4POINT1 (KSAUDIO_SPEAKER_SURROUND|SPEAKER_LOW_FREQUENCY) +#endif #define safe_release(ptr) \ do { \ diff --git a/libobs/callback/signal.c b/libobs/callback/signal.c index 5b7c3ee00..d0f0a4da2 100644 --- a/libobs/callback/signal.c +++ b/libobs/callback/signal.c @@ -24,6 +24,7 @@ struct signal_callback { signal_callback_t callback; void *data; bool remove; + bool keep_ref; }; struct signal_info { @@ -96,6 +97,7 @@ struct global_callback_info { struct signal_handler { struct signal_info *first; pthread_mutex_t mutex; + volatile long refs; DARRAY(struct global_callback_info) global_callbacks; pthread_mutex_t global_callbacks_mutex; @@ -126,6 +128,7 @@ signal_handler_t *signal_handler_create(void) { struct signal_handler *handler = bzalloc(sizeof(struct signal_handler)); handler->first = NULL; + handler->refs = 1; pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr) != 0) @@ -149,20 +152,25 @@ signal_handler_t *signal_handler_create(void) return handler; } -void signal_handler_destroy(signal_handler_t *handler) +static void signal_handler_actually_destroy(signal_handler_t *handler) { - if (handler) { - struct signal_info *sig = handler->first; - while (sig != NULL) { - struct signal_info *next = sig->next; - signal_info_destroy(sig); - sig = next; - } + struct signal_info *sig = handler->first; + while (sig != NULL) { + struct signal_info *next = sig->next; + signal_info_destroy(sig); + sig = next; + } - da_free(handler->global_callbacks); - pthread_mutex_destroy(&handler->global_callbacks_mutex); - pthread_mutex_destroy(&handler->mutex); - bfree(handler); + da_free(handler->global_callbacks); + pthread_mutex_destroy(&handler->global_callbacks_mutex); + pthread_mutex_destroy(&handler->mutex); + bfree(handler); +} + +void signal_handler_destroy(signal_handler_t *handler) +{ + if (handler && os_atomic_dec_long(&handler->refs) == 0) { + signal_handler_actually_destroy(handler); } } @@ -197,11 +205,12 @@ bool signal_handler_add(signal_handler_t *handler, const char *signal_decl) return success; } -void signal_handler_connect(signal_handler_t *handler, const char *signal, - signal_callback_t callback, void *data) +static void signal_handler_connect_internal(signal_handler_t *handler, + const char *signal, signal_callback_t callback, void *data, + bool keep_ref) { struct signal_info *sig, *last; - struct signal_callback cb_data = {callback, data, false}; + struct signal_callback cb_data = {callback, data, false, keep_ref}; size_t idx; if (!handler) @@ -221,13 +230,28 @@ void signal_handler_connect(signal_handler_t *handler, const char *signal, pthread_mutex_lock(&sig->mutex); + if (keep_ref) + os_atomic_inc_long(&handler->refs); + idx = signal_get_callback_idx(sig, callback, data); - if (idx == DARRAY_INVALID) + if (keep_ref || idx == DARRAY_INVALID) da_push_back(sig->callbacks, &cb_data); pthread_mutex_unlock(&sig->mutex); } +void signal_handler_connect(signal_handler_t *handler, const char *signal, + signal_callback_t callback, void *data) +{ + signal_handler_connect_internal(handler, signal, callback, data, false); +} + +void signal_handler_connect_ref(signal_handler_t *handler, const char *signal, + signal_callback_t callback, void *data) +{ + signal_handler_connect_internal(handler, signal, callback, data, true); +} + static inline struct signal_info *getsignal_locked(signal_handler_t *handler, const char *name) { @@ -247,6 +271,7 @@ void signal_handler_disconnect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data) { struct signal_info *sig = getsignal_locked(handler, signal); + bool keep_ref = false; size_t idx; if (!sig) @@ -256,13 +281,19 @@ void signal_handler_disconnect(signal_handler_t *handler, const char *signal, idx = signal_get_callback_idx(sig, callback, data); if (idx != DARRAY_INVALID) { - if (sig->signalling) + if (sig->signalling) { sig->callbacks.array[idx].remove = true; - else + } else { + keep_ref = sig->callbacks.array[idx].keep_ref; da_erase(sig->callbacks, idx); + } } pthread_mutex_unlock(&sig->mutex); + + if (keep_ref && os_atomic_dec_long(&handler->refs) == 0) { + signal_handler_actually_destroy(handler); + } } static THREAD_LOCAL struct signal_callback *current_signal_cb = NULL; @@ -280,6 +311,7 @@ void signal_handler_signal(signal_handler_t *handler, const char *signal, calldata_t *params) { struct signal_info *sig = getsignal_locked(handler, signal); + long remove_refs = 0; if (!sig) return; @@ -298,8 +330,12 @@ void signal_handler_signal(signal_handler_t *handler, const char *signal, for (size_t i = sig->callbacks.num; i > 0; i--) { struct signal_callback *cb = sig->callbacks.array+i-1; - if (cb->remove) + if (cb->remove) { + if (cb->keep_ref) + remove_refs++; + da_erase(sig->callbacks, i-1); + } } sig->signalling = false; @@ -331,6 +367,12 @@ void signal_handler_signal(signal_handler_t *handler, const char *signal, } pthread_mutex_unlock(&handler->global_callbacks_mutex); + + if (remove_refs) { + os_atomic_set_long(&handler->refs, + os_atomic_load_long(&handler->refs) - + remove_refs); + } } void signal_handler_connect_global(signal_handler_t *handler, diff --git a/libobs/callback/signal.h b/libobs/callback/signal.h index ea4f0a90e..e9f86f707 100644 --- a/libobs/callback/signal.h +++ b/libobs/callback/signal.h @@ -58,6 +58,8 @@ static inline bool signal_handler_add_array(signal_handler_t *handler, EXPORT void signal_handler_connect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data); +EXPORT void signal_handler_connect_ref(signal_handler_t *handler, + const char *signal, signal_callback_t callback, void *data); EXPORT void signal_handler_disconnect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data); diff --git a/libobs/graphics/effect-parser.c b/libobs/graphics/effect-parser.c index a2a931bf1..84b46a564 100644 --- a/libobs/graphics/effect-parser.c +++ b/libobs/graphics/effect-parser.c @@ -21,6 +21,39 @@ #include "effect-parser.h" #include "effect.h" +static inline bool ep_parse_param_assign(struct effect_parser *ep, + struct ep_param *param); + +static enum gs_shader_param_type get_effect_param_type(const char *type) +{ + if (strcmp(type, "float") == 0) + return GS_SHADER_PARAM_FLOAT; + else if (strcmp(type, "float2") == 0) + return GS_SHADER_PARAM_VEC2; + else if (strcmp(type, "float3") == 0) + return GS_SHADER_PARAM_VEC3; + else if (strcmp(type, "float4") == 0) + return GS_SHADER_PARAM_VEC4; + else if (strcmp(type, "int2") == 0) + return GS_SHADER_PARAM_INT2; + else if (strcmp(type, "int3") == 0) + return GS_SHADER_PARAM_INT3; + else if (strcmp(type, "int4") == 0) + return GS_SHADER_PARAM_INT4; + else if (astrcmp_n(type, "texture", 7) == 0) + return GS_SHADER_PARAM_TEXTURE; + else if (strcmp(type, "float4x4") == 0) + return GS_SHADER_PARAM_MATRIX4X4; + else if (strcmp(type, "bool") == 0) + return GS_SHADER_PARAM_BOOL; + else if (strcmp(type, "int") == 0) + return GS_SHADER_PARAM_INT; + else if (strcmp(type, "string") == 0) + return GS_SHADER_PARAM_STRING; + + return GS_SHADER_PARAM_UNKNOWN; +} + void ep_free(struct effect_parser *ep) { size_t i; @@ -92,6 +125,18 @@ static inline struct ep_param *ep_getparam(struct effect_parser *ep, return NULL; } +static inline struct ep_param *ep_getannotation(struct ep_param *param, + const char *name) +{ + size_t i; + for (i = 0; i < param->annotations.num; i++) { + if (strcmp(name, param->annotations.array[i].name) == 0) + return param->annotations.array+i; + } + + return NULL; +} + static inline struct ep_func *ep_getfunc_strref(struct effect_parser *ep, const struct strref *ref) { @@ -262,6 +307,145 @@ static void ep_parse_struct(struct effect_parser *ep) ep_struct_free(&eps); } +static inline int ep_parse_param_annotation_var(struct effect_parser *ep, + struct ep_param *var) +{ + int code; + + /* -------------------------------------- */ + /* variable type */ + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, ";")) + return PARSE_CONTINUE; + if (cf_token_is(&ep->cfp, ">")) + return PARSE_BREAK; + + code = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, "type name", ";"); + if (code != PARSE_SUCCESS) + return code; + + bfree(var->type); + cf_copy_token(&ep->cfp, &var->type); + + /* -------------------------------------- */ + /* variable name */ + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, ";")) { + cf_adderror_expecting(&ep->cfp, "variable name"); + return PARSE_UNEXPECTED_CONTINUE; + } + if (cf_token_is(&ep->cfp, ">")) { + cf_adderror_expecting(&ep->cfp, "variable name"); + return PARSE_UNEXPECTED_BREAK; + } + + code = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, "variable name", ";"); + if (code != PARSE_SUCCESS) + return code; + + bfree(var->name); + cf_copy_token(&ep->cfp, &var->name); + + /* -------------------------------------- */ + /* variable mapping if any (POSITION, TEXCOORD, etc) */ + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, ":")) { + cf_adderror_expecting(&ep->cfp, "= or ;"); + return PARSE_UNEXPECTED_BREAK; + } else if (cf_token_is(&ep->cfp, ">")) { + cf_adderror_expecting(&ep->cfp, "= or ;"); + return PARSE_UNEXPECTED_BREAK; + } else if (cf_token_is(&ep->cfp, "=")) { + if (!ep_parse_param_assign(ep, var)) { + cf_adderror_expecting(&ep->cfp, "assignment value"); + return PARSE_UNEXPECTED_BREAK; + } + } + + /* -------------------------------------- */ + + if (!cf_token_is(&ep->cfp, ";")) { + if (!cf_go_to_valid_token(&ep->cfp, ";", ">")) { + cf_adderror_expecting(&ep->cfp, "; or >"); + return PARSE_EOF; + } + return PARSE_CONTINUE; + } + + return PARSE_SUCCESS; +} + +static int ep_parse_annotations(struct effect_parser *ep, + struct darray *annotations) +{ + if (!cf_token_is(&ep->cfp, "<")) { + cf_adderror_expecting(&ep->cfp, "<"); + goto error; + } + + /* get annotation variables */ + while (true) { + bool do_break = false; + struct ep_param var; + + ep_param_init(&var, bstrdup(""), bstrdup(""), false, false, + false); + + switch (ep_parse_param_annotation_var(ep, &var)) { + case PARSE_UNEXPECTED_CONTINUE: + cf_adderror_syntax_error(&ep->cfp); + /* Falls through. */ + case PARSE_CONTINUE: + ep_param_free(&var); + continue; + + case PARSE_UNEXPECTED_BREAK: + cf_adderror_syntax_error(&ep->cfp); + /* Falls through. */ + case PARSE_BREAK: + ep_param_free(&var); + do_break = true; + break; + + case PARSE_EOF: + ep_param_free(&var); + goto error; + } + + if (do_break) + break; + + darray_push_back(sizeof(struct ep_param), annotations, &var); + } + + if (!cf_token_is(&ep->cfp, ">")) { + cf_adderror_expecting(&ep->cfp, ">"); + goto error; + } + if (!cf_next_valid_token(&ep->cfp)) + goto error; + + return true; + +error: + return false; +} + +static int ep_parse_param_annotations(struct effect_parser *ep, + struct ep_param *param) +{ + return ep_parse_annotations(ep, ¶m->annotations.da); +} + static inline int ep_parse_pass_command_call(struct effect_parser *ep, struct darray *call) { @@ -328,7 +512,7 @@ static int ep_parse_pass(struct effect_parser *ep, struct ep_pass *pass) if (!cf_token_is(&ep->cfp, "{")) { pass->name = bstrdup_n(ep->cfp.cur_token->str.array, - ep->cfp.cur_token->str.len); + ep->cfp.cur_token->str.len); if (!cf_next_valid_token(&ep->cfp)) return PARSE_EOF; } @@ -356,9 +540,19 @@ static void ep_parse_technique(struct effect_parser *ep) if (cf_next_name(&ep->cfp, &ept.name, "name", ";") != PARSE_SUCCESS) goto error; - if (cf_next_token_should_be(&ep->cfp, "{", ";", NULL) != PARSE_SUCCESS) - goto error; + if (!cf_next_valid_token(&ep->cfp)) + return; + + if (!cf_token_is(&ep->cfp, "{")) { + if (!cf_go_to_token(&ep->cfp, ";", NULL)) { + cf_adderror_expecting(&ep->cfp, ";"); + return; + } + + cf_adderror_expecting(&ep->cfp, "{"); + goto error; + } if (!cf_next_valid_token(&ep->cfp)) goto error; @@ -491,13 +685,40 @@ static inline int ep_parse_func_param(struct effect_parser *ep, struct ep_func *func, struct ep_var *var) { int code; + bool var_type_keyword = false; if (!cf_next_valid_token(&ep->cfp)) return PARSE_EOF; - code = ep_check_for_keyword(ep, "uniform", &var->uniform); + code = ep_check_for_keyword(ep, "in", &var_type_keyword); if (code == PARSE_EOF) return PARSE_EOF; + else if (var_type_keyword) + var->var_type = EP_VAR_IN; + + if (!var_type_keyword) { + code = ep_check_for_keyword(ep, "inout", &var_type_keyword); + if (code == PARSE_EOF) + return PARSE_EOF; + else if (var_type_keyword) + var->var_type = EP_VAR_INOUT; + } + + if (!var_type_keyword) { + code = ep_check_for_keyword(ep, "out", &var_type_keyword); + if (code == PARSE_EOF) + return PARSE_EOF; + else if (var_type_keyword) + var->var_type = EP_VAR_OUT; + } + + if (!var_type_keyword) { + code = ep_check_for_keyword(ep, "uniform", &var_type_keyword); + if (code == PARSE_EOF) + return PARSE_EOF; + else if (var_type_keyword) + var->var_type = EP_VAR_UNIFORM; + } code = cf_get_name(&ep->cfp, &var->type, "type", ")"); if (code != PARSE_SUCCESS) @@ -729,6 +950,30 @@ static inline int ep_parse_param_assign_texture(struct effect_parser *ep, return PARSE_SUCCESS; } +static inline int ep_parse_param_assign_string(struct effect_parser *ep, + struct ep_param *param) +{ + int code; + char *str = NULL; + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + code = cf_token_is_type(&ep->cfp, CFTOKEN_STRING, "string", ";"); + if (code != PARSE_SUCCESS) + return code; + + str = cf_literal_to_str(ep->cfp.cur_token->str.array, + ep->cfp.cur_token->str.len); + + if (str) { + da_copy_array(param->default_val, str, strlen(str) + 1); + bfree(str); + } + + return PARSE_SUCCESS; +} + static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep, struct ep_param *param, bool is_float) { @@ -762,30 +1007,51 @@ static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep, return PARSE_SUCCESS; } -/* - * parses assignment for float1, float2, float3, float4, and any combination - * for float3x3, float4x4, etc - */ -static inline int ep_parse_param_assign_float_array(struct effect_parser *ep, +static inline int ep_parse_param_assign_bool(struct effect_parser *ep, struct ep_param *param) { - const char *float_type = param->type+5; - int float_count = 0, code, i; + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, "true")) { + long l = 1; + da_push_back_array(param->default_val, &l, sizeof(long)); + return PARSE_SUCCESS; + } else if (cf_token_is(&ep->cfp, "false")) { + long l = 0; + da_push_back_array(param->default_val, &l, sizeof(long)); + return PARSE_SUCCESS; + } + + cf_adderror_expecting(&ep->cfp, "true or false"); + + return PARSE_EOF; +} + +/* + * parses assignment for float1, float2, float3, float4, int1, int2, int3, int4, + * and any combination for float3x3, float4x4, int3x3, int4x4, etc +*/ +static inline int ep_parse_param_assign_intfloat_array(struct effect_parser *ep, + struct ep_param *param, bool is_float) +{ + const char *intfloat_type = param->type + (is_float ? 5 : 3); + int intfloat_count = 0, code, i; /* -------------------------------------------- */ - if (float_type[0] < '1' || float_type[0] > '4') + if (intfloat_type[0] < '1' || intfloat_type[0] > '4') cf_adderror(&ep->cfp, "Invalid row count", LEX_ERROR, NULL, NULL, NULL); - float_count = float_type[0]-'0'; + intfloat_count = intfloat_type[0]-'0'; - if (float_type[1] == 'x') { - if (float_type[2] < '1' || float_type[2] > '4') + if (intfloat_type[1] == 'x') { + if (intfloat_type[2] < '1' || intfloat_type[2] > '4') cf_adderror(&ep->cfp, "Invalid column count", LEX_ERROR, NULL, NULL, NULL); - float_count *= float_type[2]-'0'; + intfloat_count *= intfloat_type[2]-'0'; } /* -------------------------------------------- */ @@ -793,10 +1059,10 @@ static inline int ep_parse_param_assign_float_array(struct effect_parser *ep, code = cf_next_token_should_be(&ep->cfp, "{", ";", NULL); if (code != PARSE_SUCCESS) return code; - for (i = 0; i < float_count; i++) { - char *next = ((i+1) < float_count) ? "," : "}"; + for (i = 0; i < intfloat_count; i++) { + char *next = ((i+1) < intfloat_count) ? "," : "}"; - code = ep_parse_param_assign_intfloat(ep, param, true); + code = ep_parse_param_assign_intfloat(ep, param, is_float); if (code != PARSE_SUCCESS) return code; code = cf_next_token_should_be(&ep->cfp, next, ";", NULL); @@ -815,8 +1081,14 @@ static int ep_parse_param_assignment_val(struct effect_parser *ep, return ep_parse_param_assign_intfloat(ep, param, false); else if (strcmp(param->type, "float") == 0) return ep_parse_param_assign_intfloat(ep, param, true); + else if (astrcmp_n(param->type, "int", 3) == 0) + return ep_parse_param_assign_intfloat_array(ep, param, false); else if (astrcmp_n(param->type, "float", 5) == 0) - return ep_parse_param_assign_float_array(ep, param); + return ep_parse_param_assign_intfloat_array(ep, param, true); + else if (astrcmp_n(param->type, "string", 6) == 0) + return ep_parse_param_assign_string(ep, param); + else if (strcmp(param->type, "bool") == 0) + return ep_parse_param_assign_bool(ep, param); cf_adderror(&ep->cfp, "Invalid type '$1' used for assignment", LEX_ERROR, param->type, NULL, NULL); @@ -852,6 +1124,9 @@ static void ep_parse_param(struct effect_parser *ep, goto complete; if (cf_token_is(&ep->cfp, "[") && !ep_parse_param_array(ep, ¶m)) goto error; + if (cf_token_is(&ep->cfp, "<") && !ep_parse_param_annotations(ep, + ¶m)) + goto error; if (cf_token_is(&ep->cfp, "=") && !ep_parse_param_assign(ep, ¶m)) goto error; /* @@ -918,7 +1193,6 @@ static void ep_parse_other(struct effect_parser *ep) goto error; if (cf_next_name(&ep->cfp, &name, "name", ";") != PARSE_SUCCESS) goto error; - if (!cf_next_valid_token(&ep->cfp)) goto error; @@ -1083,8 +1357,14 @@ static inline void ep_write_func_sampler_deps(struct effect_parser *ep, static inline void ep_write_var(struct dstr *shader, struct ep_var *var) { - if (var->uniform) + if (var->var_type == EP_VAR_INOUT) + dstr_cat(shader, "inout "); + else if (var->var_type == EP_VAR_OUT) + dstr_cat(shader, "out "); + else if (var->var_type == EP_VAR_UNIFORM) dstr_cat(shader, "uniform "); + // The "in" input modifier is implied by default, so leave it blank + // in that case. dstr_cat(shader, var->type); dstr_cat(shader, " "); @@ -1306,6 +1586,35 @@ static void ep_makeshaderstring(struct effect_parser *ep, ep_reset_written(ep); } +static void ep_compile_annotations(struct darray *ep_annotations, + struct darray *gsp_annotations, struct effect_parser *ep) +{ + darray_resize(sizeof(struct gs_effect_param), + gsp_annotations, ep_annotations->num); + + size_t i; + for (i = 0; i < ep_annotations->num; i++) { + struct gs_effect_param *param = ((struct gs_effect_param *) + gsp_annotations->array)+i; + struct ep_param *param_in = ((struct ep_param *) + ep_annotations->array)+i; + + param->name = bstrdup(param_in->name); + param->section = EFFECT_ANNOTATION; + param->effect = ep->effect; + da_move(param->default_val, param_in->default_val); + + param->type = get_effect_param_type(param_in->type); + } +} + +static void ep_compile_param_annotations(struct ep_param *ep_param_input, + struct gs_effect_param *gs_effect_input, struct effect_parser *ep) +{ + ep_compile_annotations(&(ep_param_input->annotations.da), + &(gs_effect_input->annotations.da), ep); +} + static void ep_compile_param(struct effect_parser *ep, size_t idx) { struct gs_effect_param *param; @@ -1320,27 +1629,14 @@ static void ep_compile_param(struct effect_parser *ep, size_t idx) param->effect = ep->effect; da_move(param->default_val, param_in->default_val); - if (strcmp(param_in->type, "bool") == 0) - param->type = GS_SHADER_PARAM_BOOL; - else if (strcmp(param_in->type, "float") == 0) - param->type = GS_SHADER_PARAM_FLOAT; - else if (strcmp(param_in->type, "int") == 0) - param->type = GS_SHADER_PARAM_INT; - else if (strcmp(param_in->type, "float2") == 0) - param->type = GS_SHADER_PARAM_VEC2; - else if (strcmp(param_in->type, "float3") == 0) - param->type = GS_SHADER_PARAM_VEC3; - else if (strcmp(param_in->type, "float4") == 0) - param->type = GS_SHADER_PARAM_VEC4; - else if (strcmp(param_in->type, "float4x4") == 0) - param->type = GS_SHADER_PARAM_MATRIX4X4; - else if (param_in->is_texture) - param->type = GS_SHADER_PARAM_TEXTURE; + param->type = get_effect_param_type(param_in->type); if (strcmp(param_in->name, "ViewProj") == 0) ep->effect->view_proj = param; else if (strcmp(param_in->name, "World") == 0) ep->effect->world = param; + + ep_compile_param_annotations(param_in, param, ep); } static bool ep_compile_pass_shaderparams(struct effect_parser *ep, diff --git a/libobs/graphics/effect-parser.h b/libobs/graphics/effect-parser.h index 36b6669a2..128139917 100644 --- a/libobs/graphics/effect-parser.h +++ b/libobs/graphics/effect-parser.h @@ -20,6 +20,7 @@ #include "../util/darray.h" #include "../util/cf-parser.h" #include "graphics.h" +#include "shader-parser.h" #ifdef __cplusplus extern "C" { @@ -37,9 +38,17 @@ struct dstr; /* ------------------------------------------------------------------------- */ /* effect parser var data */ +enum ep_var_type { + EP_VAR_NONE, + EP_VAR_IN = EP_VAR_NONE, + EP_VAR_INOUT, + EP_VAR_OUT, + EP_VAR_UNIFORM +}; + struct ep_var { char *type, *name, *mapping; - bool uniform; + enum ep_var_type var_type; }; static inline void ep_var_init(struct ep_var *epv) @@ -64,6 +73,7 @@ struct ep_param { struct gs_effect_param *param; bool is_const, is_property, is_uniform, is_texture, written; int writeorder, array_count; + DARRAY(struct ep_param) annotations; }; extern void ep_param_writevar(struct dstr *dst, struct darray *use_params); @@ -83,6 +93,7 @@ static inline void ep_param_init(struct ep_param *epp, epp->array_count = 0; da_init(epp->default_val); da_init(epp->properties); + da_init(epp->annotations); } static inline void ep_param_free(struct ep_param *epp) @@ -91,6 +102,10 @@ static inline void ep_param_free(struct ep_param *epp) bfree(epp->name); da_free(epp->default_val); da_free(epp->properties); + + for (size_t i = 0; i < epp->annotations.num; i++) + ep_param_free(epp->annotations.array + i); + da_free(epp->annotations); } /* ------------------------------------------------------------------------- */ diff --git a/libobs/graphics/effect.c b/libobs/graphics/effect.c index 4b2a71660..ddd5d5a74 100644 --- a/libobs/graphics/effect.c +++ b/libobs/graphics/effect.c @@ -286,6 +286,63 @@ gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, return NULL; } +size_t gs_param_get_num_annotations(const gs_eparam_t *param) +{ + return param ? param->annotations.num : 0; +} + +gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, + size_t annotation) +{ + if (!param) return NULL; + + struct gs_effect_param *params = param->annotations.array; + if (annotation > param->annotations.num) + return NULL; + + return params + annotation; +} + +gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param, + const char *name) +{ + if (!param) return NULL; + struct gs_effect_param *params = param->annotations.array; + + for (size_t i = 0; i < param->annotations.num; i++) { + struct gs_effect_param *g_param = params + i; + if (strcmp(g_param->name, name) == 0) + return g_param; + } + return NULL; +} + +gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique, + size_t pass) +{ + if (!technique) return NULL; + struct gs_effect_pass *passes = technique->passes.array; + + if (pass > technique->passes.num) + return NULL; + + return passes + pass; +} + +gs_epass_t *gs_technique_get_pass_by_name(const gs_technique_t *technique, + const char *name) +{ + if (!technique) return NULL; + struct gs_effect_pass *passes = technique->passes.array; + + for (size_t i = 0; i < technique->passes.num; i++) { + struct gs_effect_pass *g_pass = passes + i; + if (strcmp(g_pass->name, name) == 0) + return g_pass; + } + return NULL; +} + gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect) { return effect ? effect->view_proj : NULL; @@ -332,6 +389,45 @@ static inline void effect_setval_inline(gs_eparam_t *param, } } +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +static inline void effect_getval_inline(gs_eparam_t *param, void *data, + size_t size) +{ + if (!param) { + blog(LOG_ERROR, "effect_getval_inline: invalid param"); + return; + } + + if (!data) { + blog(LOG_ERROR, "effect_getval_inline: invalid data"); + return; + } + + size_t bytes = min(size, param->cur_val.num); + + memcpy(data, param->cur_val.array, bytes); +} + +static inline void effect_getdefaultval_inline(gs_eparam_t *param, void *data, + size_t size) +{ + if (!param) { + blog(LOG_ERROR, "effect_getdefaultval_inline: invalid param"); + return; + } + + if (!data) { + blog(LOG_ERROR, "effect_getdefaultval_inline: invalid data"); + return; + } + + size_t bytes = min(size, param->default_val.num); + + memcpy(data, param->default_val.array, bytes); +} + void gs_effect_set_bool(gs_eparam_t *param, bool val) { int b_val = (int)val; @@ -385,6 +481,54 @@ void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size) effect_setval_inline(param, val, size); } +void *gs_effect_get_val(gs_eparam_t *param) +{ + if (!param) { + blog(LOG_ERROR, "gs_effect_get_val: invalid param"); + return NULL; + } + size_t size = param->cur_val.num; + void *data; + + if (size) + data = (void*)bzalloc(size); + else + return NULL; + + effect_getval_inline(param, data, size); + + return data; +} + +size_t gs_effect_get_val_size(gs_eparam_t *param) +{ + return param ? param->cur_val.num : 0; +} + +void *gs_effect_get_default_val(gs_eparam_t *param) +{ + if (!param) { + blog(LOG_ERROR, "gs_effect_get_default_val: invalid param"); + return NULL; + } + size_t size = param->default_val.num; + void *data; + + if (size) + data = (void*)bzalloc(size); + else + return NULL; + + effect_getdefaultval_inline(param, data, size); + + return data; +} + +size_t gs_effect_get_default_val_size(gs_eparam_t *param) +{ + return param ? param->default_val.num : 0; +} + void gs_effect_set_default(gs_eparam_t *param) { effect_setval_inline(param, param->default_val.array, diff --git a/libobs/graphics/effect.h b/libobs/graphics/effect.h index d76fb85e7..9a868689e 100644 --- a/libobs/graphics/effect.h +++ b/libobs/graphics/effect.h @@ -41,7 +41,8 @@ enum effect_section { EFFECT_PARAM, EFFECT_TECHNIQUE, EFFECT_SAMPLER, - EFFECT_PASS + EFFECT_PASS, + EFFECT_ANNOTATION }; /* ------------------------------------------------------------------------- */ @@ -61,11 +62,13 @@ struct gs_effect_param { /*char *full_name; float scroller_min, scroller_max, scroller_inc, scroller_mul;*/ + DARRAY(struct gs_effect_param) annotations; }; static inline void effect_param_init(struct gs_effect_param *param) { memset(param, 0, sizeof(struct gs_effect_param)); + da_init(param->annotations); } static inline void effect_param_free(struct gs_effect_param *param) @@ -74,6 +77,12 @@ static inline void effect_param_free(struct gs_effect_param *param) //bfree(param->full_name); da_free(param->cur_val); da_free(param->default_val); + + size_t i; + for (i = 0; i < param->annotations.num; i++) + effect_param_free(param->annotations.array + i); + + da_free(param->annotations); } EXPORT void effect_param_parse_property(gs_eparam_t *param, @@ -106,6 +115,7 @@ static inline void effect_pass_free(struct gs_effect_pass *pass) bfree(pass->name); da_free(pass->vertshader_params); da_free(pass->pixelshader_params); + gs_shader_destroy(pass->vertshader); gs_shader_destroy(pass->pixelshader); } @@ -130,6 +140,7 @@ static inline void effect_technique_free(struct gs_effect_technique *t) size_t i; for (i = 0; i < t->passes.num; i++) effect_pass_free(t->passes.array+i); + da_free(t->passes); bfree(t->name); } diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index df9583f0b..33c254af6 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -267,6 +267,7 @@ typedef struct gs_shader gs_shader_t; typedef struct gs_shader_param gs_sparam_t; typedef struct gs_effect gs_effect_t; typedef struct gs_effect_technique gs_technique_t; +typedef struct gs_effect_pass gs_epass_t; typedef struct gs_effect_param gs_eparam_t; typedef struct gs_device gs_device_t; typedef struct graphics_subsystem graphics_t; @@ -368,12 +369,21 @@ EXPORT bool gs_technique_begin_pass(gs_technique_t *technique, size_t pass); EXPORT bool gs_technique_begin_pass_by_name(gs_technique_t *technique, const char *name); EXPORT void gs_technique_end_pass(gs_technique_t *technique); +EXPORT gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique, + size_t pass); +EXPORT gs_epass_t *gs_technique_get_pass_by_name( + const gs_technique_t *technique, const char *name); EXPORT size_t gs_effect_get_num_params(const gs_effect_t *effect); EXPORT gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param); EXPORT gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, const char *name); +EXPORT size_t gs_param_get_num_annotations(const gs_eparam_t *param); +EXPORT gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, + size_t annotation); +EXPORT gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param, + const char *name); /** Helper function to simplify effect usage. Use with a while loop that * contains drawing functions. Automatically handles techniques, passes, and @@ -402,6 +412,10 @@ EXPORT void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val); EXPORT void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val); EXPORT void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size); EXPORT void gs_effect_set_default(gs_eparam_t *param); +EXPORT size_t gs_effect_get_val_size(gs_eparam_t *param); +EXPORT void *gs_effect_get_val(gs_eparam_t *param); +EXPORT size_t gs_effect_get_default_val_size(gs_eparam_t *param); +EXPORT void *gs_effect_get_default_val(gs_eparam_t *param); EXPORT void gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler); diff --git a/libobs/graphics/image-file.h b/libobs/graphics/image-file.h index 3720352b6..46fec44c1 100644 --- a/libobs/graphics/image-file.h +++ b/libobs/graphics/image-file.h @@ -20,6 +20,10 @@ #include "graphics.h" #include "libnsgif/libnsgif.h" +#ifdef __cplusplus +extern "C" { +#endif + struct gs_image_file { gs_texture_t *texture; enum gs_color_format format; @@ -51,3 +55,7 @@ EXPORT void gs_image_file_init_texture(gs_image_file_t *image); EXPORT bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns); EXPORT void gs_image_file_update_texture(gs_image_file_t *image); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/graphics/shader-parser.c b/libobs/graphics/shader-parser.c index 5b1733391..5464101d1 100644 --- a/libobs/graphics/shader-parser.c +++ b/libobs/graphics/shader-parser.c @@ -334,16 +334,40 @@ static inline int sp_parse_func_param(struct shader_parser *sp, struct shader_var *var) { int code; - bool is_uniform = false; + bool var_type_keyword = false; if (!cf_next_valid_token(&sp->cfp)) return PARSE_EOF; - code = sp_check_for_keyword(sp, "uniform", &is_uniform); + code = sp_check_for_keyword(sp, "in", &var_type_keyword); if (code == PARSE_EOF) return PARSE_EOF; + else if (var_type_keyword) + var->var_type = SHADER_VAR_IN; - var->var_type = is_uniform ? SHADER_VAR_UNIFORM : SHADER_VAR_NONE; + if (!var_type_keyword) { + code = sp_check_for_keyword(sp, "inout", &var_type_keyword); + if (code == PARSE_EOF) + return PARSE_EOF; + else if (var_type_keyword) + var->var_type = SHADER_VAR_INOUT; + } + + if (!var_type_keyword) { + code = sp_check_for_keyword(sp, "out", &var_type_keyword); + if (code == PARSE_EOF) + return PARSE_EOF; + else if (var_type_keyword) + var->var_type = SHADER_VAR_OUT; + } + + if (!var_type_keyword) { + code = sp_check_for_keyword(sp, "uniform", &var_type_keyword); + if (code == PARSE_EOF) + return PARSE_EOF; + else if (var_type_keyword) + var->var_type = SHADER_VAR_UNIFORM; + } code = cf_get_name(&sp->cfp, &var->type, "type", ")"); if (code != PARSE_SUCCESS) diff --git a/libobs/graphics/shader-parser.h b/libobs/graphics/shader-parser.h index 5e8f07c5d..8292904c1 100644 --- a/libobs/graphics/shader-parser.h +++ b/libobs/graphics/shader-parser.h @@ -38,6 +38,9 @@ EXPORT enum gs_address_mode get_address_mode(const char *address_mode); enum shader_var_type { SHADER_VAR_NONE, + SHADER_VAR_IN = SHADER_VAR_NONE, + SHADER_VAR_INOUT, + SHADER_VAR_OUT, SHADER_VAR_UNIFORM, SHADER_VAR_CONST }; diff --git a/libobs/libobs.pc.in b/libobs/libobs.pc.in new file mode 100644 index 000000000..03fe4cdd0 --- /dev/null +++ b/libobs/libobs.pc.in @@ -0,0 +1,11 @@ +prefix=@DEST_DIR@ +exec_prefix=${prefix} +libdir=${prefix}/@OBS_LIBRARY_DESTINATION@ +includedir=${prefix}/include + +Name: libobs +Description: OBS Studio Library +Version: @OBS_VERSION@ +Requires: x11 +Cflags: -I${includedir} +Libs: -L${libdir} @PRIVATE_LIBS@ diff --git a/libobs/media-io/media-remux.c b/libobs/media-io/media-remux.c index fc19bb5e7..9f5f72f5e 100644 --- a/libobs/media-io/media-remux.c +++ b/libobs/media-io/media-remux.c @@ -109,6 +109,8 @@ static inline bool init_output(media_remux_job_t job, const char *out_filename) } out_stream->time_base = out_stream->codec->time_base; + av_dict_copy(&out_stream->metadata, in_stream->metadata, 0); + out_stream->codec->codec_tag = 0; if (job->ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_H; @@ -141,6 +143,9 @@ bool media_remux_job_create(media_remux_job_t *job, const char *in_filename, if (!os_file_exists(in_filename)) return false; + if (strcmp(in_filename, out_filename) == 0) + return false; + *job = (media_remux_job_t)bzalloc(sizeof(struct media_remux_job)); if (!*job) return false; diff --git a/libobs/obs-audio-controls.c b/libobs/obs-audio-controls.c index 4b0c79b4b..0de00c7bd 100644 --- a/libobs/obs-audio-controls.c +++ b/libobs/obs-audio-controls.c @@ -16,6 +16,7 @@ along with this program. If not, see . */ #include +#include #include "util/threading.h" #include "util/bmem.h" @@ -62,18 +63,20 @@ struct meter_cb { }; struct obs_volmeter { - pthread_mutex_t mutex; - obs_source_t *source; - enum obs_fader_type type; - float cur_db; + pthread_mutex_t mutex; + obs_source_t *source; + enum obs_fader_type type; + float cur_db; - pthread_mutex_t callback_mutex; - DARRAY(struct meter_cb)callbacks; + pthread_mutex_t callback_mutex; + DARRAY(struct meter_cb) callbacks; - unsigned int update_ms; + enum obs_peak_meter_type peak_meter_type; + unsigned int update_ms; + float prev_samples[MAX_AUDIO_CHANNELS][4]; - float vol_magnitude[MAX_AUDIO_CHANNELS]; - float vol_peak[MAX_AUDIO_CHANNELS]; + float magnitude[MAX_AUDIO_CHANNELS]; + float peak[MAX_AUDIO_CHANNELS]; }; static float cubic_def_to_db(const float def) @@ -256,48 +259,257 @@ static void volmeter_source_destroyed(void *vptr, calldata_t *calldata) obs_volmeter_detach_source(volmeter); } -static void volmeter_process_audio_data(obs_volmeter_t *volmeter, - const struct audio_data *data) +static int get_nr_channels_from_audio_data(const struct audio_data *data) +{ + int nr_channels = 0; + for (int i = 0; i < MAX_AV_PLANES; i++) { + if (data->data[i]) + nr_channels++; + } + return CLAMP(nr_channels, 0, MAX_AUDIO_CHANNELS); +} + +/* msb(h, g, f, e) lsb(d, c, b, a) --> msb(h, h, g, f) lsb(e, d, c, b) + */ +#define SHIFT_RIGHT_2PS(msb, lsb) {\ + __m128 tmp = _mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3));\ + lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1));\ + msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1));\ +} + +/* x(d, c, b, a) --> (|d|, |c|, |b|, |a|) + */ +#define abs_ps(v) \ + _mm_andnot_ps(_mm_set1_ps(-0.f), v) + +/* Take cross product of a vector with a matrix resulting in vector. + */ +#define VECTOR_MATRIX_CROSS_PS(out, v, m0, m1, m2, m3) \ +{\ + out = _mm_mul_ps(v, m0);\ + __m128 mul1 = _mm_mul_ps(v, m1);\ + __m128 mul2 = _mm_mul_ps(v, m2);\ + __m128 mul3 = _mm_mul_ps(v, m3);\ +\ + _MM_TRANSPOSE4_PS(out, mul1, mul2, mul3);\ +\ + out = _mm_add_ps(out, mul1);\ + out = _mm_add_ps(out, mul2);\ + out = _mm_add_ps(out, mul3);\ +} + +/* x4(d, c, b, a) --> max(a, b, c, d) + */ +#define hmax_ps(r, x4) \ + do { \ + float x4_mem[4]; \ + _mm_storeu_ps(x4_mem, x4); \ + r = x4_mem[0]; \ + r = fmaxf(r, x4_mem[1]); \ + r = fmaxf(r, x4_mem[2]); \ + r = fmaxf(r, x4_mem[3]); \ + } while (false) + +/* Calculate the true peak over a set of samples. + * The algorithm implements 5x oversampling by using Whittaker–Shannon + * interpolation over four samples. + * + * The four samples have location t=-1.5, -0.5, +0.5, +1.5 + * The oversamples are taken at locations t=-0.3, -0.1, +0.1, +0.3 + * + * @param previous_samples Last 4 samples from the previous iteration. + * @param samples The samples to find the peak in. + * @param nr_samples Number of sets of 4 samples. + * @returns 5 times oversampled true-peak from the set of samples. + */ +static float get_true_peak(__m128 previous_samples, const float *samples, + size_t nr_samples) +{ + /* These are normalized-sinc parameters for interpolating over sample + * points which are located at x-coords: -1.5, -0.5, +0.5, +1.5. + * And oversample points at x-coords: -0.3, -0.1, 0.1, 0.3. */ + const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f); + const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f); + const __m128 p1 = _mm_set_ps(-0.189207f, 0.504551f, 0.756827f, -0.216236f); + const __m128 p3 = _mm_set_ps(-0.103943f, 0.233872f, 0.935489f, -0.155915f); + + __m128 work = previous_samples; + __m128 peak = previous_samples; + for (size_t i = 0; (i + 3) < nr_samples; i += 4) { + __m128 new_work = _mm_load_ps(&samples[i]); + __m128 intrp_samples; + + /* Include the actual sample values in the peak. */ + __m128 abs_new_work = abs_ps(new_work); + peak = _mm_max_ps(peak, abs_new_work); + + /* Shift in the next point. */ + SHIFT_RIGHT_2PS(new_work, work); + VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); + peak = _mm_max_ps(peak, abs_ps(intrp_samples)); + + SHIFT_RIGHT_2PS(new_work, work); + VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); + peak = _mm_max_ps(peak, abs_ps(intrp_samples)); + + SHIFT_RIGHT_2PS(new_work, work); + VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); + peak = _mm_max_ps(peak, abs_ps(intrp_samples)); + + SHIFT_RIGHT_2PS(new_work, work); + VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); + peak = _mm_max_ps(peak, abs_ps(intrp_samples)); + } + + float r; + hmax_ps(r, peak); + return r; +} + +/* points contain the first four samples to calculate the sinc interpolation + * over. They will have come from a previous iteration. + */ +static float get_sample_peak(__m128 previous_samples, const float *samples, + size_t nr_samples) +{ + __m128 peak = previous_samples; + for (size_t i = 0; (i + 3) < nr_samples; i += 4) { + __m128 new_work = _mm_load_ps(&samples[i]); + peak = _mm_max_ps(peak, abs_ps(new_work)); + } + + float r; + hmax_ps(r, peak); + return r; +} + +static void volmeter_process_peak_last_samples(obs_volmeter_t *volmeter, + int channel_nr, float *samples, size_t nr_samples) +{ + /* Take the last 4 samples that need to be used for the next peak + * calculation. If there are less than 4 samples in total the new + * samples shift out the old samples. */ + + switch (nr_samples) { + case 0: + break; + case 1: + volmeter->prev_samples[channel_nr][0] = + volmeter->prev_samples[channel_nr][1]; + volmeter->prev_samples[channel_nr][1] = + volmeter->prev_samples[channel_nr][2]; + volmeter->prev_samples[channel_nr][2] = + volmeter->prev_samples[channel_nr][3]; + volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1]; + break; + case 2: + volmeter->prev_samples[channel_nr][0] = + volmeter->prev_samples[channel_nr][2]; + volmeter->prev_samples[channel_nr][1] = + volmeter->prev_samples[channel_nr][3]; + volmeter->prev_samples[channel_nr][2] = samples[nr_samples-2]; + volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1]; + break; + case 3: + volmeter->prev_samples[channel_nr][0] = + volmeter->prev_samples[channel_nr][3]; + volmeter->prev_samples[channel_nr][1] = samples[nr_samples-3]; + volmeter->prev_samples[channel_nr][2] = samples[nr_samples-2]; + volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1]; + break; + default: + volmeter->prev_samples[channel_nr][0] = samples[nr_samples-4]; + volmeter->prev_samples[channel_nr][1] = samples[nr_samples-3]; + volmeter->prev_samples[channel_nr][2] = samples[nr_samples-2]; + volmeter->prev_samples[channel_nr][3] = samples[nr_samples-1]; + } +} + +static void volmeter_process_peak(obs_volmeter_t *volmeter, + const struct audio_data *data, int nr_channels) { int nr_samples = data->frames; int channel_nr = 0; - - for (size_t plane_nr = 0; plane_nr < MAX_AV_PLANES; plane_nr++) { + for (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) { float *samples = (float *)data->data[plane_nr]; if (!samples) { - // This plane does not contain data. continue; } + if (((uintptr_t)samples & 0xf) > 0) { + printf("Audio plane %i is not aligned %p skipping " + "peak volume measurement.\n", + plane_nr, samples); + volmeter->peak[channel_nr] = 1.0; + channel_nr++; + continue; + } + + /* volmeter->prev_samples may not be aligned to 16 bytes; + * use unaligned load. */ + __m128 previous_samples = _mm_loadu_ps( + volmeter->prev_samples[channel_nr]); + + float peak; + switch (volmeter->peak_meter_type) { + case TRUE_PEAK_METER: + peak = get_true_peak(previous_samples, samples, + nr_samples); + break; + + case SAMPLE_PEAK_METER: + default: + peak = get_sample_peak(previous_samples, samples, + nr_samples); + break; - // For each plane calculate: - // * peak = the maximum-absolute of the sample values. - // * magnitude = root-mean-square of the sample values. - // A VU meter needs to integrate over 300ms, but this will - // be handled by the ballistics of the meter itself, - // reality. Which makes this calculation independent of - // sample rate or update rate. - float peak = 0.0; - float sum_of_squares = 0.0; - for (int sample_nr = 0; sample_nr < nr_samples; sample_nr++) { - float sample = samples[sample_nr]; - - peak = fmaxf(peak, fabsf(sample)); - sum_of_squares += (sample * sample); } - volmeter->vol_magnitude[channel_nr] = sqrtf(sum_of_squares / + volmeter_process_peak_last_samples(volmeter, channel_nr, samples, nr_samples); - volmeter->vol_peak[channel_nr] = peak; + + volmeter->peak[channel_nr] = peak; + channel_nr++; } - // Clear audio channels that are not in use. + /* Clear the peak of the channels that have not been handled. */ for (; channel_nr < MAX_AUDIO_CHANNELS; channel_nr++) { - volmeter->vol_magnitude[channel_nr] = 0.0; - volmeter->vol_peak[channel_nr] = 0.0; + volmeter->peak[channel_nr] = 0.0; } } +static void volmeter_process_magnitude(obs_volmeter_t *volmeter, + const struct audio_data *data, int nr_channels) +{ + size_t nr_samples = data->frames; + + int channel_nr = 0; + for (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) { + float *samples = (float *)data->data[plane_nr]; + if (!samples) { + continue; + } + + float sum = 0.0; + for (size_t i = 0; i < nr_samples; i++) { + float sample = samples[i]; + sum += sample * sample; + } + volmeter->magnitude[channel_nr] = sqrtf(sum / nr_samples); + + channel_nr++; + } +} + +static void volmeter_process_audio_data(obs_volmeter_t *volmeter, + const struct audio_data *data) +{ + int nr_channels = get_nr_channels_from_audio_data(data); + + volmeter_process_peak(volmeter, data, nr_channels); + volmeter_process_magnitude(volmeter, data, nr_channels); +} + static void volmeter_source_data_received(void *vptr, obs_source_t *source, const struct audio_data *data, bool muted) { @@ -317,16 +529,16 @@ static void volmeter_source_data_received(void *vptr, obs_source_t *source, for (int channel_nr = 0; channel_nr < MAX_AUDIO_CHANNELS; channel_nr++) { magnitude[channel_nr] = mul_to_db( - volmeter->vol_magnitude[channel_nr] * mul); + volmeter->magnitude[channel_nr] * mul); peak[channel_nr] = mul_to_db( - volmeter->vol_peak[channel_nr] * mul); + volmeter->peak[channel_nr] * mul); + + /* The input-peak is NOT adjusted with volume, so that the user + * can check the input-gain. */ input_peak[channel_nr] = mul_to_db( - volmeter->vol_peak[channel_nr]); + volmeter->peak[channel_nr]); } - // The input-peak is NOT adjusted with volume, so that the user - // can check the input-gain. - pthread_mutex_unlock(&volmeter->mutex); signal_levels_updated(volmeter, magnitude, peak, input_peak); @@ -641,6 +853,14 @@ void obs_volmeter_detach_source(obs_volmeter_t *volmeter) volmeter_source_data_received, volmeter); } +void obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter, + enum obs_peak_meter_type peak_meter_type) +{ + pthread_mutex_lock(&volmeter->mutex); + volmeter->peak_meter_type = peak_meter_type; + pthread_mutex_unlock(&volmeter->mutex); +} + void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter, const unsigned int ms) { diff --git a/libobs/obs-audio-controls.h b/libobs/obs-audio-controls.h index 158af7bf7..384d61872 100644 --- a/libobs/obs-audio-controls.h +++ b/libobs/obs-audio-controls.h @@ -68,6 +68,27 @@ enum obs_fader_type { OBS_FADER_LOG }; +/** + * @brief Peak meter types + */ +enum obs_peak_meter_type { + /** + * @brief A simple peak meter measuring the maximum of all samples. + * + * This was a very common type of peak meter used for audio, but + * is not very accurate with regards to further audio processing. + */ + SAMPLE_PEAK_METER, + + /** + * @brief An accurate peak meter measure the maximum of inter-samples. + * + * This meter is more computational intensive due to 4x oversampling + * to determine the true peak to an accuracy of +/- 0.5 dB. + */ + TRUE_PEAK_METER +}; + /** * @brief Create a fader * @param type the type of the fader @@ -200,6 +221,14 @@ EXPORT bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, */ EXPORT void obs_volmeter_detach_source(obs_volmeter_t *volmeter); +/** + * @brief Set the peak meter type for the volume meter + * @param volmeter pointer to the volume meter object + * @param peak_meter_type set if true-peak needs to be measured. + */ +EXPORT void obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter, + enum obs_peak_meter_type peak_meter_type); + /** * @brief Set the update interval for the volume meter * @param volmeter pointer to the volume meter object diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 066f02813..b6d8dc812 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -27,7 +27,7 @@ /* * Increment if major breaking API changes */ -#define LIBOBS_API_MAJOR_VER 21 +#define LIBOBS_API_MAJOR_VER 22 /* * Increment if backward-compatible additions @@ -61,6 +61,11 @@ # define OBS_INSTALL_PREFIX "" # define OBS_PLUGIN_DESTINATION "obs-plugins" # define OBS_RELATIVE_PREFIX "../../" +# define OBS_RELEASE_CANDIDATE_MAJOR 0 +# define OBS_RELEASE_CANDIDATE_MINOR 0 +# define OBS_RELEASE_CANDIDATE_PATCH 0 +# define OBS_RELEASE_CANDIDATE_VER 0 +# define OBS_RELEASE_CANDIDATE 0 #endif #define OBS_INSTALL_DATA_PATH OBS_INSTALL_PREFIX OBS_DATA_PATH diff --git a/libobs/obs-data.c b/libobs/obs-data.c index 870de336c..676431dc5 100644 --- a/libobs/obs-data.c +++ b/libobs/obs-data.c @@ -1319,6 +1319,19 @@ void obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj) da_insert(array->objects, idx, &obj); } +void obs_data_array_push_back_array(obs_data_array_t *array, + obs_data_array_t *array2) +{ + if (!array || !array2) + return; + + for (size_t i = 0; i < array2->objects.num; i++) { + obs_data_t *obj = array2->objects.array[i]; + obs_data_addref(obj); + } + da_push_back_da(array->objects, array2->objects); +} + void obs_data_array_erase(obs_data_array_t *array, size_t idx) { if (array) { diff --git a/libobs/obs-data.h b/libobs/obs-data.h index 824d23a95..c0bbcbe22 100644 --- a/libobs/obs-data.h +++ b/libobs/obs-data.h @@ -159,6 +159,8 @@ EXPORT obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx); EXPORT size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj); EXPORT void obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj); +EXPORT void obs_data_array_push_back_array(obs_data_array_t *array, + obs_data_array_t *array2); EXPORT void obs_data_array_erase(obs_data_array_t *array, size_t idx); /* ------------------------------------------------------------------------- */ diff --git a/libobs/obs-display.c b/libobs/obs-display.c index f5d01abc6..5bc09d8db 100644 --- a/libobs/obs-display.c +++ b/libobs/obs-display.c @@ -46,17 +46,22 @@ bool obs_display_init(struct obs_display *display, return false; } - display->background_color = 0x4C4C4C; display->enabled = true; return true; } -obs_display_t *obs_display_create(const struct gs_init_data *graphics_data) +obs_display_t *obs_display_create(const struct gs_init_data *graphics_data, + uint32_t background_color) { struct obs_display *display = bzalloc(sizeof(struct obs_display)); gs_enter_context(obs->video.graphics); + if (background_color) + display->background_color = background_color; + else + display->background_color = 0x4c4c4c; + if (!obs_display_init(display, graphics_data)) { obs_display_destroy(display); display = NULL; @@ -227,6 +232,10 @@ bool obs_display_enabled(obs_display_t *display) void obs_display_set_background_color(obs_display_t *display, uint32_t color) { - if (display) - display->background_color = color; + if (display) { + if (color) + display->background_color = color; + else + display->background_color = 0x4c4c4c; + } } diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index e4d1320ad..83b2d7f8c 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -189,8 +189,7 @@ static void add_connection(struct obs_encoder *encoder) struct video_scale_info info = {0}; get_video_info(encoder, &info); - video_output_connect(encoder->media, &info, receive_video, - encoder); + start_raw_video(encoder->media, &info, receive_video, encoder); } set_encoder_active(encoder, true); @@ -202,8 +201,7 @@ static void remove_connection(struct obs_encoder *encoder) audio_output_disconnect(encoder->media, encoder->mixer_idx, receive_audio, encoder); else - video_output_disconnect(encoder->media, receive_video, - encoder); + stop_raw_video(encoder->media, receive_video, encoder); obs_encoder_shutdown(encoder); set_encoder_active(encoder, false); @@ -295,6 +293,14 @@ obs_data_t *obs_encoder_defaults(const char *id) return (info) ? get_defaults(info) : NULL; } +obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder) +{ + if (!obs_encoder_valid(encoder, "obs_encoder_defaults")) + return NULL; + + return get_defaults(&encoder->info); +} + obs_properties_t *obs_get_encoder_properties(const char *id) { const struct obs_encoder_info *ei = find_encoder(id); diff --git a/libobs/obs-hotkey.c b/libobs/obs-hotkey.c index 7d41cff2e..b150d2975 100644 --- a/libobs/obs-hotkey.c +++ b/libobs/obs-hotkey.c @@ -79,6 +79,62 @@ obs_hotkey_t *obs_hotkey_binding_get_hotkey(obs_hotkey_binding_t *binding) return binding->hotkey; } +static inline bool find_id(obs_hotkey_id id, size_t *idx); +void obs_hotkey_set_name(obs_hotkey_id id, const char *name) +{ + size_t idx; + + if (!find_id(id, &idx)) + return; + + obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx]; + bfree(hotkey->name); + hotkey->name = bstrdup(name); +} + +void obs_hotkey_set_description(obs_hotkey_id id, const char *desc) +{ + size_t idx; + + if (!find_id(id, &idx)) + return; + + obs_hotkey_t *hotkey = &obs->hotkeys.hotkeys.array[idx]; + bfree(hotkey->description); + hotkey->description = bstrdup(desc); +} + +static inline bool find_pair_id(obs_hotkey_pair_id id, size_t *idx); +void obs_hotkey_pair_set_names(obs_hotkey_pair_id id, + const char *name0, const char *name1) +{ + size_t idx; + obs_hotkey_pair_t pair; + + if (!find_pair_id(id, &idx)) + return; + + pair = obs->hotkeys.hotkey_pairs.array[idx]; + + obs_hotkey_set_name(pair.id[0], name0); + obs_hotkey_set_name(pair.id[1], name1); +} + +void obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id, + const char *desc0, const char *desc1) +{ + size_t idx; + obs_hotkey_pair_t pair; + + if (!find_pair_id(id, &idx)) + return; + + pair = obs->hotkeys.hotkey_pairs.array[idx]; + + obs_hotkey_set_description(pair.id[0], desc0); + obs_hotkey_set_description(pair.id[1], desc1); +} + static void hotkey_signal(const char *signal, obs_hotkey_t *hotkey) { calldata_t data; @@ -804,6 +860,30 @@ obs_data_array_t *obs_hotkey_save(obs_hotkey_id id) return result; } +void obs_hotkey_pair_save(obs_hotkey_pair_id id, + obs_data_array_t **p_data0, + obs_data_array_t **p_data1) +{ + if ((!p_data0 && !p_data1) || !lock()) + return; + + size_t idx; + if (!find_pair_id(id, &idx)) + goto unlock; + + obs_hotkey_pair_t *pair = &obs->hotkeys.hotkey_pairs.array[idx]; + + if (p_data0 && find_id(pair->id[0], &idx)) { + *p_data0 = save_hotkey(&obs->hotkeys.hotkeys.array[idx]); + } + if (p_data1 && find_id(pair->id[1], &idx)) { + *p_data1 = save_hotkey(&obs->hotkeys.hotkeys.array[idx]); + } + +unlock: + unlock(); +} + static inline bool enum_save_hotkey(void *data, size_t idx, obs_hotkey_t *hotkey) { diff --git a/libobs/obs-hotkey.h b/libobs/obs-hotkey.h index 0385d6550..4e4a86ab6 100644 --- a/libobs/obs-hotkey.h +++ b/libobs/obs-hotkey.h @@ -32,6 +32,8 @@ const size_t OBS_INVALID_HOTKEY_ID = (size_t)-1; const size_t OBS_INVALID_HOTKEY_PAIR_ID = (size_t)-1; #endif +#define XINPUT_MOUSE_LEN 33 + enum obs_key { #define OBS_HOTKEY(x) x, #include "obs-hotkeys.h" @@ -58,6 +60,8 @@ enum obs_hotkey_registerer_type { }; typedef enum obs_hotkey_registerer_type obs_hotkey_registerer_t; +/* getter functions */ + EXPORT obs_hotkey_id obs_hotkey_get_id(const obs_hotkey_t *key); EXPORT const char *obs_hotkey_get_name(const obs_hotkey_t *key); EXPORT const char *obs_hotkey_get_description(const obs_hotkey_t *key); @@ -74,6 +78,15 @@ EXPORT obs_hotkey_id obs_hotkey_binding_get_hotkey_id( EXPORT obs_hotkey_t *obs_hotkey_binding_get_hotkey( obs_hotkey_binding_t *binding); +/* setter functions */ + +EXPORT void obs_hotkey_set_name(obs_hotkey_id id, const char *name); +EXPORT void obs_hotkey_set_description(obs_hotkey_id id, const char *desc); +EXPORT void obs_hotkey_pair_set_names(obs_hotkey_pair_id id, + const char *name0, const char *name1); +EXPORT void obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id, + const char *desc0, const char *desc1); + #ifndef SWIG struct obs_hotkeys_translations { const char *insert; @@ -223,6 +236,10 @@ EXPORT void obs_hotkey_pair_load(obs_hotkey_pair_id id, obs_data_array_t *data0, EXPORT obs_data_array_t *obs_hotkey_save(obs_hotkey_id id); +EXPORT void obs_hotkey_pair_save(obs_hotkey_pair_id id, + obs_data_array_t **p_data0, + obs_data_array_t **p_data1); + EXPORT obs_data_t *obs_hotkeys_save_encoder(obs_encoder_t *encoder); EXPORT obs_data_t *obs_hotkeys_save_output(obs_output_t *output); diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index eede65bd3..0aea02247 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -248,6 +248,7 @@ struct obs_core_video { gs_samplerstate_t *point_sampler; gs_stagesurf_t *mapped_surface; int cur_texture; + long raw_active; uint64_t video_time; uint64_t video_avg_frame_time_ns; @@ -330,6 +331,8 @@ struct obs_core_data { long long unnamed_index; + obs_data_t *private_data; + volatile bool valid; }; @@ -408,6 +411,13 @@ extern bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts, uint32_t mixers, struct audio_output_data *mixes); +extern void start_raw_video(video_t *video, + const struct video_scale_info *conversion, + void (*callback)(void *param, struct video_data *frame), + void *param); +extern void stop_raw_video(video_t *video, + void (*callback)(void *param, struct video_data *frame), + void *param); /* ------------------------------------------------------------------------- */ /* obs shared context data */ @@ -598,6 +608,7 @@ struct obs_source { float volume; int64_t sync_offset; int64_t last_sync_offset; + float balance; /* async video data */ gs_texture_t *async_texture; @@ -696,9 +707,6 @@ extern bool obs_source_init_context(struct obs_source *source, obs_data_t *settings, const char *name, obs_data_t *hotkey_data, bool private); -extern void obs_source_save(obs_source_t *source); -extern void obs_source_load(obs_source_t *source); - extern bool obs_transition_init(obs_source_t *transition); extern void obs_transition_free(obs_source_t *transition); extern void obs_transition_tick(obs_source_t *transition); diff --git a/libobs/obs-module.c b/libobs/obs-module.c index 274f9fe0b..41369659c 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -85,6 +85,17 @@ int obs_open_module(obs_module_t **module, const char *path, if (!module || !path || !obs) return MODULE_ERROR; +#ifdef __APPLE__ + /* HACK: Do not load obsolete obs-browser build on macOS; the + * obs-browser plugin used to live in the Application Support + * directory. */ + if (astrstri(path, "Library/Application Support") != NULL && + astrstri(path, "obs-browser") != NULL) { + blog(LOG_WARNING, "Ignoring old obs-browser.so version"); + return MODULE_ERROR; + } +#endif + blog(LOG_DEBUG, "---------------------------------"); mod.module = os_dlopen(path); @@ -725,13 +736,13 @@ void obs_register_service_s(const struct obs_service_info *info, size_t size) HANDLE_ERROR(size, obs_service_info, info); } -void obs_regsiter_modal_ui_s(const struct obs_modal_ui *info, size_t size) +void obs_register_modal_ui_s(const struct obs_modal_ui *info, size_t size) { #define CHECK_REQUIRED_VAL_(info, val, func) \ CHECK_REQUIRED_VAL(struct obs_modal_ui, info, val, func) - CHECK_REQUIRED_VAL_(info, task, obs_regsiter_modal_ui); - CHECK_REQUIRED_VAL_(info, target, obs_regsiter_modal_ui); - CHECK_REQUIRED_VAL_(info, exec, obs_regsiter_modal_ui); + CHECK_REQUIRED_VAL_(info, task, obs_register_modal_ui); + CHECK_REQUIRED_VAL_(info, target, obs_register_modal_ui); + CHECK_REQUIRED_VAL_(info, exec, obs_register_modal_ui); #undef CHECK_REQUIRED_VAL_ REGISTER_OBS_DEF(size, obs_modal_ui, obs->modal_ui_callbacks, info); @@ -741,13 +752,13 @@ void obs_regsiter_modal_ui_s(const struct obs_modal_ui *info, size_t size) HANDLE_ERROR(size, obs_modal_ui, info); } -void obs_regsiter_modeless_ui_s(const struct obs_modeless_ui *info, size_t size) +void obs_register_modeless_ui_s(const struct obs_modeless_ui *info, size_t size) { #define CHECK_REQUIRED_VAL_(info, val, func) \ CHECK_REQUIRED_VAL(struct obs_modeless_ui, info, val, func) - CHECK_REQUIRED_VAL_(info, task, obs_regsiter_modeless_ui); - CHECK_REQUIRED_VAL_(info, target, obs_regsiter_modeless_ui); - CHECK_REQUIRED_VAL_(info, create, obs_regsiter_modeless_ui); + CHECK_REQUIRED_VAL_(info, task, obs_register_modeless_ui); + CHECK_REQUIRED_VAL_(info, target, obs_register_modeless_ui); + CHECK_REQUIRED_VAL_(info, create, obs_register_modeless_ui); #undef CHECK_REQUIRED_VAL_ REGISTER_OBS_DEF(size, obs_modeless_ui, obs->modeless_ui_callbacks, diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c index e20e82e8f..355b832e4 100644 --- a/libobs/obs-nix.c +++ b/libobs/obs-nix.c @@ -16,6 +16,7 @@ along with this program. If not, see . ******************************************************************************/ +#include "obs-internal.h" #if defined(__FreeBSD__) #define _GNU_SOURCE #endif @@ -28,13 +29,13 @@ #include #include #include +#if USE_XINPUT +#include +#endif #include #include #include -#include #include -#include "util/dstr.h" -#include "obs-internal.h" const char *get_module_extension(void) { @@ -236,6 +237,34 @@ static void log_kernel_version(void) blog(LOG_INFO, "Kernel Version: %s %s", info.sysname, info.release); } +static void log_x_info(void) +{ + Display *dpy = XOpenDisplay(NULL); + if (!dpy) { + blog(LOG_INFO, "Unable to open X display"); + return; + } + + int protocol_version = ProtocolVersion(dpy); + int protocol_revision = ProtocolRevision(dpy); + int vendor_release = VendorRelease(dpy); + const char *vendor_name = ServerVendor(dpy); + + if (strstr(vendor_name, "X.Org")) { + blog(LOG_INFO, "Window System: X%d.%d, Vendor: %s, Version: %d" + ".%d.%d", protocol_version, protocol_revision, + vendor_name, vendor_release / 10000000, + (vendor_release / 100000) % 100, + (vendor_release / 1000) % 100); + } else { + blog(LOG_INFO, "Window System: X%d.%d - vendor string: %s - " + "vendor release: %d", protocol_version, + protocol_revision, vendor_name, vendor_release); + } + + XCloseDisplay(dpy); +} + #if defined(__linux__) static void log_distribution_info(void) { @@ -292,6 +321,7 @@ void log_system_info(void) #if defined(__linux__) log_distribution_info(); #endif + log_x_info(); } /* So here's how linux works with key mapping: @@ -327,6 +357,12 @@ struct obs_hotkeys_platform { xcb_keysym_t *keysyms; int num_keysyms; int syms_per_code; + +#if USE_XINPUT + bool pressed[XINPUT_MOUSE_LEN]; + bool update[XINPUT_MOUSE_LEN]; + bool button_pressed[XINPUT_MOUSE_LEN]; +#endif }; #define MOUSE_1 (1<<16) @@ -707,6 +743,54 @@ static inline bool fill_keycodes(struct obs_core_hotkeys *hotkeys) return error != NULL || reply == NULL; } +static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context, + xcb_connection_t *connection) +{ + int def_screen_idx = XDefaultScreen(context->display); + xcb_screen_iterator_t iter; + + iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); + while (iter.rem) { + if (def_screen_idx-- == 0) + return iter.data; + + xcb_screen_next(&iter); + } + + return NULL; +} + +static inline xcb_window_t root_window(obs_hotkeys_platform_t *context, + xcb_connection_t *connection) +{ + xcb_screen_t *screen = default_screen(context, connection); + if (screen) + return screen->root; + return 0; +} + +#if USE_XINPUT +static inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys) +{ + obs_hotkeys_platform_t *context = hotkeys->platform_context; + xcb_connection_t *connection = XGetXCBConnection( + context->display); + xcb_window_t window = root_window(context, connection); + + struct { + xcb_input_event_mask_t head; + xcb_input_xi_event_mask_t mask; + } mask; + mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER; + mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t); + mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | + XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE; + + xcb_input_xi_select_events(connection, window, 1, &mask.head); + xcb_flush(connection); +} +#endif + bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys) { Display *display = XOpenDisplay(NULL); @@ -716,6 +800,9 @@ bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys) hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t)); hotkeys->platform_context->display = display; +#if USE_XINPUT + registerMouseEvents(hotkeys); +#endif fill_base_keysyms(hotkeys); fill_keycodes(hotkeys); return true; @@ -735,41 +822,147 @@ void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys) hotkeys->platform_context = NULL; } -static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context, - xcb_connection_t *connection) +static bool mouse_button_pressed(xcb_connection_t *connection, + obs_hotkeys_platform_t *context, obs_key_t key) { - int def_screen_idx = XDefaultScreen(context->display); - xcb_screen_iterator_t iter; + bool ret = false; - iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); - while (iter.rem) { - if (def_screen_idx-- == 0) { - return iter.data; - break; +#if USE_XINPUT + memset(context->pressed, 0, XINPUT_MOUSE_LEN); + memset(context->update, 0, XINPUT_MOUSE_LEN); + + xcb_generic_event_t *ev; + while ((ev = xcb_poll_for_event(connection))) { + if ((ev->response_type & ~80) == XCB_GE_GENERIC) { + switch (((xcb_ge_event_t *) ev)->event_type) { + case XCB_INPUT_RAW_BUTTON_PRESS: { + xcb_input_raw_button_press_event_t *mot; + mot = (xcb_input_raw_button_press_event_t *) ev; + if (mot->detail < XINPUT_MOUSE_LEN) { + context->pressed[mot->detail-1] = true; + context->update[mot->detail-1] = true; + } else { + blog(LOG_WARNING, "Unsupported button"); + } + break; + } + case XCB_INPUT_RAW_BUTTON_RELEASE: { + xcb_input_raw_button_release_event_t *mot; + mot = (xcb_input_raw_button_release_event_t *) ev; + if (mot->detail < XINPUT_MOUSE_LEN) + context->update[mot->detail-1] = true; + else + blog(LOG_WARNING, "Unsupported button"); + break; + } + default: + break; + } } - - xcb_screen_next(&iter); + free(ev); } - return NULL; -} - -static inline xcb_window_t root_window(obs_hotkeys_platform_t *context, - xcb_connection_t *connection) -{ - xcb_screen_t *screen = default_screen(context, connection); - if (screen) - return screen->root; - return 0; -} + // Mouse 2 for OBS is Right Click and Mouse 3 is Wheel Click. + // Mouse Wheel axis clicks (xinput mot->detail 4 5 6 7) are ignored. + switch (key) { + case OBS_KEY_MOUSE1: + ret = context->pressed[0] || context->button_pressed[0]; + break; + case OBS_KEY_MOUSE2: + ret = context->pressed[2] || context->button_pressed[2]; + break; + case OBS_KEY_MOUSE3: + ret = context->pressed[1] || context->button_pressed[1]; + break; + case OBS_KEY_MOUSE4: + ret = context->pressed[7] || context->button_pressed[7]; + break; + case OBS_KEY_MOUSE5: + ret = context->pressed[8] || context->button_pressed[8]; + break; + case OBS_KEY_MOUSE6: + ret = context->pressed[9] || context->button_pressed[9]; + break; + case OBS_KEY_MOUSE7: + ret = context->pressed[10] || context->button_pressed[10]; + break; + case OBS_KEY_MOUSE8: + ret = context->pressed[11] || context->button_pressed[11]; + break; + case OBS_KEY_MOUSE9: + ret = context->pressed[12] || context->button_pressed[12]; + break; + case OBS_KEY_MOUSE10: + ret = context->pressed[13] || context->button_pressed[13]; + break; + case OBS_KEY_MOUSE11: + ret = context->pressed[14] || context->button_pressed[14]; + break; + case OBS_KEY_MOUSE12: + ret = context->pressed[15] || context->button_pressed[15]; + break; + case OBS_KEY_MOUSE13: + ret = context->pressed[16] || context->button_pressed[16]; + break; + case OBS_KEY_MOUSE14: + ret = context->pressed[17] || context->button_pressed[17]; + break; + case OBS_KEY_MOUSE15: + ret = context->pressed[18] || context->button_pressed[18]; + break; + case OBS_KEY_MOUSE16: + ret = context->pressed[19] || context->button_pressed[19]; + break; + case OBS_KEY_MOUSE17: + ret = context->pressed[20] || context->button_pressed[20]; + break; + case OBS_KEY_MOUSE18: + ret = context->pressed[21] || context->button_pressed[21]; + break; + case OBS_KEY_MOUSE19: + ret = context->pressed[22] || context->button_pressed[22]; + break; + case OBS_KEY_MOUSE20: + ret = context->pressed[23] || context->button_pressed[23]; + break; + case OBS_KEY_MOUSE21: + ret = context->pressed[24] || context->button_pressed[24]; + break; + case OBS_KEY_MOUSE22: + ret = context->pressed[25] || context->button_pressed[25]; + break; + case OBS_KEY_MOUSE23: + ret = context->pressed[26] || context->button_pressed[26]; + break; + case OBS_KEY_MOUSE24: + ret = context->pressed[27] || context->button_pressed[27]; + break; + case OBS_KEY_MOUSE25: + ret = context->pressed[28] || context->button_pressed[28]; + break; + case OBS_KEY_MOUSE26: + ret = context->pressed[29] || context->button_pressed[29]; + break; + case OBS_KEY_MOUSE27: + ret = context->pressed[30] || context->button_pressed[30]; + break; + case OBS_KEY_MOUSE28: + ret = context->pressed[31] || context->button_pressed[31]; + break; + case OBS_KEY_MOUSE29: + ret = context->pressed[32] || context->button_pressed[32]; + break; + default: + break; + } -static bool mouse_button_pressed(xcb_connection_t *connection, - obs_hotkeys_platform_t *context, obs_key_t key) -{ + for (int i = 0; i != XINPUT_MOUSE_LEN; i++) + if (context->update[i]) + context->button_pressed[i] = context->pressed[i]; +#else xcb_generic_error_t *error = NULL; xcb_query_pointer_cookie_t qpc; xcb_query_pointer_reply_t *reply; - bool ret = false; qpc = xcb_query_pointer(connection, root_window(context, connection)); reply = xcb_query_pointer_reply(connection, qpc, &error); @@ -789,6 +982,7 @@ static bool mouse_button_pressed(xcb_connection_t *connection, free(reply); free(error); +#endif return ret; } diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 4df3b9d6c..f82382fb4 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -22,7 +22,7 @@ #if BUILD_CAPTIONS #include -#include +#include #endif static inline bool active(const struct obs_output *output) @@ -419,6 +419,18 @@ bool obs_output_active(const obs_output_t *output) (active(output) || reconnecting(output)) : false; } +uint32_t obs_output_get_flags(const obs_output_t *output) +{ + return obs_output_valid(output, "obs_output_get_flags") ? + output->info.flags : 0; +} + +uint32_t obs_get_output_flags(const char *id) +{ + const struct obs_output_info *info = find_output(id); + return info ? info->flags : 0; +} + static inline obs_data_t *get_defaults(const struct obs_output_info *info) { obs_data_t *settings = obs_data_create(); @@ -977,7 +989,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) if (out->priority > 1) return false; - sei_init(&sei); + sei_init(&sei, 0.0); da_init(out_data); da_push_back_array(out_data, &ref, sizeof(ref)); @@ -1541,7 +1553,7 @@ static void hook_data_capture(struct obs_output *output, bool encoded, encoded_callback, output); } else { if (has_video) - video_output_connect(output->video, + start_raw_video(output->video, get_video_conversion(output), default_raw_video_callback, output); if (has_audio) @@ -1797,7 +1809,7 @@ static void *end_data_capture_thread(void *data) stop_audio_encoders(output, encoded_callback); } else { if (has_video) - video_output_disconnect(output->video, + stop_raw_video(output->video, default_raw_video_callback, output); if (has_audio) audio_output_disconnect(output->audio, @@ -2061,7 +2073,8 @@ static struct caption_text *caption_text_new(const char *text, size_t bytes, struct caption_text *tail, struct caption_text **head) { struct caption_text *next = bzalloc(sizeof(struct caption_text)); - snprintf(&next->text[0], CAPTION_LINE_BYTES + 1, "%.*s", bytes, text); + snprintf(&next->text[0], CAPTION_LINE_BYTES + 1, "%.*s", + (int)bytes, text); if (!*head) { *head = next; @@ -2081,34 +2094,14 @@ void obs_output_output_caption_text1(obs_output_t *output, const char *text) // split text into 32 character strings int size = (int)strlen(text); - int r; - size_t char_count; - size_t line_length = 0; - size_t trimmed_length = 0; - blog(LOG_DEBUG, "Caption text: %s", text); pthread_mutex_lock(&output->caption_mutex); - for (r = 0 ; 0 < size && CAPTION_LINE_CHARS > r; ++r) { - line_length = utf8_line_length(text); - trimmed_length = utf8_trimmed_length(text, line_length); - char_count = utf8_char_count(text, trimmed_length); - - if (SCREEN_COLS < char_count) { - char_count = utf8_wrap_length(text, CAPTION_LINE_CHARS); - line_length = utf8_string_length(text, char_count + 1); - } - - output->caption_tail = caption_text_new( - text, - line_length, - output->caption_tail, - &output->caption_head); - - text += line_length; - size -= (int)line_length; - } + output->caption_tail = caption_text_new( + text, size, + output->caption_tail, + &output->caption_head); pthread_mutex_unlock(&output->caption_mutex); } diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 2b7a7f016..a3ba2d806 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -20,6 +20,21 @@ #include "graphics/math-defs.h" #include "obs-scene.h" +const struct obs_source_info group_info; + +static void resize_group(obs_sceneitem_t *group); +static void resize_scene(obs_scene_t *scene); +static void signal_parent(obs_scene_t *parent, const char *name, + calldata_t *params); +static void get_ungrouped_transform(obs_sceneitem_t *group, + struct vec2 *pos, + struct vec2 *scale, + float *rot); +static inline bool crop_enabled(const struct obs_sceneitem_crop *crop); +static inline bool item_texture_enabled(const struct obs_scene_item *item); +static void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item, + const char *name); + /* NOTE: For proper mutex lock order (preventing mutual cross-locks), never * lock the graphics mutex inside either of the scene mutexes. * @@ -48,11 +63,9 @@ static inline void signal_item_remove(struct obs_scene_item *item) uint8_t stack[128]; calldata_init_fixed(¶ms, stack, sizeof(stack)); - calldata_set_ptr(¶ms, "scene", item->parent); calldata_set_ptr(¶ms, "item", item); - signal_handler_signal(item->parent->source->context.signals, - "item_remove", ¶ms); + signal_parent(item->parent, "item_remove", ¶ms); } static const char *scene_getname(void *unused) @@ -61,18 +74,28 @@ static const char *scene_getname(void *unused) return "Scene"; } +static const char *group_getname(void *unused) +{ + UNUSED_PARAMETER(unused); + return "Group"; +} + static void *scene_create(obs_data_t *settings, struct obs_source *source) { pthread_mutexattr_t attr; - struct obs_scene *scene = bmalloc(sizeof(struct obs_scene)); - scene->source = source; - scene->first_item = NULL; + struct obs_scene *scene = bzalloc(sizeof(struct obs_scene)); + scene->source = source; + + if (source->info.id == group_info.id) { + scene->is_group = true; + scene->custom_size = true; + scene->cx = 0; + scene->cy = 0; + } signal_handler_add_array(obs_source_get_signal_handler(source), obs_scene_signals); - scene->id_counter = 0; - if (pthread_mutexattr_init(&attr) != 0) goto fail; if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) @@ -311,21 +334,29 @@ static inline uint32_t calc_cy(const struct obs_scene_item *item, return (crop_cy > height) ? 2 : (height - crop_cy); } -static void update_item_transform(struct obs_scene_item *item) +static void update_item_transform(struct obs_scene_item *item, bool update_tex) { - uint32_t width = obs_source_get_width(item->source); - uint32_t height = obs_source_get_height(item->source); - uint32_t cx = calc_cx(item, width); - uint32_t cy = calc_cy(item, height); + uint32_t width; + uint32_t height; + uint32_t cx; + uint32_t cy; struct vec2 base_origin; struct vec2 origin; - struct vec2 scale = item->scale; + struct vec2 scale; struct calldata params; uint8_t stack[128]; if (os_atomic_load_long(&item->defer_update) > 0) return; + width = obs_source_get_width(item->source); + height = obs_source_get_height(item->source); + cx = calc_cx(item, width); + cy = calc_cy(item, height); + scale = item->scale; + item->last_width = width; + item->last_height = height; + width = cx; height = cy; @@ -378,14 +409,26 @@ static void update_item_transform(struct obs_scene_item *item) /* ----------------------- */ - item->last_width = width; - item->last_height = height; - calldata_init_fixed(¶ms, stack, sizeof(stack)); - calldata_set_ptr(¶ms, "scene", item->parent); calldata_set_ptr(¶ms, "item", item); - signal_handler_signal(item->parent->source->context.signals, - "item_transform", ¶ms); + signal_parent(item->parent, "item_transform", ¶ms); + + if (!update_tex) + return; + + if (item->item_render && !item_texture_enabled(item)) { + obs_enter_graphics(); + gs_texrender_destroy(item->item_render); + item->item_render = NULL; + obs_leave_graphics(); + + } else if (!item->item_render && item_texture_enabled(item)) { + obs_enter_graphics(); + item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE); + obs_leave_graphics(); + } + + os_atomic_set_bool(&item->update_transform, false); } static inline bool source_size_changed(struct obs_scene_item *item) @@ -414,7 +457,7 @@ static inline bool item_is_scene(const struct obs_scene_item *item) static inline bool item_texture_enabled(const struct obs_scene_item *item) { return crop_enabled(&item->crop) || scale_filter_enabled(item) || - item_is_scene(item); + (item_is_scene(item) && !item->is_group); } static void render_item_texture(struct obs_scene_item *item) @@ -467,6 +510,10 @@ static inline void render_item(struct obs_scene_item *item) if (item->item_render) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); + + if (!width || !height) + return; + uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); @@ -521,6 +568,49 @@ static void scene_video_tick(void *data, float seconds) UNUSED_PARAMETER(seconds); } +/* assumes video lock */ +static void update_transforms_and_prune_sources(obs_scene_t *scene, + struct darray *remove_items, obs_sceneitem_t *group_sceneitem) +{ + struct obs_scene_item *item = scene->first_item; + bool rebuild_group = group_sceneitem && + os_atomic_load_bool(&group_sceneitem->update_group_resize); + + while (item) { + if (obs_source_removed(item->source)) { + struct obs_scene_item *del_item = item; + item = item->next; + + remove_without_release(del_item); + darray_push_back(sizeof(struct obs_scene_item*), + remove_items, &del_item); + rebuild_group = true; + continue; + } + + if (item->is_group) { + obs_scene_t *group_scene = item->source->context.data; + + video_lock(group_scene); + update_transforms_and_prune_sources(group_scene, + remove_items, item); + video_unlock(group_scene); + } + + if (os_atomic_load_bool(&item->update_transform) || + source_size_changed(item)) { + + update_item_transform(item, true); + rebuild_group = true; + } + + item = item->next; + } + + if (rebuild_group && group_sceneitem) + resize_group(group_sceneitem); +} + static void scene_video_render(void *data, gs_effect_t *effect) { DARRAY(struct obs_scene_item*) remove_items; @@ -530,24 +620,17 @@ static void scene_video_render(void *data, gs_effect_t *effect) da_init(remove_items); video_lock(scene); - item = scene->first_item; + + if (!scene->is_group) { + update_transforms_and_prune_sources(scene, &remove_items.da, + NULL); + } gs_blend_state_push(); gs_reset_blend_state(); + item = scene->first_item; while (item) { - if (obs_source_removed(item->source)) { - struct obs_scene_item *del_item = item; - item = item->next; - - remove_without_release(del_item); - da_push_back(remove_items, &del_item); - continue; - } - - if (source_size_changed(item)) - update_item_transform(item); - if (item->user_visible) render_item(item); @@ -586,18 +669,24 @@ static void set_visibility(struct obs_scene_item *item, bool vis) pthread_mutex_unlock(&item->actions_mutex); } +static void scene_load(void *data, obs_data_t *settings); + static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) { const char *name = obs_data_get_string(item_data, "name"); - obs_source_t *source = obs_get_source_by_name(name); + obs_source_t *source; const char *scale_filter_str; struct obs_scene_item *item; bool visible; bool lock; + if (obs_data_get_bool(item_data, "group_item_backup")) + return; + + source = obs_get_source_by_name(name); if (!source) { - blog(LOG_WARNING, "[scene_load_item] Source %s not found!", - name); + blog(LOG_WARNING, "[scene_load_item] Source %s not " + "found!", name); return; } @@ -611,6 +700,8 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) return; } + item->is_group = source->info.id == group_info.id; + obs_data_set_default_int(item_data, "align", OBS_ALIGN_TOP | OBS_ALIGN_LEFT); @@ -673,7 +764,7 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) obs_source_release(source); - update_item_transform(item); + update_item_transform(item, false); } static void scene_load(void *data, obs_data_t *settings) @@ -697,22 +788,38 @@ static void scene_load(void *data, obs_data_t *settings) if (obs_data_has_user_value(settings, "id_counter")) scene->id_counter = obs_data_get_int(settings, "id_counter"); + if (obs_data_get_bool(settings, "custom_size")) { + scene->cx = (uint32_t)obs_data_get_int(settings, "cx"); + scene->cy = (uint32_t)obs_data_get_int(settings, "cy"); + scene->custom_size = true; + } + obs_data_array_release(items); } +static void scene_save(void *data, obs_data_t *settings); + static void scene_save_item(obs_data_array_t *array, - struct obs_scene_item *item) + struct obs_scene_item *item, + struct obs_scene_item *backup_group) { obs_data_t *item_data = obs_data_create(); const char *name = obs_source_get_name(item->source); const char *scale_filter; + struct vec2 pos = item->pos; + struct vec2 scale = item->scale; + float rot = item->rot; + + if (backup_group) { + get_ungrouped_transform(backup_group, &pos, &scale, &rot); + } obs_data_set_string(item_data, "name", name); obs_data_set_bool (item_data, "visible", item->user_visible); obs_data_set_bool (item_data, "locked", item->locked); - obs_data_set_double(item_data, "rot", item->rot); - obs_data_set_vec2 (item_data, "pos", &item->pos); - obs_data_set_vec2 (item_data, "scale", &item->scale); + obs_data_set_double(item_data, "rot", rot); + obs_data_set_vec2 (item_data, "pos", &pos); + obs_data_set_vec2 (item_data, "scale", &scale); obs_data_set_int (item_data, "align", (int)item->align); obs_data_set_int (item_data, "bounds_type", (int)item->bounds_type); obs_data_set_int (item_data, "bounds_align", (int)item->bounds_align); @@ -722,6 +829,25 @@ static void scene_save_item(obs_data_array_t *array, obs_data_set_int (item_data, "crop_right", (int)item->crop.right); obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom); obs_data_set_int (item_data, "id", item->id); + obs_data_set_bool (item_data, "group_item_backup", !!backup_group); + + if (item->is_group) { + obs_scene_t *group_scene = item->source->context.data; + obs_sceneitem_t *group_item; + + /* save group items as part of main scene, but ignored. + * causes an automatic ungroup if scene collection file + * is loaded in previous versions. */ + full_lock(group_scene); + + group_item = group_scene->first_item; + while (group_item) { + scene_save_item(array, group_item, item); + group_item = group_item->next; + } + + full_unlock(group_scene); + } if (item->scale_filter == OBS_SCALE_POINT) scale_filter = "point"; @@ -753,11 +879,16 @@ static void scene_save(void *data, obs_data_t *settings) item = scene->first_item; while (item) { - scene_save_item(array, item); + scene_save_item(array, item, NULL); item = item->next; } obs_data_set_int(settings, "id_counter", scene->id_counter); + obs_data_set_bool(settings, "custom_size", scene->custom_size); + if (scene->custom_size) { + obs_data_set_int(settings, "cx", scene->cx); + obs_data_set_int(settings, "cy", scene->cy); + } full_unlock(scene); @@ -767,14 +898,14 @@ static void scene_save(void *data, obs_data_t *settings) static uint32_t scene_getwidth(void *data) { - UNUSED_PARAMETER(data); - return obs->video.base_width; + obs_scene_t *scene = data; + return scene->custom_size ? scene->cx : obs->video.base_width; } static uint32_t scene_getheight(void *data) { - UNUSED_PARAMETER(data); - return obs->video.base_height; + obs_scene_t *scene = data; + return scene->custom_size ? scene->cy : obs->video.base_height; } static void apply_scene_item_audio_actions(struct obs_scene_item *item, @@ -1008,20 +1139,51 @@ const struct obs_source_info scene_info = .enum_all_sources = scene_enum_all_sources }; -obs_scene_t *obs_scene_create(const char *name) +const struct obs_source_info group_info = +{ + .id = "group", + .type = OBS_SOURCE_TYPE_SCENE, + .output_flags = OBS_SOURCE_VIDEO | + OBS_SOURCE_CUSTOM_DRAW | + OBS_SOURCE_COMPOSITE, + .get_name = group_getname, + .create = scene_create, + .destroy = scene_destroy, + .video_tick = scene_video_tick, + .video_render = scene_video_render, + .audio_render = scene_audio_render, + .get_width = scene_getwidth, + .get_height = scene_getheight, + .load = scene_load, + .save = scene_save, + .enum_active_sources = scene_enum_active_sources, + .enum_all_sources = scene_enum_all_sources +}; + +static inline obs_scene_t *create_id(const char *id, const char *name) { - struct obs_source *source = obs_source_create("scene", name, NULL, + struct obs_source *source = obs_source_create(id, name, NULL, NULL); return source->context.data; } -obs_scene_t *obs_scene_create_private(const char *name) +static inline obs_scene_t *create_private_id(const char *id, const char *name) { - struct obs_source *source = obs_source_create_private("scene", name, + struct obs_source *source = obs_source_create_private(id, name, NULL); return source->context.data; } +obs_scene_t *obs_scene_create(const char *name) +{ + return create_id("scene", name); +} + +obs_scene_t *obs_scene_create_private(const char *name) +{ + return create_private_id("scene", name); +} + static obs_source_t *get_child_at_idx(obs_scene_t *scene, size_t idx) { struct obs_scene_item *item = scene->first_item; @@ -1062,6 +1224,59 @@ static inline obs_source_t *new_ref(obs_source_t *source) return source; } +static inline void duplicate_item_data(struct obs_scene_item *dst, + struct obs_scene_item *src, bool defer_texture_update, + bool duplicate_hotkeys, bool duplicate_private_data) +{ + struct obs_scene *dst_scene = dst->parent; + + if (!src->user_visible) + set_visibility(dst, false); + + dst->selected = src->selected; + dst->pos = src->pos; + dst->rot = src->rot; + dst->scale = src->scale; + dst->align = src->align; + dst->last_width = src->last_width; + dst->last_height = src->last_height; + dst->output_scale = src->output_scale; + dst->scale_filter = src->scale_filter; + dst->box_transform = src->box_transform; + dst->draw_transform = src->draw_transform; + dst->bounds_type = src->bounds_type; + dst->bounds_align = src->bounds_align; + dst->bounds = src->bounds; + + if (duplicate_hotkeys && !dst_scene->source->context.private) { + obs_data_array_t *data0 = NULL; + obs_data_array_t *data1 = NULL; + + obs_hotkey_pair_save(src->toggle_visibility, &data0, &data1); + obs_hotkey_pair_load(dst->toggle_visibility, data0, data1); + + obs_data_array_release(data0); + obs_data_array_release(data1); + } + + obs_sceneitem_set_crop(dst, &src->crop); + + if (defer_texture_update) { + os_atomic_set_bool(&dst->update_transform, true); + } else { + if (!dst->item_render && item_texture_enabled(dst)) { + obs_enter_graphics(); + dst->item_render = gs_texrender_create( + GS_RGBA, GS_ZS_NONE); + obs_leave_graphics(); + } + } + + if (duplicate_private_data) { + obs_data_apply(dst->private_settings, src->private_settings); + } +} + obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, enum obs_scene_duplicate_type type) { @@ -1092,14 +1307,19 @@ obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, /* --------------------------------- */ - new_scene = make_private ? - obs_scene_create_private(name) : obs_scene_create(name); + new_scene = make_private + ? create_private_id(scene->source->info.id, name) + : create_id(scene->source->info.id, name); obs_source_copy_filters(new_scene->source, scene->source); obs_data_apply(new_scene->source->private_settings, scene->source->private_settings); + /* never duplicate sub-items for groups */ + if (scene->is_group) + make_unique = false; + for (size_t i = 0; i < items.num; i++) { item = items.array[i]; source = make_unique ? @@ -1115,36 +1335,8 @@ obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, continue; } - if (!item->user_visible) - set_visibility(new_item, false); - - new_item->selected = item->selected; - new_item->pos = item->pos; - new_item->rot = item->rot; - new_item->scale = item->scale; - new_item->align = item->align; - new_item->last_width = item->last_width; - new_item->last_height = item->last_height; - new_item->output_scale = item->output_scale; - new_item->scale_filter = item->scale_filter; - new_item->box_transform = item->box_transform; - new_item->draw_transform = item->draw_transform; - new_item->bounds_type = item->bounds_type; - new_item->bounds_align = item->bounds_align; - new_item->bounds = item->bounds; - - new_item->toggle_visibility = - OBS_INVALID_HOTKEY_PAIR_ID; - - obs_sceneitem_set_crop(new_item, &item->crop); - - if (!new_item->item_render && - item_texture_enabled(new_item)) { - obs_enter_graphics(); - new_item->item_render = gs_texrender_create( - GS_RGBA, GS_ZS_NONE); - obs_leave_graphics(); - } + duplicate_item_data(new_item, item, false, false, + false); obs_source_release(source); } @@ -1153,6 +1345,9 @@ obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, for (size_t i = 0; i < items.num; i++) obs_sceneitem_release(items.array[i]); + if (new_scene->is_group) + resize_scene(new_scene); + da_free(items); return new_scene; } @@ -1182,6 +1377,14 @@ obs_scene_t *obs_scene_from_source(const obs_source_t *source) return source->context.data; } +obs_scene_t *obs_group_from_source(const obs_source_t *source) +{ + if (!source || source->info.id != group_info.id) + return NULL; + + return source->context.data; +} + obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name) { struct obs_scene_item *item; @@ -1332,18 +1535,55 @@ static void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item, dstr_free(&hide_desc); } +static void sceneitem_rename_hotkey(const obs_sceneitem_t *scene_item, + const char *new_name) +{ + struct dstr show = { 0 }; + struct dstr hide = { 0 }; + struct dstr show_desc = { 0 }; + struct dstr hide_desc = { 0 }; + + dstr_copy(&show, "libobs.show_scene_item.%1"); + dstr_replace(&show, "%1", new_name); + dstr_copy(&hide, "libobs.hide_scene_item.%1"); + dstr_replace(&hide, "%1", new_name); + + obs_hotkey_pair_set_names(scene_item->toggle_visibility, + show.array, hide.array); + + dstr_copy(&show_desc, obs->hotkeys.sceneitem_show); + dstr_replace(&show_desc, "%1", new_name); + dstr_copy(&hide_desc, obs->hotkeys.sceneitem_hide); + dstr_replace(&hide_desc, "%1", new_name); + + obs_hotkey_pair_set_descriptions(scene_item->toggle_visibility, + show_desc.array, hide_desc.array); + + dstr_free(&show); + dstr_free(&hide); + dstr_free(&show_desc); + dstr_free(&hide_desc); +} + +static void sceneitem_renamed(void *param, calldata_t *data) +{ + obs_sceneitem_t *scene_item = param; + const char *name = calldata_string(data, "new_name"); + + sceneitem_rename_hotkey(scene_item, name); +} + static inline bool source_has_audio(obs_source_t *source) { return (source->info.output_flags & (OBS_SOURCE_AUDIO | OBS_SOURCE_COMPOSITE)) != 0; } -obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) +static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, + obs_source_t *source, obs_sceneitem_t *insert_after) { struct obs_scene_item *last; struct obs_scene_item *item; - struct calldata params; - uint8_t stack[128]; pthread_mutex_t mutex; struct item_action action = { @@ -1380,7 +1620,9 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) item->actions_mutex = mutex; item->user_visible = true; item->locked = false; + item->is_group = source->info.id == group_info.id; item->private_settings = obs_data_create(); + item->toggle_visibility = OBS_INVALID_HOTKEY_PAIR_ID; os_atomic_set_long(&item->active_refs, 1); vec2_set(&item->scale, 1.0f, 1.0f); matrix4_identity(&item->draw_transform); @@ -1403,15 +1645,23 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) full_lock(scene); - last = scene->first_item; - if (!last) { - scene->first_item = item; + if (insert_after) { + obs_sceneitem_t *next = insert_after->next; + if (next) next->prev = item; + item->next = insert_after->next; + item->prev = insert_after; + insert_after->next = item; } else { - while (last->next) - last = last->next; + last = scene->first_item; + if (!last) { + scene->first_item = item; + } else { + while (last->next) + last = last->next; - last->next = item; - item->prev = last; + last->next = item; + item->prev = last; + } } full_unlock(scene); @@ -1419,12 +1669,23 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) if (!scene->source->context.private) init_hotkeys(scene, item, obs_source_get_name(source)); + signal_handler_connect(obs_source_get_signal_handler(source), "rename", + sceneitem_renamed, item); + + return item; +} + +obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) +{ + obs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL); + struct calldata params; + uint8_t stack[128]; + calldata_init_fixed(¶ms, stack, sizeof(stack)); calldata_set_ptr(¶ms, "scene", scene); calldata_set_ptr(¶ms, "item", item); signal_handler_signal(scene->source->context.signals, "item_add", ¶ms); - return item; } @@ -1439,6 +1700,9 @@ static void obs_sceneitem_destroy(obs_sceneitem_t *item) obs_data_release(item->private_settings); obs_hotkey_pair_unregister(item->toggle_visibility); pthread_mutex_destroy(&item->actions_mutex); + signal_handler_disconnect( + obs_source_get_signal_handler(item->source), + "rename", sceneitem_renamed, item); if (item->source) obs_source_release(item->source); da_free(item->audio_actions); @@ -1503,6 +1767,13 @@ obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item) return item ? item->source : NULL; } +static void signal_parent(obs_scene_t *parent, const char *command, + calldata_t *params) +{ + calldata_set_ptr(params, "scene", parent); + signal_handler_signal(parent->source->context.signals, command, params); +} + void obs_sceneitem_select(obs_sceneitem_t *item, bool select) { struct calldata params; @@ -1515,10 +1786,9 @@ void obs_sceneitem_select(obs_sceneitem_t *item, bool select) item->selected = select; calldata_init_fixed(¶ms, stack, sizeof(stack)); - calldata_set_ptr(¶ms, "scene", item->parent); calldata_set_ptr(¶ms, "item", item); - signal_handler_signal(item->parent->source->context.signals, - command, ¶ms); + + signal_parent(item->parent, command, ¶ms); } bool obs_sceneitem_selected(const obs_sceneitem_t *item) @@ -1526,11 +1796,19 @@ bool obs_sceneitem_selected(const obs_sceneitem_t *item) return item ? item->selected : false; } +#define do_update_transform(item) \ + do { \ + if (!item->parent || item->parent->is_group) \ + os_atomic_set_bool(&item->update_transform, true); \ + else \ + update_item_transform(item, false); \ + } while (false) + void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos) { if (item) { vec2_copy(&item->pos, pos); - update_item_transform(item); + do_update_transform(item); } } @@ -1538,7 +1816,7 @@ void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot) { if (item) { item->rot = rot; - update_item_transform(item); + do_update_transform(item); } } @@ -1546,7 +1824,7 @@ void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale) { if (item) { vec2_copy(&item->scale, scale); - update_item_transform(item); + do_update_transform(item); } } @@ -1554,7 +1832,7 @@ void obs_sceneitem_set_alignment(obs_sceneitem_t *item, uint32_t alignment) { if (item) { item->align = alignment; - update_item_transform(item); + do_update_transform(item); } } @@ -1567,10 +1845,7 @@ static inline void signal_reorder(struct obs_scene_item *item) command = "reorder"; calldata_init_fixed(¶ms, stack, sizeof(stack)); - calldata_set_ptr(¶ms, "scene", item->parent); - - signal_handler_signal(item->parent->source->context.signals, - command, ¶ms); + signal_parent(item->parent, command, ¶ms); } void obs_sceneitem_set_order(obs_sceneitem_t *item, @@ -1610,9 +1885,9 @@ void obs_sceneitem_set_order(obs_sceneitem_t *item, attach_sceneitem(scene, item, NULL); } - signal_reorder(item); - full_unlock(scene); + + signal_reorder(item); obs_scene_release(scene); } @@ -1642,9 +1917,9 @@ void obs_sceneitem_set_order_position(obs_sceneitem_t *item, attach_sceneitem(scene, item, next); } - signal_reorder(item); - full_unlock(scene); + + signal_reorder(item); obs_scene_release(scene); } @@ -1653,7 +1928,7 @@ void obs_sceneitem_set_bounds_type(obs_sceneitem_t *item, { if (item) { item->bounds_type = type; - update_item_transform(item); + do_update_transform(item); } } @@ -1662,7 +1937,7 @@ void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item, { if (item) { item->bounds_align = alignment; - update_item_transform(item); + do_update_transform(item); } } @@ -1670,7 +1945,7 @@ void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds) { if (item) { item->bounds = *bounds; - update_item_transform(item); + do_update_transform(item); } } @@ -1737,7 +2012,7 @@ void obs_sceneitem_set_info(obs_sceneitem_t *item, item->bounds_type = info->bounds_type; item->bounds_align = info->bounds_alignment; item->bounds = info->bounds; - update_item_transform(item); + do_update_transform(item); } } @@ -1791,12 +2066,10 @@ bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible) item->user_visible = visible; calldata_init_fixed(&cd, stack, sizeof(stack)); - calldata_set_ptr(&cd, "scene", item->parent); calldata_set_ptr(&cd, "item", item); calldata_set_bool(&cd, "visible", visible); - signal_handler_signal(item->parent->source->context.signals, - "item_visible", &cd); + signal_parent(item->parent, "item_visible", &cd); if (source_has_audio(item->source)) { pthread_mutex_lock(&item->actions_mutex); @@ -1888,9 +2161,9 @@ bool obs_scene_reorder_items(obs_scene_t *scene, prev = item_order[i]; } - signal_reorder(scene->first_item); - full_unlock(scene); + + signal_reorder(scene->first_item); obs_scene_release(scene); return true; } @@ -1920,8 +2193,6 @@ static inline bool crop_equal(const struct obs_sceneitem_crop *crop1, void obs_sceneitem_set_crop(obs_sceneitem_t *item, const struct obs_sceneitem_crop *crop) { - bool item_tex_now_enabled; - if (!obs_ptr_valid(item, "obs_sceneitem_set_crop")) return; if (!obs_ptr_valid(crop, "obs_sceneitem_set_crop")) @@ -1929,28 +2200,14 @@ void obs_sceneitem_set_crop(obs_sceneitem_t *item, if (crop_equal(crop, &item->crop)) return; - item_tex_now_enabled = crop_enabled(crop) || - scale_filter_enabled(item) || item_is_scene(item); - - obs_enter_graphics(); - - if (!item_tex_now_enabled) { - gs_texrender_destroy(item->item_render); - item->item_render = NULL; - - } else if (!item->item_render) { - item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE); - } - memcpy(&item->crop, crop, sizeof(*crop)); if (item->crop.left < 0) item->crop.left = 0; if (item->crop.right < 0) item->crop.right = 0; if (item->crop.top < 0) item->crop.top = 0; if (item->crop.bottom < 0) item->crop.bottom = 0; - obs_leave_graphics(); - update_item_transform(item); + os_atomic_set_bool(&item->update_transform, true); } void obs_sceneitem_get_crop(const obs_sceneitem_t *item, @@ -1972,19 +2229,7 @@ void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item, item->scale_filter = filter; - obs_enter_graphics(); - - if (!item_texture_enabled(item)) { - gs_texrender_destroy(item->item_render); - item->item_render = NULL; - - } else if (!item->item_render) { - item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE); - } - - obs_leave_graphics(); - - update_item_transform(item); + os_atomic_set_bool(&item->update_transform, true); } enum obs_scale_type obs_sceneitem_get_scale_filter( @@ -2008,7 +2253,24 @@ void obs_sceneitem_defer_update_end(obs_sceneitem_t *item) return; if (os_atomic_dec_long(&item->defer_update) == 0) - update_item_transform(item); + do_update_transform(item); +} + +void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item) +{ + if (!obs_ptr_valid(item, "obs_sceneitem_defer_group_resize_begin")) + return; + + os_atomic_inc_long(&item->defer_group_resize); +} + +void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item) +{ + if (!obs_ptr_valid(item, "obs_sceneitem_defer_group_resize_end")) + return; + + if (os_atomic_dec_long(&item->defer_group_resize) == 0) + os_atomic_set_bool(&item->update_group_resize, true); } int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item) @@ -2027,3 +2289,608 @@ obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item) obs_data_addref(item->private_settings); return item->private_settings; } + +static inline void transform_val(struct vec2 *v2, struct matrix4 *transform) +{ + struct vec3 v; + vec3_set(&v, v2->x, v2->y, 0.0f); + vec3_transform(&v, &v, transform); + v2->x = v.x; + v2->y = v.y; +} + +static void get_ungrouped_transform(obs_sceneitem_t *group, + struct vec2 *pos, + struct vec2 *scale, + float *rot) +{ + struct matrix4 transform; + struct matrix4 mat; + struct vec4 x_base; + + vec4_set(&x_base, 1.0f, 0.0f, 0.0f, 0.0f); + + matrix4_copy(&transform, &group->draw_transform); + + transform_val(pos, &transform); + vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f); + + vec4_set(&mat.x, scale->x, 0.0f, 0.0f, 0.0f); + vec4_set(&mat.y, 0.0f, scale->y, 0.0f, 0.0f); + vec4_set(&mat.z, 0.0f, 0.0f, 1.0f, 0.0f); + vec4_set(&mat.t, 0.0f, 0.0f, 0.0f, 1.0f); + matrix4_mul(&mat, &mat, &transform); + + scale->x = vec4_len(&mat.x) * (scale->x > 0.0f ? 1.0f : -1.0f); + scale->y = vec4_len(&mat.y) * (scale->y > 0.0f ? 1.0f : -1.0f); + *rot += group->rot; +} + +static void remove_group_transform(obs_sceneitem_t *group, + obs_sceneitem_t *item) +{ + obs_scene_t *parent = item->parent; + if (!parent || !group) + return; + + get_ungrouped_transform(group, &item->pos, &item->scale, &item->rot); + + update_item_transform(item, false); +} + +static void apply_group_transform(obs_sceneitem_t *item, obs_sceneitem_t *group) +{ + struct matrix4 transform; + struct matrix4 mat; + struct vec4 x_base; + + vec4_set(&x_base, 1.0f, 0.0f, 0.0f, 0.0f); + + matrix4_inv(&transform, &group->draw_transform); + + transform_val(&item->pos, &transform); + vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f); + + vec4_set(&mat.x, item->scale.x, 0.0f, 0.0f, 0.0f); + vec4_set(&mat.y, 0.0f, item->scale.y, 0.0f, 0.0f); + vec4_set(&mat.z, 0.0f, 0.0f, 1.0f, 0.0f); + vec4_set(&mat.t, 0.0f, 0.0f, 0.0f, 1.0f); + matrix4_mul(&mat, &mat, &transform); + + item->scale.x = vec4_len(&mat.x) * (item->scale.x > 0.0f ? 1.0f : -1.0f); + item->scale.y = vec4_len(&mat.y) * (item->scale.y > 0.0f ? 1.0f : -1.0f); + item->rot -= group->rot; + + update_item_transform(item, false); +} + +static bool resize_scene_base(obs_scene_t *scene, + struct vec2 *minv, + struct vec2 *maxv, + struct vec2 *scale) +{ + vec2_set(minv, M_INFINITE, M_INFINITE); + vec2_set(maxv, -M_INFINITE, -M_INFINITE); + + obs_sceneitem_t *item = scene->first_item; + if (!item) { + scene->cx = 0; + scene->cy = 0; + return false; + } + + while (item) { +#define get_min_max(x_val, y_val) \ + do { \ + struct vec3 v; \ + vec3_set(&v, x_val, y_val, 0.0f); \ + vec3_transform(&v, &v, &item->box_transform); \ + if (v.x < minv->x) minv->x = v.x; \ + if (v.y < minv->y) minv->y = v.y; \ + if (v.x > maxv->x) maxv->x = v.x; \ + if (v.y > maxv->y) maxv->y = v.y; \ + } while (false) + + get_min_max(0.0f, 0.0f); + get_min_max(1.0f, 0.0f); + get_min_max(0.0f, 1.0f); + get_min_max(1.0f, 1.0f); +#undef get_min_max + + item = item->next; + } + + item = scene->first_item; + while (item) { + vec2_sub(&item->pos, &item->pos, minv); + update_item_transform(item, false); + item = item->next; + } + + vec2_sub(scale, maxv, minv); + scene->cx = (uint32_t)ceilf(scale->x); + scene->cy = (uint32_t)ceilf(scale->y); + return true; +} + +static void resize_scene(obs_scene_t *scene) +{ + struct vec2 minv; + struct vec2 maxv; + struct vec2 scale; + resize_scene_base(scene, &minv, &maxv, &scale); +} + +/* assumes group scene and parent scene is locked */ +static void resize_group(obs_sceneitem_t *group) +{ + obs_scene_t *scene = group->source->context.data; + struct vec2 minv; + struct vec2 maxv; + struct vec2 scale; + + if (os_atomic_load_long(&group->defer_group_resize) > 0) + return; + + if (!resize_scene_base(scene, &minv, &maxv, &scale)) + return; + + if (group->bounds_type == OBS_BOUNDS_NONE) { + struct vec2 new_pos; + + if ((group->align & OBS_ALIGN_LEFT) != 0) + new_pos.x = minv.x; + else if ((group->align & OBS_ALIGN_RIGHT) != 0) + new_pos.x = maxv.x; + else + new_pos.x = (maxv.x - minv.x) * 0.5f + minv.x; + + if ((group->align & OBS_ALIGN_TOP) != 0) + new_pos.y = minv.y; + else if ((group->align & OBS_ALIGN_BOTTOM) != 0) + new_pos.y = maxv.y; + else + new_pos.y = (maxv.y - minv.y) * 0.5f + minv.y; + + transform_val(&new_pos, &group->draw_transform); + vec2_copy(&group->pos, &new_pos); + } + + os_atomic_set_bool(&group->update_group_resize, false); + + update_item_transform(group, false); +} + +obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name) +{ + return obs_scene_insert_group(scene, name, NULL, 0); +} + +obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, + const char *name, obs_sceneitem_t **items, size_t count) +{ + if (!scene) + return NULL; + + /* don't allow groups or sub-items of other groups */ + for (size_t i = count; i > 0; i--) { + obs_sceneitem_t *item = items[i - 1]; + if (item->parent != scene || item->is_group) + return NULL; + } + + obs_scene_t *sub_scene = create_id("group", name); + obs_sceneitem_t *last_item = items ? items[count - 1] : NULL; + + obs_sceneitem_t *item = obs_scene_add_internal( + scene, sub_scene->source, last_item); + + obs_scene_release(sub_scene); + + if (!items || !count) + return item; + + /* ------------------------- */ + + full_lock(scene); + full_lock(sub_scene); + sub_scene->first_item = items[0]; + + for (size_t i = count; i > 0; i--) { + size_t idx = i - 1; + remove_group_transform(item, items[idx]); + detach_sceneitem(items[idx]); + } + for (size_t i = 0; i < count; i++) { + size_t idx = i; + if (idx != (count - 1)) { + size_t next_idx = idx + 1; + items[idx]->next = items[next_idx]; + items[next_idx]->prev = items[idx]; + } else { + items[idx]->next = NULL; + } + items[idx]->parent = sub_scene; + apply_group_transform(items[idx], item); + } + items[0]->prev = NULL; + resize_group(item); + full_unlock(sub_scene); + full_unlock(scene); + + /* ------------------------- */ + + return item; +} + +obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, const char *name) +{ + if (!scene || !name || !*name) { + return NULL; + } + + obs_sceneitem_t *group = NULL; + obs_sceneitem_t *item; + + full_lock(scene); + + item = scene->first_item; + while (item) { + if (item->is_group && item->source->context.name) { + if (strcmp(item->source->context.name, name) == 0) { + group = item; + break; + } + } + + item = item->next; + } + + full_unlock(scene); + + return group; +} + +bool obs_sceneitem_is_group(obs_sceneitem_t *item) +{ + return item && item->is_group; +} + +obs_scene_t *obs_sceneitem_group_get_scene(const obs_sceneitem_t *item) +{ + return (item && item->is_group) ? item->source->context.data : NULL; +} + +void obs_sceneitem_group_ungroup(obs_sceneitem_t *item) +{ + if (!item || !item->is_group) + return; + + obs_scene_t *scene = item->parent; + obs_scene_t *subscene = item->source->context.data; + obs_sceneitem_t *insert_after = item; + obs_sceneitem_t *first; + obs_sceneitem_t *last; + + full_lock(scene); + + /* ------------------------- */ + + full_lock(subscene); + first = subscene->first_item; + last = first; + while (last) { + obs_sceneitem_t *dst; + + remove_group_transform(item, last); + dst = obs_scene_add_internal(scene, last->source, insert_after); + duplicate_item_data(dst, last, true, true, true); + apply_group_transform(last, item); + + if (!last->next) + break; + + insert_after = dst; + last = last->next; + } + full_unlock(subscene); + + /* ------------------------- */ + + detach_sceneitem(item); + full_unlock(scene); + + obs_sceneitem_release(item); +} + +void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item) +{ + if (!group || !group->is_group || !item) + return; + + obs_scene_t *scene = group->parent; + obs_scene_t *groupscene = group->source->context.data; + obs_sceneitem_t *last; + + if (item->parent != scene) + return; + + /* ------------------------- */ + + full_lock(scene); + remove_group_transform(group, item); + detach_sceneitem(item); + + /* ------------------------- */ + + full_lock(groupscene); + last = groupscene->first_item; + if (last) { + for (;;) { + if (!last->next) + break; + last = last->next; + } + last->next = item; + item->prev = last; + } else { + groupscene->first_item = item; + } + item->parent = groupscene; + item->next = NULL; + apply_group_transform(item, group); + resize_group(group); + full_unlock(groupscene); + + /* ------------------------- */ + + full_unlock(scene); +} + +void obs_sceneitem_group_remove_item(obs_sceneitem_t *group, + obs_sceneitem_t *item) +{ + if (!item || !group || !group->is_group) + return; + + obs_scene_t *groupscene = item->parent; + obs_scene_t *scene = group->parent; + + /* ------------------------- */ + + full_lock(scene); + full_lock(groupscene); + remove_group_transform(group, item); + detach_sceneitem(item); + + /* ------------------------- */ + + if (group->prev) { + group->prev->next = item; + item->prev = group->prev; + } else { + scene->first_item = item; + item->prev = NULL; + } + group->prev = item; + item->next = group; + item->parent = scene; + + /* ------------------------- */ + + resize_group(group); + full_unlock(groupscene); + full_unlock(scene); +} + +static void build_current_order_info(obs_scene_t *scene, + struct obs_sceneitem_order_info **items_out, + size_t *size_out) +{ + DARRAY(struct obs_sceneitem_order_info) items; + da_init(items); + + obs_sceneitem_t *item = scene->first_item; + while (item) { + da_push_back(items, &item); + + if (item->is_group) { + obs_scene_t *sub_scene = item->source->context.data; + + full_lock(sub_scene); + + obs_sceneitem_t *sub_item = sub_scene->first_item; + + while (sub_item) { + da_push_back(items, &item); + + sub_item = sub_item->next; + } + + full_unlock(sub_scene); + } + + item = item->next; + } + + *items_out = items.array; + *size_out = items.num; +} + +static bool sceneitems_match2(obs_scene_t *scene, + struct obs_sceneitem_order_info *items, size_t size) +{ + struct obs_sceneitem_order_info *cur_items; + size_t cur_size; + + build_current_order_info(scene, &cur_items, &cur_size); + if (cur_size != size) { + bfree(cur_items); + return false; + } + + for (size_t i = 0; i < size; i++) { + struct obs_sceneitem_order_info *new = &items[i]; + struct obs_sceneitem_order_info *old = &cur_items[i]; + + if (new->group != old->group || new->item != old->item) { + bfree(cur_items); + return false; + } + } + + bfree(cur_items); + return true; +} + +static obs_sceneitem_t *get_sceneitem_parent_group(obs_scene_t *scene, + obs_sceneitem_t *group_subitem) +{ + if (group_subitem->is_group) + return NULL; + + obs_sceneitem_t *item = scene->first_item; + while (item) { + if (item->is_group && + item->source->context.data == group_subitem->parent) + return item; + item = item->next; + } + + return NULL; +} + +bool obs_scene_reorder_items2(obs_scene_t *scene, + struct obs_sceneitem_order_info *item_order, + size_t item_order_size) +{ + if (!scene || !item_order_size || !item_order) + return false; + + obs_scene_addref(scene); + full_lock(scene); + + if (sceneitems_match2(scene, item_order, item_order_size)) { + full_unlock(scene); + obs_scene_release(scene); + return false; + } + + for (size_t i = 0; i < item_order_size; i++) { + struct obs_sceneitem_order_info *info = &item_order[i]; + if (!info->item->is_group) { + obs_sceneitem_t *group = + get_sceneitem_parent_group(scene, info->item); + remove_group_transform(group, info->item); + } + } + + scene->first_item = item_order[0].item; + + obs_sceneitem_t *prev = NULL; + for (size_t i = 0; i < item_order_size; i++) { + struct obs_sceneitem_order_info *info = &item_order[i]; + obs_sceneitem_t *item = info->item; + + if (info->item->is_group) { + obs_sceneitem_t *sub_prev = NULL; + obs_scene_t *sub_scene = + info->item->source->context.data; + + sub_scene->first_item = NULL; + + obs_scene_addref(sub_scene); + full_lock(sub_scene); + + for (i++; i < item_order_size; i++) { + struct obs_sceneitem_order_info *sub_info = + &item_order[i]; + obs_sceneitem_t *sub_item = sub_info->item; + + if (sub_info->group != info->item) { + i--; + break; + } + + if (!sub_scene->first_item) + sub_scene->first_item = sub_item; + + sub_item->prev = sub_prev; + sub_item->next = NULL; + sub_item->parent = sub_scene; + + if (sub_prev) + sub_prev->next = sub_item; + + apply_group_transform(sub_info->item, + sub_info->group); + + sub_prev = sub_item; + } + + resize_group(info->item); + full_unlock(sub_scene); + obs_scene_release(sub_scene); + } + + item->prev = prev; + item->next = NULL; + item->parent = scene; + + if (prev) + prev->next = item; + + prev = item; + } + + full_unlock(scene); + + signal_reorder(scene->first_item); + obs_scene_release(scene); + return true; +} + +obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene, + obs_sceneitem_t *group_subitem) +{ + if (!scene || !group_subitem || group_subitem->is_group) + return NULL; + + full_lock(scene); + obs_sceneitem_t *group = get_sceneitem_parent_group(scene, + group_subitem); + full_unlock(scene); + + return group; +} + +bool obs_source_is_group(const obs_source_t *source) +{ + return source && source->info.id == group_info.id; +} + +bool obs_scene_is_group(const obs_scene_t *scene) +{ + return scene ? scene->is_group : false; +} + +void obs_sceneitem_group_enum_items(obs_sceneitem_t *group, + bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), + void *param) +{ + if (!group || !group->is_group) + return; + + obs_scene_t *scene = group->source->context.data; + if (scene) + obs_scene_enum_items(scene, callback, param); +} + +void obs_sceneitem_force_update_transform(obs_sceneitem_t *item) +{ + if (!item) + return; + + if (os_atomic_set_bool(&item->update_transform, false)) + update_item_transform(item, false); +} diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 06db0a6d9..4591db5ed 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -32,12 +32,17 @@ struct obs_scene_item { volatile long ref; volatile bool removed; + bool is_group; + bool update_transform; + bool update_group_resize; + int64_t id; struct obs_scene *parent; struct obs_source *source; volatile long active_refs; volatile long defer_update; + volatile long defer_group_resize; bool user_visible; bool visible; bool selected; @@ -81,6 +86,11 @@ struct obs_scene_item { struct obs_scene { struct obs_source *source; + bool is_group; + bool custom_size; + uint32_t cx; + uint32_t cy; + int64_t id_counter; pthread_mutex_t video_mutex; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 869711738..d1d0bea1a 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -16,6 +16,7 @@ ******************************************************************************/ #include +#include #include "media-io/format-conversion.h" #include "media-io/video-frame.h" @@ -140,6 +141,7 @@ bool obs_source_init(struct obs_source *source) source->user_volume = 1.0f; source->volume = 1.0f; source->sync_offset = 0; + source->balance = 0.5f; pthread_mutex_init_value(&source->filter_mutex); pthread_mutex_init_value(&source->async_mutex); pthread_mutex_init_value(&source->audio_mutex); @@ -347,10 +349,14 @@ static obs_source_t *obs_source_create_internal(const char *id, blog(LOG_DEBUG, "%ssource '%s' (%s) created", private ? "private " : "", name, id); - obs_source_dosignal(source, "source_create", NULL); source->flags = source->default_flags; source->enabled = true; + + if (!private) { + obs_source_dosignal(source, "source_create", NULL); + } + return source; fail: @@ -427,9 +433,16 @@ static void duplicate_filters(obs_source_t *dst, obs_source_t *src, void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src) { + if (!obs_source_valid(dst, "obs_source_copy_filters")) + return; + if (!obs_source_valid(src, "obs_source_copy_filters")) + return; + duplicate_filters(dst, src, dst->context.private); } +extern obs_scene_t *obs_group_from_source(const obs_source_t *source); + obs_source_t *obs_source_duplicate(obs_source_t *source, const char *new_name, bool create_private) { @@ -446,6 +459,11 @@ obs_source_t *obs_source_duplicate(obs_source_t *source, if (source->info.type == OBS_SOURCE_TYPE_SCENE) { obs_scene_t *scene = obs_scene_from_source(source); + if (!scene) + scene = obs_group_from_source(source); + if (!scene) + return NULL; + obs_scene_t *new_scene = obs_scene_duplicate(scene, new_name, create_private ? OBS_SCENE_DUP_PRIVATE_COPY : OBS_SCENE_DUP_COPY); @@ -1720,9 +1738,18 @@ static inline void obs_source_render_async_video(obs_source_t *source) static inline void obs_source_render_filters(obs_source_t *source) { + obs_source_t *first_filter; + + pthread_mutex_lock(&source->filter_mutex); + first_filter = source->filters.array[0]; + obs_source_addref(first_filter); + pthread_mutex_unlock(&source->filter_mutex); + source->rendering_filter = true; - obs_source_video_render(source->filters.array[0]); + obs_source_video_render(first_filter); source->rendering_filter = false; + + obs_source_release(first_filter); } void obs_source_default_render(obs_source_t *source) @@ -1810,11 +1837,12 @@ void obs_source_video_render(obs_source_t *source) static uint32_t get_base_width(const obs_source_t *source) { bool is_filter = !!source->filter_parent; + bool func_valid = source->context.data && source->info.get_width; if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) { return source->enabled ? source->transition_actual_cx : 0; - } else if (source->info.get_width && (!is_filter || source->enabled)) { + } else if (func_valid && (!is_filter || source->enabled)) { return source->info.get_width(source->context.data); } else if (is_filter) { @@ -1827,11 +1855,12 @@ static uint32_t get_base_width(const obs_source_t *source) static uint32_t get_base_height(const obs_source_t *source) { bool is_filter = !!source->filter_parent; + bool func_valid = source->context.data && source->info.get_height; if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) { return source->enabled ? source->transition_actual_cy : 0; - } else if (source->info.get_height && (!is_filter || source->enabled)) { + } else if (func_valid && (!is_filter || source->enabled)) { return source->info.get_height(source->context.data); } else if (is_filter) { @@ -2270,6 +2299,12 @@ static void copy_frame_data(struct obs_source_frame *dst, } } +void obs_source_frame_copy(struct obs_source_frame *dst, + const struct obs_source_frame *src) +{ + copy_frame_data(dst, src); +} + static inline bool async_texture_changed(struct obs_source *source, const struct obs_source_frame *frame) { @@ -2311,7 +2346,7 @@ static void clean_cache(obs_source_t *source) } #define MAX_ASYNC_FRAMES 30 - +//if return value is not null then do (os_atomic_dec_long(&output->refs) == 0) && obs_source_frame_destroy(output) static inline struct obs_source_frame *cache_video(struct obs_source *source, const struct obs_source_frame *frame) { @@ -2368,11 +2403,6 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source, copy_frame_data(new_frame, frame); - if (os_atomic_dec_long(&new_frame->refs) == 0) { - obs_source_frame_destroy(new_frame); - new_frame = NULL; - } - return new_frame; } @@ -2391,13 +2421,17 @@ void obs_source_output_video(obs_source_t *source, cache_video(source, frame) : NULL; /* ------------------------------------------- */ - + pthread_mutex_lock(&source->async_mutex); if (output) { - pthread_mutex_lock(&source->async_mutex); - da_push_back(source->async_frames, &output); - pthread_mutex_unlock(&source->async_mutex); - source->async_active = true; + if (os_atomic_dec_long(&output->refs) == 0) { + obs_source_frame_destroy(output); + output = NULL; + } else { + da_push_back(source->async_frames, &output); + source->async_active = true; + } } + pthread_mutex_unlock(&source->async_mutex); } static inline bool preload_frame_changed(obs_source_t *source, @@ -2558,6 +2592,37 @@ static void downmix_to_mono_planar(struct obs_source *source, uint32_t frames) } } +static void process_audio_balancing(struct obs_source *source, uint32_t frames, + float balance, enum obs_balance_type type) +{ + float **data = (float**)source->audio_data.data; + + switch(type) { + case OBS_BALANCE_TYPE_SINE_LAW: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * + sinf((1.0f - balance) * (M_PI/2.0f)); + data[1][frame] = data[1][frame] * + sinf(balance * (M_PI/2.0f)); + } + break; + case OBS_BALANCE_TYPE_SQUARE_LAW: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * sqrtf(1.0f - balance); + data[1][frame] = data[1][frame] * sqrtf(balance); + } + break; + case OBS_BALANCE_TYPE_LINEAR: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * (1.0f - balance); + data[1][frame] = data[1][frame] * balance; + } + break; + default: + break; + } +} + /* resamples/remixes new audio to the designated main audio output format */ static void process_audio(obs_source_t *source, const struct obs_source_audio *audio) @@ -2591,6 +2656,13 @@ static void process_audio(obs_source_t *source, mono_output = audio_output_get_channels(obs->audio.audio) == 1; + if ((!mono_output && source->sample_info.speakers == SPEAKERS_STEREO) || + !(source->balance <= 0.51f && + source->balance >= 0.49f)) { + process_audio_balancing(source, frames, source->balance, + OBS_BALANCE_TYPE_SINE_LAW); + } + if (!mono_output && (source->flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0) downmix_to_mono_planar(source, frames); } @@ -4134,3 +4206,25 @@ EXPORT void obs_enable_source_type(const char *name, bool enable) else info->output_flags |= OBS_SOURCE_CAP_DISABLED; } + +enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source) +{ + if (!obs_source_valid(source, "obs_source_get_audio_channels")) + return SPEAKERS_UNKNOWN; + + return source->sample_info.speakers; +} + +void obs_source_set_balance_value(obs_source_t *source, float balance) +{ + if (!obs_source_valid(source, "obs_source_set_balance_value")) + return; + + source->balance = balance; +} + +float obs_source_get_balance_value(const obs_source_t *source) +{ + return obs_source_valid(source, "obs_source_get_balance_value") ? + source->balance : 0.5f; +} diff --git a/libobs/obs-source.h b/libobs/obs-source.h index 04215e95b..fdda90dc7 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -38,6 +38,11 @@ enum obs_source_type { OBS_SOURCE_TYPE_SCENE, }; +enum obs_balance_type { + OBS_BALANCE_TYPE_SINE_LAW, + OBS_BALANCE_TYPE_SQUARE_LAW, + OBS_BALANCE_TYPE_LINEAR, +}; /** * @name Source output flags diff --git a/libobs/obs-video.c b/libobs/obs-video.c index d7641ad75..aaae79abe 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -334,8 +334,8 @@ static inline void stage_output_texture(struct obs_core_video *video, profile_end(stage_output_texture_name); } -static inline void render_video(struct obs_core_video *video, int cur_texture, - int prev_texture) +static inline void render_video(struct obs_core_video *video, bool raw_active, + int cur_texture, int prev_texture) { gs_begin_scene(); @@ -343,11 +343,14 @@ static inline void render_video(struct obs_core_video *video, int cur_texture, gs_set_cull_mode(GS_NEITHER); render_main_texture(video, cur_texture); - render_output_texture(video, cur_texture, prev_texture); - if (video->gpu_conversion) - render_convert_texture(video, cur_texture, prev_texture); - stage_output_texture(video, cur_texture, prev_texture); + if (raw_active) { + render_output_texture(video, cur_texture, prev_texture); + if (video->gpu_conversion) + render_convert_texture(video, cur_texture, prev_texture); + + stage_output_texture(video, cur_texture, prev_texture); + } gs_set_render_target(NULL, NULL); gs_enable_blending(true); @@ -522,7 +525,7 @@ static inline void output_video_data(struct obs_core_video *video, } } -static inline void video_sleep(struct obs_core_video *video, +static inline void video_sleep(struct obs_core_video *video, bool active, uint64_t *p_time, uint64_t interval_ns) { struct obs_vframe_info vframe_info; @@ -543,8 +546,9 @@ static inline void video_sleep(struct obs_core_video *video, vframe_info.timestamp = cur_time; vframe_info.count = count; - circlebuf_push_back(&video->vframe_info_buffer, &vframe_info, - sizeof(vframe_info)); + if (active) + circlebuf_push_back(&video->vframe_info_buffer, &vframe_info, + sizeof(vframe_info)); } static const char *output_frame_gs_context_name = "gs_context(video->graphics)"; @@ -552,7 +556,7 @@ static const char *output_frame_render_video_name = "render_video"; static const char *output_frame_download_frame_name = "download_frame"; static const char *output_frame_gs_flush_name = "gs_flush"; static const char *output_frame_output_video_data_name = "output_video_data"; -static inline void output_frame(void) +static inline void output_frame(bool raw_active) { struct obs_core_video *video = &obs->video; int cur_texture = video->cur_texture; @@ -566,12 +570,14 @@ static inline void output_frame(void) gs_enter_context(video->graphics); profile_start(output_frame_render_video_name); - render_video(video, cur_texture, prev_texture); + render_video(video, raw_active, cur_texture, prev_texture); profile_end(output_frame_render_video_name); - profile_start(output_frame_download_frame_name); - frame_ready = download_frame(video, prev_texture, &frame); - profile_end(output_frame_download_frame_name); + if (raw_active) { + profile_start(output_frame_download_frame_name); + frame_ready = download_frame(video, prev_texture, &frame); + profile_end(output_frame_download_frame_name); + } profile_start(output_frame_gs_flush_name); gs_flush(); @@ -580,7 +586,7 @@ static inline void output_frame(void) gs_leave_context(); profile_end(output_frame_gs_context_name); - if (frame_ready) { + if (raw_active && frame_ready) { struct obs_vframe_info vframe_info; circlebuf_pop_front(&video->vframe_info_buffer, &vframe_info, sizeof(vframe_info)); @@ -597,6 +603,17 @@ static inline void output_frame(void) #define NBSP "\xC2\xA0" +static void clear_frame_data(void) +{ + struct obs_core_video *video = &obs->video; + memset(video->textures_rendered, 0, sizeof(video->textures_rendered)); + memset(video->textures_output, 0, sizeof(video->textures_output)); + memset(video->textures_copied, 0, sizeof(video->textures_copied)); + memset(video->textures_converted, 0, sizeof(video->textures_converted)); + circlebuf_free(&video->vframe_info_buffer); + video->cur_texture = 0; +} + static const char *tick_sources_name = "tick_sources"; static const char *render_displays_name = "render_displays"; static const char *output_frame_name = "output_frame"; @@ -607,6 +624,7 @@ void *obs_graphics_thread(void *param) uint64_t frame_time_total_ns = 0; uint64_t fps_total_ns = 0; uint32_t fps_total_frames = 0; + bool raw_was_active = false; obs->video.video_time = os_gettime_ns(); @@ -622,6 +640,11 @@ void *obs_graphics_thread(void *param) while (!video_output_stopped(obs->video.video)) { uint64_t frame_start = os_gettime_ns(); uint64_t frame_time_ns; + bool raw_active = obs->video.raw_active > 0; + + if (!raw_was_active && raw_active) + clear_frame_data(); + raw_was_active = raw_active; profile_start(video_thread_name); @@ -630,7 +653,7 @@ void *obs_graphics_thread(void *param) profile_end(tick_sources_name); profile_start(output_frame_name); - output_frame(); + output_frame(raw_active); profile_end(output_frame_name); profile_start(render_displays_name); @@ -643,7 +666,8 @@ void *obs_graphics_thread(void *param) profile_reenable_thread(); - video_sleep(&obs->video, &obs->video.video_time, interval); + video_sleep(&obs->video, raw_active, &obs->video.video_time, + interval); frame_time_total_ns += frame_time_ns; fps_total_ns += (obs->video.video_time - last_time); diff --git a/libobs/obs-win-crash-handler.c b/libobs/obs-win-crash-handler.c index 41987dd7c..33ffff91d 100644 --- a/libobs/obs-win-crash-handler.c +++ b/libobs/obs-win-crash-handler.c @@ -255,10 +255,16 @@ static inline void write_header(struct exception_handler_data *data) ts = *localtime(&now); strftime(date_time, sizeof(date_time), "%Y-%m-%d, %X", &ts); + const char *obs_bitness; + if (sizeof(void*) == 8) + obs_bitness = "64"; + else + obs_bitness = "32"; + dstr_catf(&data->str, "Unhandled exception: %x\r\n" "Date/Time: %s\r\n" "Fault address: %"PRIX64" (%s)\r\n" - "libobs version: "OBS_VERSION"\r\n" + "libobs version: "OBS_VERSION" (%s-bit)\r\n" "Windows version: %d.%d build %d (revision: %d; " "%s-bit)\r\n" "CPU: %s\r\n\r\n", @@ -266,6 +272,7 @@ static inline void write_header(struct exception_handler_data *data) date_time, data->main_trace.instruction_ptr, data->module_name.array, + obs_bitness, data->win_version.major, data->win_version.minor, data->win_version.build, data->win_version.revis, is_64_bit_windows() ? "64" : "32", diff --git a/libobs/obs-windows.c b/libobs/obs-windows.c index 75a927868..e6bbcb0bb 100644 --- a/libobs/obs-windows.c +++ b/libobs/obs-windows.c @@ -788,6 +788,13 @@ void reset_win32_symbol_paths(void) da_free(paths); } +extern void initialize_crash_handler(void); + +void obs_init_win32_crash_handler(void) +{ + initialize_crash_handler(); +} + void initialize_com(void) { CoInitializeEx(0, COINIT_MULTITHREADED); diff --git a/libobs/obs.c b/libobs/obs.c index f37d85462..ff9e08b28 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -218,7 +218,7 @@ static bool obs_init_textures(struct obs_video_info *ovi) gs_effect_t *obs_load_effect(gs_effect_t **effect, const char *file) { if (!*effect) { - char *filename = find_libobs_data_file(file); + char *filename = obs_find_data_file(file); *effect = gs_effect_create_from_file(filename, NULL); bfree(filename); } @@ -250,49 +250,49 @@ static int obs_init_graphics(struct obs_video_info *ovi) gs_enter_context(video->graphics); - char *filename = find_libobs_data_file("default.effect"); + char *filename = obs_find_data_file("default.effect"); video->default_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); if (gs_get_device_type() == GS_DEVICE_OPENGL) { - filename = find_libobs_data_file("default_rect.effect"); + filename = obs_find_data_file("default_rect.effect"); video->default_rect_effect = gs_effect_create_from_file( filename, NULL); bfree(filename); } - filename = find_libobs_data_file("opaque.effect"); + filename = obs_find_data_file("opaque.effect"); video->opaque_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); - filename = find_libobs_data_file("solid.effect"); + filename = obs_find_data_file("solid.effect"); video->solid_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); - filename = find_libobs_data_file("format_conversion.effect"); + filename = obs_find_data_file("format_conversion.effect"); video->conversion_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); - filename = find_libobs_data_file("bicubic_scale.effect"); + filename = obs_find_data_file("bicubic_scale.effect"); video->bicubic_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); - filename = find_libobs_data_file("lanczos_scale.effect"); + filename = obs_find_data_file("lanczos_scale.effect"); video->lanczos_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); - filename = find_libobs_data_file("bilinear_lowres_scale.effect"); + filename = obs_find_data_file("bilinear_lowres_scale.effect"); video->bilinear_lowres_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); - filename = find_libobs_data_file("premultiplied_alpha.effect"); + filename = obs_find_data_file("premultiplied_alpha.effect"); video->premultiplied_alpha_effect = gs_effect_create_from_file(filename, NULL); bfree(filename); @@ -565,6 +565,7 @@ static bool obs_init_data(void) if (!obs_view_init(&data->main_view)) goto fail; + data->private_data = obs_data_create(); data->valid = true; fail: @@ -620,6 +621,7 @@ static void obs_free_data(void) pthread_mutex_destroy(&data->draw_callbacks_mutex); da_free(data->draw_callbacks); da_free(data->tick_callbacks); + obs_data_release(data->private_data); } static const char *obs_signals[] = { @@ -741,6 +743,7 @@ static inline void obs_free_hotkeys(void) } extern const struct obs_source_info scene_info; +extern const struct obs_source_info group_info; extern void log_system_info(void); @@ -771,16 +774,60 @@ static bool obs_init(const char *locale, const char *module_config_path, obs->module_config_path = bstrdup(module_config_path); obs->locale = bstrdup(locale); obs_register_source(&scene_info); + obs_register_source(&group_info); add_default_module_paths(); return true; } #ifdef _WIN32 -extern void initialize_crash_handler(void); extern void initialize_com(void); extern void uninitialize_com(void); #endif +/* Separate from actual context initialization + * since this can be set before startup and persist + * after shutdown. */ +static DARRAY(struct dstr) core_module_paths = {0}; + +char *obs_find_data_file(const char *file) +{ + struct dstr path = {0}; + + char *result = find_libobs_data_file(file); + if (result) + return result; + + for (size_t i = 0; i < core_module_paths.num; ++i) { + if (check_path(file, core_module_paths.array[i].array, &path)) + return path.array; + } + + dstr_free(&path); + return NULL; +} + +void obs_add_data_path(const char *path) +{ + struct dstr *new_path = da_push_back_new(core_module_paths); + dstr_init_copy(new_path, path); + da_push_back(core_module_paths, new_path); +} + +bool obs_remove_data_path(const char *path) +{ + for (size_t i = 0; i < core_module_paths.num; ++i) { + int result = dstr_cmp(&core_module_paths.array[i], path); + + if (result == 0) { + dstr_free(&core_module_paths.array[i]); + da_erase(core_module_paths, i); + return true; + } + } + + return false; +} + static const char *obs_startup_name = "obs_startup"; bool obs_startup(const char *locale, const char *module_config_path, profiler_name_store_t *store) @@ -795,7 +842,6 @@ bool obs_startup(const char *locale, const char *module_config_path, } #ifdef _WIN32 - initialize_crash_handler(); initialize_com(); #endif @@ -807,6 +853,42 @@ bool obs_startup(const char *locale, const char *module_config_path, return success; } +static struct obs_cmdline_args cmdline_args = {0, NULL}; +void obs_set_cmdline_args(int argc, char **argv) +{ + char *data; + size_t len; + int i; + + /* Once argc is set (non-zero) we shouldn't call again */ + if (cmdline_args.argc) + return; + + cmdline_args.argc = argc; + + /* Safely copy over argv */ + len = 0; + for (i = 0; i < argc; i++) + len += strlen(argv[i]) + 1; + + cmdline_args.argv = bmalloc(sizeof(char *) * (argc + 1) + len); + data = (char *) cmdline_args.argv + sizeof(char *) * (argc + 1); + + for (i = 0; i < argc; i++) { + cmdline_args.argv[i] = data; + len = strlen(argv[i]) + 1; + memcpy(data, argv[i], len); + data += len; + } + + cmdline_args.argv[argc] = NULL; +} + +struct obs_cmdline_args obs_get_cmdline_args(void) +{ + return cmdline_args; +} + void obs_shutdown(void) { struct obs_module *module; @@ -872,6 +954,7 @@ void obs_shutdown(void) bfree(core->module_config_path); bfree(core->locale); bfree(core); + bfree(cmdline_args.argv); #ifdef _WIN32 uninitialize_com(); @@ -1262,10 +1345,39 @@ void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param) obs_source_t *next_source = (obs_source_t*)source->context.next; - if ((source->info.type == OBS_SOURCE_TYPE_INPUT) != 0 && + if (source->info.id == group_info.id && + !enum_proc(param, source)) { + break; + } else if (source->info.type == OBS_SOURCE_TYPE_INPUT && + !source->context.private && + !enum_proc(param, source)) { + break; + } + + source = next_source; + } + + pthread_mutex_unlock(&obs->data.sources_mutex); +} + +void obs_enum_scenes(bool (*enum_proc)(void*, obs_source_t*), void *param) +{ + obs_source_t *source; + + if (!obs) return; + + pthread_mutex_lock(&obs->data.sources_mutex); + source = obs->data.first_source; + + while (source) { + obs_source_t *next_source = + (obs_source_t*)source->context.next; + + if (source->info.type == OBS_SOURCE_TYPE_SCENE && !source->context.private && - !enum_proc(param, source)) + !enum_proc(param, source)) { break; + } source = next_source; } @@ -1468,6 +1580,23 @@ void obs_render_main_texture(void) gs_draw_sprite(tex, 0, 0, 0); } +gs_texture_t *obs_get_main_texture(void) +{ + struct obs_core_video *video = &obs->video; + int last_tex; + + if (!obs) return NULL; + + last_tex = video->cur_texture == 0 + ? NUM_TEXTURES - 1 + : video->cur_texture - 1; + + if (!video->textures_rendered[last_tex]) + return NULL; + + return video->render_textures[last_tex]; +} + void obs_set_master_volume(float volume) { struct calldata data = {0}; @@ -1496,6 +1625,7 @@ static obs_source_t *obs_load_source_type(obs_data_t *source_data) obs_data_t *settings = obs_data_get_obj(source_data, "settings"); obs_data_t *hotkeys = obs_data_get_obj(source_data, "hotkeys"); double volume; + double balance; int64_t sync; uint32_t flags; uint32_t mixers; @@ -1511,6 +1641,10 @@ static obs_source_t *obs_load_source_type(obs_data_t *source_data) volume = obs_data_get_double(source_data, "volume"); obs_source_set_volume(source, (float)volume); + obs_data_set_default_double(source_data, "balance", 0.5); + balance = obs_data_get_double(source_data, "balance"); + obs_source_set_balance_value(source, (float)balance); + sync = obs_data_get_int(source_data, "sync"); obs_source_set_sync_offset(source, sync); @@ -1627,7 +1761,8 @@ void obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb, if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) obs_transition_load(source, source_data); obs_source_load(source); - cb(private_data, source); + if (cb) + cb(private_data, source); } obs_data_release(source_data); } @@ -1648,6 +1783,7 @@ obs_data_t *obs_save_source(obs_source_t *source) obs_data_t *hotkey_data = source->context.hotkey_data; obs_data_t *hotkeys; float volume = obs_source_get_volume(source); + float balance = obs_source_get_balance_value(source); uint32_t mixers = obs_source_get_audio_mixers(source); int64_t sync = obs_source_get_sync_offset(source); uint32_t flags = obs_source_get_flags(source); @@ -1680,6 +1816,7 @@ obs_data_t *obs_save_source(obs_source_t *source) obs_data_set_int (source_data, "sync", sync); obs_data_set_int (source_data, "flags", flags); obs_data_set_double(source_data, "volume", volume); + obs_data_set_double(source_data, "balance", balance); obs_data_set_bool (source_data, "enabled", enabled); obs_data_set_bool (source_data, "muted", muted); obs_data_set_bool (source_data, "push-to-mute", push_to_mute); @@ -1950,7 +2087,7 @@ bool obs_set_audio_monitoring_device(const char *name, const char *id) if (!obs || !name || !id || !*name || !*id) return false; -#if defined(_WIN32) || HAVE_PULSEAUDIO +#if defined(_WIN32) || HAVE_PULSEAUDIO || defined(__APPLE__) pthread_mutex_lock(&obs->audio.monitoring_mutex); if (strcmp(id, obs->audio.monitoring_device_id) == 0) { @@ -2054,3 +2191,70 @@ uint32_t obs_get_lagged_frames(void) { return obs ? obs->video.lagged_frames : 0; } + +void start_raw_video(video_t *v, const struct video_scale_info *conversion, + void (*callback)(void *param, struct video_data *frame), + void *param) +{ + struct obs_core_video *video = &obs->video; + os_atomic_inc_long(&video->raw_active); + video_output_connect(v, conversion, callback, param); +} + +void stop_raw_video(video_t *v, + void (*callback)(void *param, struct video_data *frame), + void *param) +{ + struct obs_core_video *video = &obs->video; + os_atomic_dec_long(&video->raw_active); + video_output_disconnect(v, callback, param); +} + +void obs_add_raw_video_callback( + const struct video_scale_info *conversion, + void (*callback)(void *param, struct video_data *frame), + void *param) +{ + struct obs_core_video *video = &obs->video; + if (!obs) + return; + start_raw_video(video->video, conversion, callback, param); +} + +void obs_remove_raw_video_callback( + void (*callback)(void *param, struct video_data *frame), + void *param) +{ + struct obs_core_video *video = &obs->video; + if (!obs) + return; + stop_raw_video(video->video, callback, param); +} + +void obs_apply_private_data(obs_data_t *settings) +{ + if (!obs || !settings) + return; + + obs_data_apply(obs->data.private_data, settings); +} + +void obs_set_private_data(obs_data_t *settings) +{ + if (!obs) + return; + + obs_data_clear(obs->data.private_data); + if (settings) + obs_data_apply(obs->data.private_data, settings); +} + +obs_data_t *obs_get_private_data(void) +{ + if (!obs) + return NULL; + + obs_data_t *private_data = obs->data.private_data; + obs_data_addref(private_data); + return private_data; +} diff --git a/libobs/obs.h b/libobs/obs.h index 2b221a631..bd40e696a 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -239,9 +239,41 @@ struct obs_source_frame { bool prev_frame; }; +/** Access to the argc/argv used to start OBS. What you see is what you get. */ +struct obs_cmdline_args { + int argc; + char **argv; +}; + /* ------------------------------------------------------------------------- */ /* OBS context */ +/** + * Find a core libobs data file + * @param path name of the base file + * @return A string containing the full path to the file. + * Use bfree after use. + */ +EXPORT char *obs_find_data_file(const char *file); + +/** + * Add a path to search libobs data files in. + * @param path Full path to directory to look in. + * The string is copied. + */ +EXPORT void obs_add_data_path(const char *path); + +/** + * Remove a path from libobs core data paths. + * @param path The path to compare to currently set paths. + * It does not need to be the same pointer, but + * the path string must match an entry fully. + * @return Whether or not the path was successfully removed. + * If false, the path could not be found. + */ +EXPORT bool obs_remove_data_path(const char *path); + + /** * Initializes OBS * @@ -265,6 +297,24 @@ EXPORT uint32_t obs_get_version(void); /** @return The current core version string */ EXPORT const char *obs_get_version_string(void); +/** + * Sets things up for calls to obs_get_cmdline_args. Called onl yonce at startup + * and safely copies argv/argc from main(). Subsequent calls do nothing. + * + * @param argc The count of command line arguments, from main() + * @param argv An array of command line arguments, copied from main() and ends + * with NULL. + */ +EXPORT void obs_set_cmdline_args(int argc, char **argv); + +/** + * Get the argc/argv used to start OBS + * + * @return The command line arguments used for main(). Don't modify this or + * you'll mess things up for other callers. + */ +EXPORT struct obs_cmdline_args obs_get_cmdline_args(void); + /** * Sets a new locale to use for modules. This will call obs_module_set_locale * for each module with the new locale. @@ -276,6 +326,12 @@ EXPORT void obs_set_locale(const char *locale); /** @return the current locale */ EXPORT const char *obs_get_locale(void); +/** Initialize the Windows-specific crash handler */ + +#ifdef _WIN32 +EXPORT void obs_init_win32_crash_handler(void); +#endif + /** * Returns the profiler name store (see util/profiler.h) used by OBS, which is * either a name store passed to obs_startup, an internal name store, or NULL @@ -500,6 +556,10 @@ EXPORT obs_source_t *obs_get_output_source(uint32_t channel); EXPORT void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param); +/** Enumerates scenes */ +EXPORT void obs_enum_scenes(bool (*enum_proc)(void*, obs_source_t*), + void *param); + /** Enumerates outputs */ EXPORT void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t*), void *param); @@ -564,6 +624,10 @@ EXPORT void obs_render_main_view(void); /** Renders the last main output texture */ EXPORT void obs_render_main_texture(void); +/** Returns the last main output texture. This can return NULL if the texture + * is unavailable. */ +EXPORT gs_texture_t *obs_get_main_texture(void); + /** Sets the master user volume */ EXPORT void obs_set_master_volume(float volume); @@ -576,6 +640,12 @@ EXPORT obs_data_t *obs_save_source(obs_source_t *source); /** Loads a source from settings data */ EXPORT obs_source_t *obs_load_source(obs_data_t *data); +/** Send a save signal to sources */ +EXPORT void obs_source_save(obs_source_t *source); + +/** Send a load signal to sources */ +EXPORT void obs_source_load(obs_source_t *source); + typedef void (*obs_load_source_cb)(void *private_data, obs_source_t *source); /** Loads sources from a data array */ @@ -624,6 +694,26 @@ EXPORT void obs_remove_main_render_callback( void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param); +EXPORT void obs_add_raw_video_callback( + const struct video_scale_info *conversion, + void (*callback)(void *param, struct video_data *frame), + void *param); +EXPORT void obs_remove_raw_video_callback( + void (*callback)(void *param, struct video_data *frame), + void *param); + +EXPORT uint64_t obs_get_video_frame_time(void); + +EXPORT double obs_get_active_fps(void); +EXPORT uint64_t obs_get_average_frame_time_ns(void); + +EXPORT uint32_t obs_get_total_frames(void); +EXPORT uint32_t obs_get_lagged_frames(void); + +EXPORT void obs_apply_private_data(obs_data_t *settings); +EXPORT void obs_set_private_data(obs_data_t *settings); +EXPORT obs_data_t *obs_get_private_data(void); + /* ------------------------------------------------------------------------- */ /* View context */ @@ -650,14 +740,6 @@ EXPORT obs_source_t *obs_view_get_source(obs_view_t *view, /** Renders the sources of this view context */ EXPORT void obs_view_render(obs_view_t *view); -EXPORT uint64_t obs_get_video_frame_time(void); - -EXPORT double obs_get_active_fps(void); -EXPORT uint64_t obs_get_average_frame_time_ns(void); - -EXPORT uint32_t obs_get_total_frames(void); -EXPORT uint32_t obs_get_lagged_frames(void); - /* ------------------------------------------------------------------------- */ /* Display context */ @@ -670,7 +752,8 @@ EXPORT uint32_t obs_get_lagged_frames(void); * @return The new display context, or NULL if failed. */ EXPORT obs_display_t *obs_display_create( - const struct gs_init_data *graphics_data); + const struct gs_init_data *graphics_data, + uint32_t backround_color); /** Destroys a display context */ EXPORT void obs_display_destroy(obs_display_t *display); @@ -839,6 +922,15 @@ EXPORT void obs_source_set_volume(obs_source_t *source, float volume); /** Gets the user volume for a source that has audio output */ EXPORT float obs_source_get_volume(const obs_source_t *source); +/* Gets speaker layout of a source */ +EXPORT enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source); + +/** Sets the balance value for a stereo audio source */ +EXPORT void obs_source_set_balance_value(obs_source_t *source, float balance); + +/** Gets the balance value for a stereo audio source */ +EXPORT float obs_source_get_balance_value(const obs_source_t *source); + /** Sets the audio sync offset (in nanoseconds) for a source */ EXPORT void obs_source_set_sync_offset(obs_source_t *source, int64_t offset); @@ -1275,6 +1367,15 @@ EXPORT void obs_scene_enum_items(obs_scene_t *scene, EXPORT bool obs_scene_reorder_items(obs_scene_t *scene, obs_sceneitem_t * const *item_order, size_t item_order_size); +struct obs_sceneitem_order_info { + obs_sceneitem_t *group; + obs_sceneitem_t *item; +}; + +EXPORT bool obs_scene_reorder_items2(obs_scene_t *scene, + struct obs_sceneitem_order_info *item_order, + size_t item_order_size); + /** Adds/creates a new scene item for a source */ EXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source); @@ -1364,6 +1465,8 @@ EXPORT void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item, EXPORT enum obs_scale_type obs_sceneitem_get_scale_filter( obs_sceneitem_t *item); +EXPORT void obs_sceneitem_force_update_transform(obs_sceneitem_t *item); + EXPORT void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item); EXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item); @@ -1371,6 +1474,39 @@ EXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item); * automatically. Returns an incremented reference. */ EXPORT obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item); +EXPORT obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, + const char *name); +EXPORT obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, + const char *name, obs_sceneitem_t **items, size_t count); + +EXPORT obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, + const char *name); + +EXPORT bool obs_sceneitem_is_group(obs_sceneitem_t *item); + +EXPORT obs_scene_t *obs_sceneitem_group_get_scene( + const obs_sceneitem_t *group); + +EXPORT void obs_sceneitem_group_ungroup(obs_sceneitem_t *group); + +EXPORT void obs_sceneitem_group_add_item(obs_sceneitem_t *group, + obs_sceneitem_t *item); +EXPORT void obs_sceneitem_group_remove_item(obs_sceneitem_t *group, + obs_sceneitem_t *item); + +EXPORT obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene, + obs_sceneitem_t *item); + +EXPORT bool obs_source_is_group(const obs_source_t *source); +EXPORT bool obs_scene_is_group(const obs_scene_t *scene); + +EXPORT void obs_sceneitem_group_enum_items(obs_sceneitem_t *group, + bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), + void *param); + +EXPORT void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item); +EXPORT void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item); + /* ------------------------------------------------------------------------- */ /* Outputs */ @@ -1440,6 +1576,12 @@ EXPORT void obs_output_force_stop(obs_output_t *output); /** Returns whether the output is active */ EXPORT bool obs_output_active(const obs_output_t *output); +/** Returns output capability flags */ +EXPORT uint32_t obs_output_get_flags(const obs_output_t *output); + +/** Returns output capability flags */ +EXPORT uint32_t obs_get_output_flags(const char *id); + /** Gets the default settings for an output type */ EXPORT obs_data_t *obs_output_defaults(const char *id); @@ -1717,6 +1859,7 @@ EXPORT enum video_format obs_encoder_get_preferred_video_format( /** Gets the default settings for an encoder type */ EXPORT obs_data_t *obs_encoder_defaults(const char *id); +EXPORT obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder); /** Returns the property list, if any. Free with obs_properties_destroy */ EXPORT obs_properties_t *obs_get_encoder_properties(const char *id); @@ -1898,6 +2041,8 @@ static inline void obs_source_frame_destroy(struct obs_source_frame *frame) } } +EXPORT void obs_source_frame_copy(struct obs_source_frame *dst, + const struct obs_source_frame *src); #ifdef __cplusplus } diff --git a/libobs/obs.hpp b/libobs/obs.hpp index 11fbe29e4..78780fcdb 100644 --- a/libobs/obs.hpp +++ b/libobs/obs.hpp @@ -211,7 +211,7 @@ class OBSSignal { callback (callback_), param (param_) { - signal_handler_connect(handler, signal, callback, param); + signal_handler_connect_ref(handler, signal, callback, param); } inline void Disconnect() @@ -236,7 +236,7 @@ class OBSSignal { signal = signal_; callback = callback_; param = param_; - signal_handler_connect(handler, signal, callback, param); + signal_handler_connect_ref(handler, signal, callback, param); } OBSSignal(const OBSSignal&) = delete; diff --git a/libobs/obsconfig.h.in b/libobs/obsconfig.h.in index b978bdb04..a959f1482 100644 --- a/libobs/obsconfig.h.in +++ b/libobs/obsconfig.h.in @@ -18,6 +18,24 @@ #define BUILD_CAPTIONS @BUILD_CAPTIONS@ #define HAVE_DBUS @HAVE_DBUS@ #define HAVE_PULSEAUDIO @HAVE_PULSEAUDIO@ +#define USE_XINPUT @USE_XINPUT@ #define LIBOBS_IMAGEMAGICK_DIR_STYLE_6L 6 #define LIBOBS_IMAGEMAGICK_DIR_STYLE_7GE 7 #define LIBOBS_IMAGEMAGICK_DIR_STYLE @LIBOBS_IMAGEMAGICK_DIR_STYLE@ + +/* NOTE: Release candidate version numbers internally are always the previous + * main release number! For example, if the current public release is 21.0 and + * the build is 22.0 release candidate 1, internally the build number (defined + * by LIBOBS_API_VER/etc) will always be 21.0, despite the OBS_VERSION string + * saying "22.0 RC1". + * + * If the release candidate version number is 0.0.0 and the RC number is 0, + * that means it's not a release candidate build. */ +#define OBS_RELEASE_CANDIDATE_MAJOR @OBS_RELEASE_CANDIDATE_MAJOR@ +#define OBS_RELEASE_CANDIDATE_MINOR @OBS_RELEASE_CANDIDATE_MINOR@ +#define OBS_RELEASE_CANDIDATE_PATCH @OBS_RELEASE_CANDIDATE_PATCH@ +#define OBS_RELEASE_CANDIDATE_VER \ + MAKE_SEMANTIC_VERSION(OBS_RELEASE_CANDIDATE_MAJOR, \ + OBS_RELEASE_CANDIDATE_MINOR, \ + OBS_RELEASE_CANDIDATE_PATCH) +#define OBS_RELEASE_CANDIDATE @OBS_RELEASE_CANDIDATE@ diff --git a/libobs/util/apple/cfstring-utils.h b/libobs/util/apple/cfstring-utils.h new file mode 100644 index 000000000..d131fbb48 --- /dev/null +++ b/libobs/util/apple/cfstring-utils.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../c99defs.h" +#include "../dstr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +EXPORT char *cfstr_copy_cstr(CFStringRef cfstr, CFStringEncoding cfstr_enc); + +EXPORT bool cfstr_copy_dstr(CFStringRef cfstr, CFStringEncoding cfstr_enc, + struct dstr *str); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/util/cf-lexer.c b/libobs/util/cf-lexer.c index 8258582ce..0a8dd5c6e 100644 --- a/libobs/util/cf-lexer.c +++ b/libobs/util/cf-lexer.c @@ -76,11 +76,12 @@ char *cf_literal_to_str(const char *literal, size_t count) if (literal[0] != '\"' && literal[0] != '\'') return NULL; - str = bmalloc(count - 1); - temp_src = literal; + /* strip leading and trailing quote characters */ + str = bzalloc(--count); + temp_src = literal + 1; temp_dst = str; - while (*temp_src) { + while (*temp_src && --count > 0) { if (*temp_src == '\\') { temp_src++; cf_convert_from_escape_literal(&temp_dst, &temp_src); diff --git a/libobs/util/darray.h b/libobs/util/darray.h index 99f35d69f..69e1c40a2 100644 --- a/libobs/util/darray.h +++ b/libobs/util/darray.h @@ -207,11 +207,13 @@ static inline void *darray_push_back_new(const size_t element_size, static inline size_t darray_push_back_array(const size_t element_size, struct darray *dst, const void *array, const size_t num) { - size_t old_num = dst->num; - - assert(array != NULL); - assert(num != 0); + size_t old_num; + if (!dst) + return 0; + if (!array || !num) + return dst->num; + old_num = dst->num; darray_resize(element_size, dst, dst->num+num); memcpy(darray_item(element_size, dst, old_num), array, element_size*num); diff --git a/libobs/util/pipe-windows.c b/libobs/util/pipe-windows.c index c3cf906d9..d3d35e225 100644 --- a/libobs/util/pipe-windows.c +++ b/libobs/util/pipe-windows.c @@ -50,7 +50,7 @@ static inline bool create_process(const char *cmd_line, HANDLE stdin_handle, bool success = false; si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES; + si.dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK; si.hStdInput = stdin_handle; si.hStdOutput = stdout_handle; diff --git a/libobs/util/platform-cocoa.m b/libobs/util/platform-cocoa.m index f93341cc0..de48cdb0c 100644 --- a/libobs/util/platform-cocoa.m +++ b/libobs/util/platform-cocoa.m @@ -1,6 +1,7 @@ /* - * Copyright (c) 2013-2014 Ruwen Hahn + * Copyright (c) 2013-2018 Ruwen Hahn * Hugh "Jim" Bailey + * Marvin Scholz * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,6 +34,8 @@ #import +#include "apple/cfstring-utils.h" + /* clock function selection taken from libc++ */ static uint64_t ns_time_simple() { @@ -417,3 +420,90 @@ uint64_t os_get_proc_virtual_size(void) return 0; return taskinfo.virtual_size; } + +/* Obtains a copy of the contents of a CFString in specified encoding. + * Returns char* (must be bfree'd by caller) or NULL on failure. + */ +char *cfstr_copy_cstr(CFStringRef cfstring, CFStringEncoding cfstring_encoding) +{ + if (!cfstring) + return NULL; + + // Try the quick way to obtain the buffer + const char *tmp_buffer = CFStringGetCStringPtr(cfstring, + cfstring_encoding); + + if (tmp_buffer != NULL) + return bstrdup(tmp_buffer); + + // The quick way did not work, try the more expensive one + CFIndex length = CFStringGetLength(cfstring); + CFIndex max_size = + CFStringGetMaximumSizeForEncoding(length, cfstring_encoding); + + // If result would exceed LONG_MAX, kCFNotFound is returned + if (max_size == kCFNotFound) + return NULL; + + // Account for the null terminator + max_size++; + + char *buffer = bmalloc(max_size); + + if (buffer == NULL) { + return NULL; + } + + // Copy CFString in requested encoding to buffer + Boolean success = + CFStringGetCString(cfstring, buffer, max_size, cfstring_encoding); + + if (!success) { + bfree(buffer); + buffer = NULL; + } + return buffer; +} + +/* Copies the contents of a CFString in specified encoding to a given dstr. + * Returns true on success or false on failure. + * In case of failure, the dstr capacity but not size is changed. + */ +bool cfstr_copy_dstr(CFStringRef cfstring, + CFStringEncoding cfstring_encoding, struct dstr *str) +{ + if (!cfstring) + return false; + + // Try the quick way to obtain the buffer + const char *tmp_buffer = CFStringGetCStringPtr(cfstring, + cfstring_encoding); + + if (tmp_buffer != NULL) { + dstr_copy(str, tmp_buffer); + return true; + } + + // The quick way did not work, try the more expensive one + CFIndex length = CFStringGetLength(cfstring); + CFIndex max_size = + CFStringGetMaximumSizeForEncoding(length, cfstring_encoding); + + // If result would exceed LONG_MAX, kCFNotFound is returned + if (max_size == kCFNotFound) + return NULL; + + // Account for the null terminator + max_size++; + + dstr_ensure_capacity(str, max_size); + + // Copy CFString in requested encoding to dstr buffer + Boolean success = CFStringGetCString( + cfstring, str->array, max_size, cfstring_encoding); + + if (success) + dstr_resize(str, max_size); + + return (bool)success; +} diff --git a/libobs/util/threading-posix.c b/libobs/util/threading-posix.c index 435ff1b7b..d02e3d308 100644 --- a/libobs/util/threading-posix.c +++ b/libobs/util/threading-posix.c @@ -253,6 +253,12 @@ void os_set_thread_name(const char *name) #elif defined(__FreeBSD__) pthread_set_name_np(pthread_self(), name); #elif defined(__GLIBC__) && !defined(__MINGW32__) - pthread_setname_np(pthread_self(), name); + if (strlen(name) <= 15) { + pthread_setname_np(pthread_self(), name); + } else { + char *thread_name = bstrdup_n(name, 15); + pthread_setname_np(pthread_self(), thread_name); + bfree(thread_name); + } #endif } diff --git a/libobs/util/util_uint128.h b/libobs/util/util_uint128.h index 511ab0a05..ac96f3ee0 100644 --- a/libobs/util/util_uint128.h +++ b/libobs/util/util_uint128.h @@ -51,11 +51,19 @@ static inline util_uint128_t util_add128(util_uint128_t a, util_uint128_t b) return out; } -static inline util_uint128_t util_lshift64(uint64_t a, int num) +static inline util_uint128_t util_lshift64_internal_32(uint64_t a) { util_uint128_t val; - val.low = a << num; - val.high = a >> (64 - num); + val.low = a << 32; + val.high = a >> 32; + return val; +} + +static inline util_uint128_t util_lshift64_internal_64(uint64_t a) +{ + util_uint128_t val; + val.low = 0; + val.high = a; return val; } @@ -69,13 +77,13 @@ static inline util_uint128_t util_mul64_64(uint64_t a, uint64_t b) out.high = 0; m = (a >> 32) * (b & 0xFFFFFFFFULL); - out = util_add128(out, util_lshift64(m, 32)); + out = util_add128(out, util_lshift64_internal_32(m)); m = (a & 0xFFFFFFFFULL) * (b >> 32); - out = util_add128(out, util_lshift64(m, 32)); + out = util_add128(out, util_lshift64_internal_32(m)); m = (a >> 32) * (b >> 32); - out = util_add128(out, util_lshift64(m, 64)); + out = util_add128(out, util_lshift64_internal_64(m)); return out; } diff --git a/package.sh b/package.sh new file mode 100755 index 000000000..9b6fab09b --- /dev/null +++ b/package.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +export QTDIR=/usr/local/Cellar/qt/5.11.1 +./CI/before-script-osx.sh +cd build && make -j 8 && cd .. +./CI/before-deploy-osx.sh +echo "Package created in ./nigthly" \ No newline at end of file diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1e8593046..97957b051 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -53,9 +53,13 @@ if(WIN32 OR APPLE) if (BUILD_BROWSER) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt") add_subdirectory(obs-browser) + set(BROWSER_AVAILABLE_INTERNAL ON CACHE BOOL "Interal global cmake variable" FORCE) else() message(STATUS "obs-browser submodule not found! Please fetch submodules. obs-browser plugin disabled.") + set(BROWSER_AVAILABLE_INTERNAL OFF CACHE BOOL "Interal global cmake variable" FORCE) endif() + else() + set(BROWSER_AVAILABLE_INTERNAL OFF CACHE BOOL "Interal global cmake variable" FORCE) endif() if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-vst/CMakeLists.txt") diff --git a/plugins/coreaudio-encoder/data/locale/fil-PH.ini b/plugins/coreaudio-encoder/data/locale/fil-PH.ini new file mode 100644 index 000000000..48a70802a --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/fil-PH.ini @@ -0,0 +1,6 @@ +CoreAudioAAC="CoreAudio AAC encoder" +Bitrate="Bitrate" +AllowHEAAC="Payagan ang HE-AAC" +OutputSamplerate="Output Sample Rate" +UseInputSampleRate="Gamitin ang Sample Rate ng Input (OBS) (maaaring maglista ng mga hindi suportadong bitrates)" + diff --git a/plugins/coreaudio-encoder/data/locale/gd-GB.ini b/plugins/coreaudio-encoder/data/locale/gd-GB.ini new file mode 100644 index 000000000..99e5ae0f1 --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/gd-GB.ini @@ -0,0 +1,2 @@ +Bitrate="Reat bhiotaichean" + diff --git a/plugins/coreaudio-encoder/data/locale/ka-GE.ini b/plugins/coreaudio-encoder/data/locale/ka-GE.ini new file mode 100644 index 000000000..40d69b508 --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/ka-GE.ini @@ -0,0 +1,6 @@ +CoreAudioAAC="CoreAudio AAC გამშიფრავი" +Bitrate="ბიტური სიხშირე" +AllowHEAAC="HE-AAC დაშვება" +OutputSamplerate="გამომავალი სიგნალის სიხშირე" +UseInputSampleRate="შემავალი (OBS) სიგნალის სიხშირის გამოყენება (შესაძლოა მოიცავდეს მხარდაუჭერელ სიხშირეებსაც)" + diff --git a/plugins/coreaudio-encoder/data/locale/tl-PH.ini b/plugins/coreaudio-encoder/data/locale/tl-PH.ini new file mode 100644 index 000000000..483f3eb64 --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/tl-PH.ini @@ -0,0 +1,6 @@ +CoreAudioAAC="Ang Buod ng Audio AAC encoder" +Bitrate="Baytreyt" +AllowHEAAC="Pahintulutan ang HE-AAC" +OutputSamplerate="Paglabas ng Sample Rate" +UseInputSampleRate="Gamitin ang pangpasok na (OBS) Sample Rate (Maaaring ilista ang mga hindi sinusuportahang bitrates)" + diff --git a/plugins/coreaudio-encoder/encoder.cpp b/plugins/coreaudio-encoder/encoder.cpp index 972376daf..0affe6ed7 100644 --- a/plugins/coreaudio-encoder/encoder.cpp +++ b/plugins/coreaudio-encoder/encoder.cpp @@ -10,6 +10,7 @@ #ifndef _WIN32 #include +#include #endif #define CA_LOG(level, format, ...) \ @@ -257,17 +258,8 @@ static DStr osstatus_to_dstr(OSStatus code) kCFErrorDomainOSStatus, code, NULL)}; cf_ptr str{CFErrorCopyDescription(err.get())}; - CFIndex length = CFStringGetLength(str.get()); - CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, - kCFStringEncodingUTF8); - - dstr_ensure_capacity(result, max_size); - - if (result->array && CFStringGetCString(str.get(), result->array, - max_size, kCFStringEncodingUTF8)) { - dstr_resize(result, strlen(result->array)); + if (cfstr_copy_dstr(str.get(), kCFStringEncodingUTF8, result)) return result; - } #endif const char *code_str = code_to_str(code); @@ -1374,6 +1366,10 @@ static obs_properties_t *aac_properties(void *data) OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("coreaudio-encoder", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Apple CoreAudio based encoder"; +} bool obs_module_load(void) { diff --git a/plugins/decklink/DecklinkBase.cpp b/plugins/decklink/DecklinkBase.cpp new file mode 100644 index 000000000..e916a243c --- /dev/null +++ b/plugins/decklink/DecklinkBase.cpp @@ -0,0 +1,20 @@ +#include "DecklinkBase.h" + +DecklinkBase::DecklinkBase(DeckLinkDeviceDiscovery *discovery_) + : discovery(discovery_) +{ +} + +DeckLinkDevice *DecklinkBase::GetDevice() const +{ + return instance ? instance->GetDevice() : nullptr; +} + +bool DecklinkBase::Activate(DeckLinkDevice*, long long) +{ + return false; +} + +void DecklinkBase::Deactivate() +{ +} diff --git a/plugins/decklink/DecklinkBase.h b/plugins/decklink/DecklinkBase.h new file mode 100644 index 000000000..a337092e9 --- /dev/null +++ b/plugins/decklink/DecklinkBase.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include + +#include "platform.hpp" + +#include "decklink-device-discovery.hpp" +#include "decklink-device-instance.hpp" +#include "decklink-device-mode.hpp" + +class DecklinkBase { + +protected: + DecklinkBase(DeckLinkDeviceDiscovery *discovery_); + ComPtr instance; + DeckLinkDeviceDiscovery *discovery; + std::recursive_mutex deviceMutex; + volatile long activateRefs = 0; + BMDPixelFormat pixelFormat = bmdFormat8BitYUV; + video_colorspace colorSpace = VIDEO_CS_DEFAULT; + video_range_type colorRange = VIDEO_RANGE_DEFAULT; + speaker_layout channelFormat = SPEAKERS_STEREO; + +public: + virtual bool Activate(DeckLinkDevice *device, long long modeId); + virtual void Deactivate(); + + DeckLinkDevice *GetDevice() const; +}; diff --git a/plugins/decklink/decklink.cpp b/plugins/decklink/DecklinkInput.cpp similarity index 70% rename from plugins/decklink/decklink.cpp rename to plugins/decklink/DecklinkInput.cpp index ea0ec9632..58687a5c6 100644 --- a/plugins/decklink/decklink.cpp +++ b/plugins/decklink/DecklinkInput.cpp @@ -1,30 +1,23 @@ -#include "decklink.hpp" -#include "decklink-device-discovery.hpp" -#include "decklink-device-instance.hpp" -#include "decklink-device-mode.hpp" +#include "DecklinkInput.hpp" #include -DeckLink::DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_) : - discovery(discovery_), source(source) +DeckLinkInput::DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_) + : DecklinkBase(discovery_), + source(source) { - discovery->AddCallback(DeckLink::DevicesChanged, this); + discovery->AddCallback(DeckLinkInput::DevicesChanged, this); } -DeckLink::~DeckLink(void) +DeckLinkInput::~DeckLinkInput(void) { - discovery->RemoveCallback(DeckLink::DevicesChanged, this); + discovery->RemoveCallback(DeckLinkInput::DevicesChanged, this); Deactivate(); } -DeckLinkDevice *DeckLink::GetDevice() const +void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device, bool added) { - return instance ? instance->GetDevice() : nullptr; -} - -void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added) -{ - DeckLink *decklink = reinterpret_cast(param); + DeckLinkInput *decklink = reinterpret_cast(param); std::lock_guard lock(decklink->deviceMutex); obs_source_update_properties(decklink->source); @@ -54,7 +47,7 @@ void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added) } } -bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) +bool DeckLinkInput::Activate(DeckLinkDevice *device, long long modeId) { std::lock_guard lock(deviceMutex); DeckLinkDevice *curDevice = GetDevice(); @@ -75,13 +68,19 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) if (isActive) instance->StopCapture(); + isCapturing = false; if (!same) instance.Set(new DeckLinkDeviceInstance(this, device)); if (instance == nullptr) return false; - DeckLinkDeviceMode *mode = GetDevice()->FindMode(modeId); + if (GetDevice() == nullptr) { + LOG(LOG_ERROR, "Tried to activate an input with nullptr device."); + return false; + } + + DeckLinkDeviceMode *mode = GetDevice()->FindInputMode(modeId); if (mode == nullptr) { instance = nullptr; return false; @@ -94,20 +93,28 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) os_atomic_inc_long(&activateRefs); SaveSettings(); + id = modeId; + isCapturing = true; return true; } -void DeckLink::Deactivate(void) +void DeckLinkInput::Deactivate(void) { std::lock_guard lock(deviceMutex); if (instance) instance->StopCapture(); + isCapturing = false; instance = nullptr; os_atomic_dec_long(&activateRefs); } -void DeckLink::SaveSettings() +bool DeckLinkInput::Capturing(void) +{ + return isCapturing; +} + +void DeckLinkInput::SaveSettings() { if (!instance) return; @@ -127,7 +134,7 @@ void DeckLink::SaveSettings() obs_data_release(settings); } -obs_source_t *DeckLink::GetSource(void) const +obs_source_t *DeckLinkInput::GetSource(void) const { return source; } diff --git a/plugins/decklink/decklink.hpp b/plugins/decklink/DecklinkInput.hpp similarity index 54% rename from plugins/decklink/decklink.hpp rename to plugins/decklink/DecklinkInput.hpp index 23f8b59b7..4111fa9f9 100644 --- a/plugins/decklink/decklink.hpp +++ b/plugins/decklink/DecklinkInput.hpp @@ -1,40 +1,19 @@ #pragma once -#include "platform.hpp" +#include "DecklinkBase.h" -#include - -#include -#include -#include - -class DeckLinkDeviceDiscovery; -class DeckLinkDeviceInstance; -class DeckLinkDevice; -class DeckLinkDeviceMode; - -class DeckLink { +class DeckLinkInput : public DecklinkBase { protected: - ComPtr instance; - DeckLinkDeviceDiscovery *discovery; bool isCapturing = false; obs_source_t *source; - volatile long activateRefs = 0; - std::recursive_mutex deviceMutex; - BMDPixelFormat pixelFormat = bmdFormat8BitYUV; - video_colorspace colorSpace = VIDEO_CS_DEFAULT; - video_range_type colorRange = VIDEO_RANGE_DEFAULT; - speaker_layout channelFormat = SPEAKERS_STEREO; void SaveSettings(); static void DevicesChanged(void *param, DeckLinkDevice *device, bool added); public: - DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery); - virtual ~DeckLink(void); - - DeckLinkDevice *GetDevice() const; + DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery); + virtual ~DeckLinkInput(void); long long GetActiveModeId(void) const; obs_source_t *GetSource(void) const; @@ -62,6 +41,10 @@ class DeckLink { bool Activate(DeckLinkDevice *device, long long modeId); void Deactivate(); + bool Capturing(); bool buffering = false; + bool dwns = false; + std::string hash; + long long id; }; diff --git a/plugins/decklink/DecklinkOutput.cpp b/plugins/decklink/DecklinkOutput.cpp new file mode 100644 index 000000000..40eaf3377 --- /dev/null +++ b/plugins/decklink/DecklinkOutput.cpp @@ -0,0 +1,110 @@ +#include "DecklinkOutput.hpp" + +#include + +DeckLinkOutput::DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery_) + : DecklinkBase(discovery_), + output(output) +{ + discovery->AddCallback(DeckLinkOutput::DevicesChanged, this); +} + +DeckLinkOutput::~DeckLinkOutput(void) +{ + discovery->RemoveCallback(DeckLinkOutput::DevicesChanged, this); + Deactivate(); +} + +void DeckLinkOutput::DevicesChanged(void *param, DeckLinkDevice *device, bool) +{ + auto *decklink = reinterpret_cast(param); + std::lock_guard lock(decklink->deviceMutex); + + blog(LOG_DEBUG, "%s", device->GetHash().c_str()); +} + +bool DeckLinkOutput::Activate(DeckLinkDevice *device, long long modeId) +{ + std::lock_guard lock(deviceMutex); + DeckLinkDevice *curDevice = GetDevice(); + const bool same = device == curDevice; + const bool isActive = instance != nullptr; + + if (same) { + if (!isActive) + return false; + + if (instance->GetActiveModeId() == modeId && + instance->GetActivePixelFormat() == pixelFormat && + instance->GetActiveColorSpace() == colorSpace && + instance->GetActiveColorRange() == colorRange && + instance->GetActiveChannelFormat() == channelFormat) + return false; + } + + if (isActive) + instance->StopOutput(); + + if (!same) + instance.Set(new DeckLinkDeviceInstance(this, device)); + + if (instance == nullptr) + return false; + + DeckLinkDeviceMode *mode = GetDevice()->FindOutputMode(modeId); + if (mode == nullptr) { + instance = nullptr; + return false; + } + + + if (!instance->StartOutput(mode)) { + instance = nullptr; + return false; + } + + os_atomic_inc_long(&activateRefs); + return true; +} + +void DeckLinkOutput::Deactivate(void) +{ + std::lock_guard lock(deviceMutex); + if (instance) + instance->StopOutput(); + + instance = nullptr; + + os_atomic_dec_long(&activateRefs); +} + +obs_output_t *DeckLinkOutput::GetOutput(void) const +{ + return output; +} + +void DeckLinkOutput::DisplayVideoFrame(video_data *frame) +{ + instance->DisplayVideoFrame(frame); +} + +void DeckLinkOutput::WriteAudio(audio_data *frames) +{ + instance->WriteAudio(frames); +} + +void DeckLinkOutput::SetSize(int width, int height) +{ + this->width = width; + this->height = height; +} + +int DeckLinkOutput::GetWidth() +{ + return width; +} + +int DeckLinkOutput::GetHeight() +{ + return height; +} diff --git a/plugins/decklink/DecklinkOutput.hpp b/plugins/decklink/DecklinkOutput.hpp new file mode 100644 index 000000000..bdd873bca --- /dev/null +++ b/plugins/decklink/DecklinkOutput.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "DecklinkBase.h" + +#include "../../libobs/media-io/video-scaler.h" + +class DeckLinkOutput : public DecklinkBase { +protected: + obs_output_t *output; + int width; + int height; + + static void DevicesChanged(void *param, DeckLinkDevice *device, bool added); + +public: + const char *deviceHash; + long long modeID; + uint64_t start_timestamp; + uint32_t audio_samplerate; + size_t audio_planes; + size_t audio_size; + + DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery); + virtual ~DeckLinkOutput(void); + obs_output_t *GetOutput(void) const; + bool Activate(DeckLinkDevice *device, long long modeId) override; + void Deactivate() override; + void DisplayVideoFrame(video_data *pData); + void WriteAudio(audio_data *frames); + void SetSize(int width, int height); + int GetWidth(); + int GetHeight(); +}; diff --git a/plugins/decklink/audio-repack.c b/plugins/decklink/audio-repack.c index 02d0879fb..5fd4293b5 100644 --- a/plugins/decklink/audio-repack.c +++ b/plugins/decklink/audio-repack.c @@ -21,38 +21,16 @@ int check_buffer(struct audio_repack *repack, } /* - * Swap channel 3 & 4, 5 & 7, 6 & 8 and squash arrays + * Squash arrays. + * For instance: * 2.1: * * | FL | FR | LFE | emp | emp | emp |emp |emp | * | | | * | FL | FR | LFE | - * 4.0 (quad): - * - * | FL | FR | BR | BL | emp | emp |emp |emp | - * | | x | - * | FL | FR | BL | BC | - * - * 4.1: - * - * | FL | FR |LFE | FC | BC | emp |emp |emp | - * | | x | | - * | FL | FR | FC |LFE | BC | - * - * 5.1: - * - * | FL | FR |LFE | FC |(emp|emp)|(BL|BR)| - * | | x x - * | FL | FR | FC |LFE | BL | BR | - * - * 7.1: - * - * | FL | FR |LFE | FC |( SL | SR )|(BL |BR )| - * | | x X - * | FL | FR | FC |LFE |( BL | BR )|(SL |SR )| - */ +*/ -int repack_squash_swap(struct audio_repack *repack, +int repack_squash(struct audio_repack *repack, const uint8_t *bsrc, uint32_t frame_count) { if (check_buffer(repack, frame_count) < 0) @@ -62,23 +40,13 @@ int repack_squash_swap(struct audio_repack *repack, const __m128i *src = (__m128i *)bsrc; const __m128i *esrc = src + frame_count; uint16_t *dst = (uint16_t *)repack->packet_buffer; - /* 2.1 audio does not require re-ordering but still needs squashing - * in order to avoid sampling issues. + + /* Audio needs squashing in order to avoid resampling issues. */ - if (squash == 5) { - while (src != esrc) { - __m128i target = _mm_load_si128(src++); - _mm_storeu_si128((__m128i *)dst, target); - dst += 8 - squash; - } - } else { - while (src != esrc) { - __m128i target = _mm_load_si128(src++); - __m128i buf = _mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0)); - __m128i buf2 = _mm_shufflehi_epi16(buf, _MM_SHUFFLE(1, 0, 3, 2)); - _mm_storeu_si128((__m128i *)dst, buf2); - dst += 8 - squash; - } + while (src != esrc) { + __m128i target = _mm_load_si128(src++); + _mm_storeu_si128((__m128i *)dst, target); + dst += 8 - squash; } return 0; @@ -92,43 +60,10 @@ int audio_repack_init(struct audio_repack *repack, if (sample_bit != 16) return -1; - switch (repack_mode) { - case repack_mode_8to3ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 3 * (16 / 8); - repack->extra_dst_size = 5; - repack->repack_func = &repack_squash_swap; - break; - case repack_mode_8to4ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 4 * (16 / 8); - repack->extra_dst_size = 4; - repack->repack_func = &repack_squash_swap; - break; - - case repack_mode_8to5ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 5 * (16 / 8); - repack->extra_dst_size = 3; - repack->repack_func = &repack_squash_swap; - break; - - case repack_mode_8to6ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 6 * (16 / 8); - repack->extra_dst_size = 2; - repack->repack_func = &repack_squash_swap; - break; - - case repack_mode_8ch_swap23_swap46_swap57: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 8 * (16 / 8); - repack->extra_dst_size = 0; - repack->repack_func = &repack_squash_swap; - break; - - default: return -1; - } + repack->base_src_size = 8 * (16 / 8); + repack->base_dst_size = (int)repack_mode * (16 / 8); + repack->extra_dst_size = 8 - (int)repack_mode; + repack->repack_func = &repack_squash; return 0; } diff --git a/plugins/decklink/audio-repack.h b/plugins/decklink/audio-repack.h index e063e13dc..22a1df81b 100644 --- a/plugins/decklink/audio-repack.h +++ b/plugins/decklink/audio-repack.h @@ -26,11 +26,10 @@ struct audio_repack { }; enum _audio_repack_mode { - repack_mode_8to3ch_swap23, - repack_mode_8to4ch_swap23, - repack_mode_8to5ch_swap23, - repack_mode_8to6ch_swap23, - repack_mode_8ch_swap23_swap46_swap57, + repack_mode_8to3ch=3, + repack_mode_8to4ch, + repack_mode_8to5ch, + repack_mode_8to6ch, }; typedef enum _audio_repack_mode audio_repack_mode_t; diff --git a/plugins/decklink/const.h b/plugins/decklink/const.h new file mode 100644 index 000000000..166777f20 --- /dev/null +++ b/plugins/decklink/const.h @@ -0,0 +1,32 @@ +#define DEVICE_HASH "device_hash" +#define DEVICE_NAME "device_name" +#define MODE_ID "mode_id" +#define MODE_NAME "mode_name" +#define CHANNEL_FORMAT "channel_format" +#define PIXEL_FORMAT "pixel_format" +#define COLOR_SPACE "color_space" +#define COLOR_RANGE "color_range" +#define BUFFERING "buffering" +#define DEACTIVATE_WNS "deactivate_when_not_showing" +#define AUTO_START "auto_start" + +#define TEXT_DEVICE obs_module_text("Device") +#define TEXT_MODE obs_module_text("Mode") +#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat") +#define TEXT_COLOR_SPACE obs_module_text("ColorSpace") +#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default") +#define TEXT_COLOR_RANGE obs_module_text("ColorRange") +#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default") +#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial") +#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full") +#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat") +#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None") +#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch") +#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch") +#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch") +#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch") +#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch") +#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch") +#define TEXT_BUFFERING obs_module_text("Buffering") +#define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing") +#define TEXT_AUTO_START obs_module_text("AutoStart") diff --git a/plugins/decklink/data/locale/el-GR.ini b/plugins/decklink/data/locale/el-GR.ini index a77c8fa7f..26fa84c90 100644 --- a/plugins/decklink/data/locale/el-GR.ini +++ b/plugins/decklink/data/locale/el-GR.ini @@ -12,4 +12,9 @@ ColorRange.Full="Πλήρης" ChannelFormat="Κανάλι" ChannelFormat.None="Κανένα" ChannelFormat.2_0ch="2 Καναλιών" +ChannelFormat.2_1ch="2.1ch" +ChannelFormat.4_0ch="4ch" +ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/en-US.ini b/plugins/decklink/data/locale/en-US.ini index a694a06f9..7fc9453a4 100644 --- a/plugins/decklink/data/locale/en-US.ini +++ b/plugins/decklink/data/locale/en-US.ini @@ -17,3 +17,5 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Deactivate when not showing" +AutoStart="Auto start on launch" diff --git a/plugins/decklink/data/locale/fil-PH.ini b/plugins/decklink/data/locale/fil-PH.ini new file mode 100644 index 000000000..9f0477c68 --- /dev/null +++ b/plugins/decklink/data/locale/fil-PH.ini @@ -0,0 +1,20 @@ +BlackmagicDevice="Blackmagic Device" +Device="Kagamitan" +Mode="Mode" +Buffering="Gamitin ang Buffering" +PixelFormat="Format ng Pixel" +ColorSpace="YUV Kulay Space" +ColorSpace.Default="Pangunahin" +ColorRange="Saklaw ng Kulay ng YUV" +ColorRange.Default="Pangunahin" +ColorRange.Partial="Bahagyang" +ColorRange.Full="Buong" +ChannelFormat="Pinagmulan" +ChannelFormat.None="Wala" +ChannelFormat.2_0ch="2ch" +ChannelFormat.2_1ch="2.1ch" +ChannelFormat.4_0ch="4ch" +ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" + diff --git a/plugins/decklink/data/locale/gd-GB.ini b/plugins/decklink/data/locale/gd-GB.ini new file mode 100644 index 000000000..ecb1814ca --- /dev/null +++ b/plugins/decklink/data/locale/gd-GB.ini @@ -0,0 +1,12 @@ +Device="Uidheam" +Mode="Modh" +Buffering="Cleachd bufaireadh" +ColorSpace="Spàs dhathan YUV" +ColorSpace.Default="Bun-roghainn" +ColorRange="Rainse dhathan YUV" +ColorRange.Default="Bun-roghainn" +ColorRange.Partial="Leth-phàirteach" +ColorRange.Full="Làn" +ChannelFormat="Seanail" +ChannelFormat.None="Chan eil gin" + diff --git a/plugins/decklink/data/locale/ka-GE.ini b/plugins/decklink/data/locale/ka-GE.ini new file mode 100644 index 000000000..a655ee432 --- /dev/null +++ b/plugins/decklink/data/locale/ka-GE.ini @@ -0,0 +1,20 @@ +BlackmagicDevice="Blackmagic მოწყობილობა" +Device="მოწყობილობა" +Mode="რეჟიმი" +Buffering="ბუფერიზაციის გამოყენება" +PixelFormat="პიქსელის ფორმატი" +ColorSpace="YUV ფერთა სისტემა" +ColorSpace.Default="ნაგულისხმევი" +ColorRange="YUV ფერთა გამა" +ColorRange.Default="ნაგულისხმევი" +ColorRange.Partial="ნაწილობრივი" +ColorRange.Full="სრული" +ChannelFormat="არხი" +ChannelFormat.None="არცერთი" +ChannelFormat.2_0ch="2 არხიანი" +ChannelFormat.2_1ch="2.1 არხიანი" +ChannelFormat.4_0ch="4 არხიანი" +ChannelFormat.4_1ch="4.1 არხიანი" +ChannelFormat.5_1ch="5.1 არხიანი" +ChannelFormat.7_1ch="7.1 არხიანი" + diff --git a/plugins/decklink/data/locale/tl-PH.ini b/plugins/decklink/data/locale/tl-PH.ini new file mode 100644 index 000000000..6eccea986 --- /dev/null +++ b/plugins/decklink/data/locale/tl-PH.ini @@ -0,0 +1,20 @@ +BlackmagicDevice="Itim na salamangka na aparato" +Device="Aparato" +Mode="I-mode" +Buffering="Paggamit ng Buffering" +PixelFormat="Ang Format ng Pixel" +ColorSpace="Pagitan sa kulay na YUV" +ColorSpace.Default="I-default" +ColorRange="Ang Saklaw ng Kulay na YUV" +ColorRange.Default="I-default" +ColorRange.Partial="Bahagya" +ColorRange.Full="Puno" +ChannelFormat="Ang Channel" +ChannelFormat.None="Wala" +ChannelFormat.2_0ch="2ch" +ChannelFormat.2_1ch="2.1ch" +ChannelFormat.4_0ch="4ch" +ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" + diff --git a/plugins/decklink/data/locale/vi-VN.ini b/plugins/decklink/data/locale/vi-VN.ini index 4a635365a..389e366f7 100644 --- a/plugins/decklink/data/locale/vi-VN.ini +++ b/plugins/decklink/data/locale/vi-VN.ini @@ -6,4 +6,9 @@ ColorRange.Full="Đầy đủ" ChannelFormat="Kênh" ChannelFormat.None="Không có" ChannelFormat.2_0ch="2ch" +ChannelFormat.2_1ch="2.1ch" +ChannelFormat.4_0ch="4ch" +ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/decklink-device-discovery.hpp b/plugins/decklink/decklink-device-discovery.hpp index 3bb6e3cd8..9f2842cec 100644 --- a/plugins/decklink/decklink-device-discovery.hpp +++ b/plugins/decklink/decklink-device-discovery.hpp @@ -1,10 +1,11 @@ #pragma once +#include +#include "platform.hpp" + #include #include -#include "decklink.hpp" - class DeckLinkDevice; typedef void (*DeviceChangeCallback)(void *param, DeckLinkDevice *device, diff --git a/plugins/decklink/decklink-device-instance.cpp b/plugins/decklink/decklink-device-instance.cpp index 5dc674e5e..a68588997 100644 --- a/plugins/decklink/decklink-device-instance.cpp +++ b/plugins/decklink/decklink-device-instance.cpp @@ -1,19 +1,14 @@ #include "decklink-device-instance.hpp" #include "audio-repack.hpp" +#include "DecklinkInput.hpp" +#include "DecklinkOutput.hpp" + #include #include #include - -#define LOG(level, message, ...) blog(level, "%s: " message, \ - obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__) - -#ifdef _WIN32 -#define IS_WIN 1 -#else -#define IS_WIN 0 -#endif +#include static inline enum video_format ConvertPixelFormat(BMDPixelFormat format) { @@ -47,22 +42,21 @@ static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format) { switch (format) { case SPEAKERS_2POINT1: - return repack_mode_8to3ch_swap23; + return repack_mode_8to3ch; case SPEAKERS_4POINT0: - return repack_mode_8to4ch_swap23; + return repack_mode_8to4ch; case SPEAKERS_4POINT1: - return repack_mode_8to5ch_swap23; + return repack_mode_8to5ch; case SPEAKERS_5POINT1: - return repack_mode_8to6ch_swap23; + return repack_mode_8to6ch; case SPEAKERS_7POINT1: - return repack_mode_8ch_swap23_swap46_swap57; default: assert(false && "No repack requested"); return (audio_repack_mode_t)-1; } } -DeckLinkDeviceInstance::DeckLinkDeviceInstance(DeckLink *decklink_, +DeckLinkDeviceInstance::DeckLinkDeviceInstance(DecklinkBase *decklink_, DeckLinkDevice *device_) : currentFrame(), currentPacket(), decklink(decklink_), device(device_) { @@ -92,7 +86,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket( currentPacket.frames = frameCount; currentPacket.timestamp = timestamp; - if (decklink && !decklink->buffering) { + if (decklink && !static_cast(decklink)->buffering) { currentPacket.timestamp = os_gettime_ns(); currentPacket.timestamp -= (uint64_t)frameCount * 1000000000ULL / @@ -100,13 +94,12 @@ void DeckLinkDeviceInstance::HandleAudioPacket( } int maxdevicechannel = device->GetMaxChannel(); - bool isWin = IS_WIN; if (channelFormat != SPEAKERS_UNKNOWN && channelFormat != SPEAKERS_MONO && channelFormat != SPEAKERS_STEREO && - maxdevicechannel >= 8 && - isWin) { + channelFormat != SPEAKERS_7POINT1 && + maxdevicechannel >= 8) { if (audioRepacker->repack((uint8_t *)bytes, frameCount) < 0) { LOG(LOG_ERROR, "Failed to convert audio packet data"); @@ -120,7 +113,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket( nextAudioTS = timestamp + ((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1; - obs_source_output_audio(decklink->GetSource(), ¤tPacket); + obs_source_output_audio(static_cast(decklink)->GetSource(), ¤tPacket); } void DeckLinkDeviceInstance::HandleVideoFrame( @@ -141,7 +134,7 @@ void DeckLinkDeviceInstance::HandleVideoFrame( currentFrame.height = (uint32_t)videoFrame->GetHeight(); currentFrame.timestamp = timestamp; - obs_source_output_video(decklink->GetSource(), ¤tFrame); + obs_source_output_video(static_cast(decklink)->GetSource(), ¤tFrame); } void DeckLinkDeviceInstance::FinalizeStream() @@ -169,7 +162,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) currentFrame.format = ConvertPixelFormat(pixelFormat); - colorSpace = decklink->GetColorSpace(); + colorSpace = static_cast(decklink)->GetColorSpace(); if (colorSpace == VIDEO_CS_DEFAULT) { const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags(); if (flags & bmdDisplayModeColorspaceRec709) @@ -182,7 +175,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) activeColorSpace = colorSpace; } - colorRange = decklink->GetColorRange(); + colorRange = static_cast(decklink)->GetColorRange(); currentFrame.full_range = colorRange == VIDEO_RANGE_FULL; video_format_get_parameters(activeColorSpace, colorRange, @@ -218,7 +211,7 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) flags = bmdVideoInputEnableFormatDetection; } else { displayMode = mode_->GetDisplayMode(); - pixelFormat = decklink->GetPixelFormat(); + pixelFormat = static_cast(decklink)->GetPixelFormat(); flags = bmdVideoInputFlagDefault; } @@ -231,11 +224,10 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) SetupVideoFormat(mode_); - channelFormat = decklink->GetChannelFormat(); + channelFormat = static_cast(decklink)->GetChannelFormat(); currentPacket.speakers = channelFormat; int maxdevicechannel = device->GetMaxChannel(); - bool isWin = IS_WIN; if (channelFormat != SPEAKERS_UNKNOWN) { const int channel = ConvertChannelFormat(channelFormat); @@ -248,8 +240,8 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) if (channelFormat != SPEAKERS_UNKNOWN && channelFormat != SPEAKERS_MONO && channelFormat != SPEAKERS_STEREO && - maxdevicechannel >= 8 && - isWin) { + channelFormat != SPEAKERS_7POINT1 && + maxdevicechannel >= 8) { const audio_repack_mode_t repack_mode = ConvertRepackFormat (channelFormat); @@ -288,6 +280,101 @@ bool DeckLinkDeviceInstance::StopCapture(void) return true; } +bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_) +{ + if (mode != nullptr) + return false; + if (mode_ == nullptr) + return false; + + LOG(LOG_INFO, "Starting output..."); + + if (!device->GetOutput(&output)) + return false; + + const HRESULT videoResult = output->EnableVideoOutput( + mode_->GetDisplayMode(), + bmdVideoOutputFlagDefault); + if (videoResult != S_OK) { + LOG(LOG_ERROR, "Failed to enable video output"); + return false; + } + + const HRESULT audioResult = output->EnableAudioOutput( + bmdAudioSampleRate48kHz, + bmdAudioSampleType16bitInteger, + 2, + bmdAudioOutputStreamTimestamped); + if (audioResult != S_OK) { + LOG(LOG_ERROR, "Failed to enable audio output"); + return false; + } + + mode = mode_; + + auto decklinkOutput = dynamic_cast(decklink); + if (decklinkOutput == nullptr) + return false; + + HRESULT result; + result = output->CreateVideoFrame(decklinkOutput->GetWidth(), + decklinkOutput->GetHeight(), + decklinkOutput->GetWidth() * 2, + bmdFormat8BitYUV, + bmdFrameFlagDefault, + &decklinkOutputFrame); + if (result != S_OK) { + blog(LOG_ERROR ,"failed to make frame 0x%X", result); + return false; + } + + return true; +} + +bool DeckLinkDeviceInstance::StopOutput() +{ + if (mode == nullptr || output == nullptr) + return false; + + LOG(LOG_INFO, "Stopping output of '%s'...", + GetDevice()->GetDisplayName().c_str()); + + output->DisableVideoOutput(); + output->DisableAudioOutput(); + + if (decklinkOutputFrame != nullptr) { + decklinkOutputFrame->Release(); + decklinkOutputFrame = nullptr; + } + + return true; +} + +void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame) +{ + auto decklinkOutput = dynamic_cast(decklink); + if (decklinkOutput == nullptr) + return; + + uint8_t *destData; + decklinkOutputFrame->GetBytes((void**)&destData); + + uint8_t *outData = frame->data[0]; + + std::copy(outData, outData + (decklinkOutput->GetWidth() * + decklinkOutput->GetHeight() * 2), destData); + + output->DisplayVideoFrameSync(decklinkOutputFrame); +} + +void DeckLinkDeviceInstance::WriteAudio(audio_data *frames) +{ + uint32_t sampleFramesWritten; + output->WriteAudioSamplesSync(frames->data[0], + frames->frames, + &sampleFramesWritten); +} + #define TIME_BASE 1000000000 HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived( diff --git a/plugins/decklink/decklink-device-instance.hpp b/plugins/decklink/decklink-device-instance.hpp index ffbed0782..3d3251340 100644 --- a/plugins/decklink/decklink-device-instance.hpp +++ b/plugins/decklink/decklink-device-instance.hpp @@ -1,14 +1,19 @@ #pragma once +#define LOG(level, message, ...) blog(level, "%s: " message, "decklink", ##__VA_ARGS__) + +#include #include "decklink-device.hpp" +#include "../../libobs/media-io/video-scaler.h" class AudioRepacker; +class DecklinkBase; class DeckLinkDeviceInstance : public IDeckLinkInputCallback { protected: struct obs_source_frame currentFrame; struct obs_source_audio currentPacket; - DeckLink *decklink = nullptr; + DecklinkBase *decklink = nullptr; DeckLinkDevice *device = nullptr; DeckLinkDeviceMode *mode = nullptr; BMDDisplayMode displayMode = bmdModeNTSC; @@ -17,6 +22,7 @@ class DeckLinkDeviceInstance : public IDeckLinkInputCallback { video_colorspace activeColorSpace = VIDEO_CS_DEFAULT; video_range_type colorRange = VIDEO_RANGE_DEFAULT; ComPtr input; + ComPtr output; volatile long refCount = 1; int64_t audioOffset = 0; uint64_t nextAudioTS = 0; @@ -24,6 +30,8 @@ class DeckLinkDeviceInstance : public IDeckLinkInputCallback { AudioRepacker *audioRepacker = nullptr; speaker_layout channelFormat = SPEAKERS_STEREO; + IDeckLinkMutableVideoFrame *decklinkOutputFrame; + void FinalizeStream(); void SetupVideoFormat(DeckLinkDeviceMode *mode_); @@ -33,7 +41,7 @@ class DeckLinkDeviceInstance : public IDeckLinkInputCallback { const uint64_t timestamp); public: - DeckLinkDeviceInstance(DeckLink *decklink, DeckLinkDevice *device); + DeckLinkDeviceInstance(DecklinkBase *decklink, DeckLinkDevice *device); virtual ~DeckLinkDeviceInstance(); inline DeckLinkDevice *GetDevice() const {return device;} @@ -52,6 +60,9 @@ class DeckLinkDeviceInstance : public IDeckLinkInputCallback { bool StartCapture(DeckLinkDeviceMode *mode); bool StopCapture(void); + bool StartOutput(DeckLinkDeviceMode *mode_); + bool StopOutput(void); + HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioPacket); @@ -63,4 +74,7 @@ class DeckLinkDeviceInstance : public IDeckLinkInputCallback { ULONG STDMETHODCALLTYPE AddRef(void); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv); ULONG STDMETHODCALLTYPE Release(void); + + void DisplayVideoFrame(video_data *frame); + void WriteAudio(audio_data *frames); }; diff --git a/plugins/decklink/decklink-device-mode.cpp b/plugins/decklink/decklink-device-mode.cpp index 48d6eac5d..67a052bd3 100644 --- a/plugins/decklink/decklink-device-mode.cpp +++ b/plugins/decklink/decklink-device-mode.cpp @@ -32,6 +32,22 @@ BMDDisplayMode DeckLinkDeviceMode::GetDisplayMode(void) const return bmdModeUnknown; } +int DeckLinkDeviceMode::GetWidth() +{ + if (mode != nullptr) + return mode->GetWidth(); + + return 0; +} + +int DeckLinkDeviceMode::GetHeight() +{ + if (mode != nullptr) + return mode->GetHeight(); + + return 0; +} + BMDDisplayModeFlags DeckLinkDeviceMode::GetDisplayModeFlags(void) const { if (mode != nullptr) diff --git a/plugins/decklink/decklink-device-mode.hpp b/plugins/decklink/decklink-device-mode.hpp index 8ff642da7..ae001532b 100644 --- a/plugins/decklink/decklink-device-mode.hpp +++ b/plugins/decklink/decklink-device-mode.hpp @@ -23,4 +23,7 @@ class DeckLinkDeviceMode { const std::string& GetName(void) const; void SetMode(IDeckLinkDisplayMode *mode); + + int GetWidth(); + int GetHeight(); }; diff --git a/plugins/decklink/decklink-device.cpp b/plugins/decklink/decklink-device.cpp index d173053af..9d0f35c65 100644 --- a/plugins/decklink/decklink-device.cpp +++ b/plugins/decklink/decklink-device.cpp @@ -10,7 +10,10 @@ DeckLinkDevice::DeckLinkDevice(IDeckLink *device_) : device(device_) DeckLinkDevice::~DeckLinkDevice(void) { - for (DeckLinkDeviceMode *mode : modes) + for (DeckLinkDeviceMode *mode : inputModes) + delete mode; + + for (DeckLinkDeviceMode *mode : outputModes) delete mode; } @@ -37,35 +40,61 @@ bool DeckLinkDevice::Init() decklink_bool_t detectable = false; if (attributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &detectable) == S_OK && !!detectable) { - DeckLinkDeviceMode *mode = - new DeckLinkDeviceMode("Auto", MODE_ID_AUTO); - modes.push_back(mode); - modeIdMap[MODE_ID_AUTO] = mode; + DeckLinkDeviceMode *mode = new DeckLinkDeviceMode( + "Auto", + MODE_ID_AUTO); + inputModes.push_back(mode); + inputModeIdMap[MODE_ID_AUTO] = mode; } } + // Find input modes ComPtr input; - if (device->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK) - return false; - - IDeckLinkDisplayModeIterator *modeIterator; - if (input->GetDisplayModeIterator(&modeIterator) == S_OK) { - IDeckLinkDisplayMode *displayMode; - long long modeId = 1; - - while (modeIterator->Next(&displayMode) == S_OK) { - if (displayMode == nullptr) - continue; - - DeckLinkDeviceMode *mode = - new DeckLinkDeviceMode(displayMode, modeId); - modes.push_back(mode); - modeIdMap[modeId] = mode; - displayMode->Release(); - ++modeId; + if (device->QueryInterface(IID_IDeckLinkInput, (void **) &input) == S_OK) { + IDeckLinkDisplayModeIterator *modeIterator; + if (input->GetDisplayModeIterator(&modeIterator) == S_OK) { + IDeckLinkDisplayMode *displayMode; + long long modeId = 1; + + while (modeIterator->Next(&displayMode) == S_OK) { + if (displayMode == nullptr) + continue; + + DeckLinkDeviceMode *mode = + new DeckLinkDeviceMode(displayMode, modeId); + inputModes.push_back(mode); + inputModeIdMap[modeId] = mode; + displayMode->Release(); + ++modeId; + } + + modeIterator->Release(); } + } + + // find output modes + ComPtr output; + if (device->QueryInterface(IID_IDeckLinkOutput, (void **) &output) == S_OK) { - modeIterator->Release(); + IDeckLinkDisplayModeIterator *modeIterator; + if (output->GetDisplayModeIterator(&modeIterator) == S_OK) { + IDeckLinkDisplayMode *displayMode; + long long modeId = 1; + + while (modeIterator->Next(&displayMode) == S_OK) { + if (displayMode == nullptr) + continue; + + DeckLinkDeviceMode *mode = + new DeckLinkDeviceMode(displayMode, modeId); + outputModes.push_back(mode); + outputModeIdMap[modeId] = mode; + displayMode->Release(); + ++modeId; + } + + modeIterator->Release(); + } } decklink_string_t decklinkModelName; @@ -115,9 +144,22 @@ bool DeckLinkDevice::GetInput(IDeckLinkInput **input) return true; } -DeckLinkDeviceMode *DeckLinkDevice::FindMode(long long id) +bool DeckLinkDevice::GetOutput(IDeckLinkOutput **output) +{ + if (device->QueryInterface(IID_IDeckLinkOutput, (void**)output) != S_OK) + return false; + + return true; +} + +DeckLinkDeviceMode *DeckLinkDevice::FindInputMode(long long id) { - return modeIdMap[id]; + return inputModeIdMap[id]; +} + +DeckLinkDeviceMode *DeckLinkDevice::FindOutputMode(long long id) +{ + return outputModeIdMap[id]; } const std::string& DeckLinkDevice::GetDisplayName(void) @@ -130,9 +172,14 @@ const std::string& DeckLinkDevice::GetHash(void) const return hash; } -const std::vector& DeckLinkDevice::GetModes(void) const +const std::vector& DeckLinkDevice::GetInputModes(void) const +{ + return inputModes; +} + +const std::vector& DeckLinkDevice::GetOutputModes(void) const { - return modes; + return outputModes; } const std::string& DeckLinkDevice::GetName(void) const diff --git a/plugins/decklink/decklink-device.hpp b/plugins/decklink/decklink-device.hpp index 9825ad8df..723bce0a0 100644 --- a/plugins/decklink/decklink-device.hpp +++ b/plugins/decklink/decklink-device.hpp @@ -1,6 +1,5 @@ #pragma once -#include "decklink.hpp" #include "decklink-device-mode.hpp" #include @@ -9,8 +8,10 @@ class DeckLinkDevice { ComPtr device; - std::map modeIdMap; - std::vector modes; + std::map inputModeIdMap; + std::vector inputModes; + std::map outputModeIdMap; + std::vector outputModes; std::string name; std::string displayName; std::string hash; @@ -26,14 +27,17 @@ class DeckLinkDevice { bool Init(); - DeckLinkDeviceMode *FindMode(long long id); + DeckLinkDeviceMode *FindInputMode(long long id); + DeckLinkDeviceMode *FindOutputMode(long long id); const std::string& GetDisplayName(void); const std::string& GetHash(void) const; - const std::vector& GetModes(void) const; + const std::vector& GetInputModes(void) const; + const std::vector& GetOutputModes(void) const; const std::string& GetName(void) const; int32_t GetMaxChannel(void) const; bool GetInput(IDeckLinkInput **input); + bool GetOutput(IDeckLinkOutput **output); inline bool IsDevice(IDeckLink *device_) { diff --git a/plugins/decklink/decklink-devices.cpp b/plugins/decklink/decklink-devices.cpp new file mode 100644 index 000000000..28607ef73 --- /dev/null +++ b/plugins/decklink/decklink-devices.cpp @@ -0,0 +1,17 @@ +#include "decklink-devices.hpp" + +DeckLinkDeviceDiscovery *deviceEnum = nullptr; + +void fill_out_devices(obs_property_t *list) +{ + deviceEnum->Lock(); + + const std::vector &devices = deviceEnum->GetDevices(); + for (DeckLinkDevice *device : devices) { + obs_property_list_add_string(list, + device->GetDisplayName().c_str(), + device->GetHash().c_str()); + } + + deviceEnum->Unlock(); +} diff --git a/plugins/decklink/decklink-devices.hpp b/plugins/decklink/decklink-devices.hpp new file mode 100644 index 000000000..fa1d24cad --- /dev/null +++ b/plugins/decklink/decklink-devices.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" + +extern DeckLinkDeviceDiscovery *deviceEnum; + +void fill_out_devices(obs_property_t *list); diff --git a/plugins/decklink/decklink-output.cpp b/plugins/decklink/decklink-output.cpp new file mode 100644 index 000000000..19edc3e27 --- /dev/null +++ b/plugins/decklink/decklink-output.cpp @@ -0,0 +1,239 @@ +#include +#include + +#include "const.h" + +#include "DecklinkOutput.hpp" +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" +#include "decklink-devices.hpp" + +#include "../../libobs/media-io/video-scaler.h" + +static void decklink_output_destroy(void *data) +{ + auto *decklink = (DeckLinkOutput *)data; + delete decklink; +} + +static void *decklink_output_create(obs_data_t *settings, obs_output_t *output) +{ + auto *decklinkOutput = new DeckLinkOutput(output, deviceEnum); + + decklinkOutput->deviceHash = obs_data_get_string(settings, DEVICE_HASH); + decklinkOutput->modeID = obs_data_get_int(settings, MODE_ID); + + return decklinkOutput; +} + +static void decklink_output_update(void *data, obs_data_t *settings) +{ + auto *decklink = (DeckLinkOutput *)data; + + decklink->deviceHash = obs_data_get_string(settings, DEVICE_HASH); + decklink->modeID = obs_data_get_int(settings, MODE_ID); +} + +static bool decklink_output_start(void *data) +{ + auto *decklink = (DeckLinkOutput *)data; + struct obs_audio_info aoi; + + if (!obs_get_audio_info(&aoi)) { + blog(LOG_WARNING, "No active audio"); + return false; + } + + decklink->audio_samplerate = aoi.samples_per_sec; + decklink->audio_planes = 2; + decklink->audio_size = get_audio_size(AUDIO_FORMAT_16BIT, aoi.speakers, 1); + + decklink->start_timestamp = 0; + + ComPtr device; + + device.Set(deviceEnum->FindByHash(decklink->deviceHash)); + + DeckLinkDeviceMode *mode = device->FindOutputMode(decklink->modeID); + + decklink->SetSize(mode->GetWidth(), mode->GetHeight()); + + struct video_scale_info to = {}; + to.format = VIDEO_FORMAT_UYVY; + to.width = mode->GetWidth(); + to.height = mode->GetHeight(); + + obs_output_set_video_conversion(decklink->GetOutput(), &to); + + decklink->Activate(device, decklink->modeID); + + struct audio_convert_info conversion = {}; + conversion.format = AUDIO_FORMAT_16BIT; + conversion.speakers = SPEAKERS_STEREO; + conversion.samples_per_sec = 48000; // Only format the decklink supports + + obs_output_set_audio_conversion(decklink->GetOutput(), &conversion); + + if (!obs_output_begin_data_capture(decklink->GetOutput(), 0)) + return false; + + return true; +} + +static void decklink_output_stop(void *data, uint64_t) +{ + auto *decklink = (DeckLinkOutput *)data; + + obs_output_end_data_capture(decklink->GetOutput()); + + ComPtr device; + + device.Set(deviceEnum->FindByHash(decklink->deviceHash)); + + decklink->Deactivate(); +} + +static void decklink_output_raw_video(void *data, struct video_data *frame) +{ + auto *decklink = (DeckLinkOutput *)data; + + if (!decklink->start_timestamp) + decklink->start_timestamp = frame->timestamp; + + decklink->DisplayVideoFrame(frame); +} + +static bool prepare_audio(DeckLinkOutput *decklink, + const struct audio_data *frame, + struct audio_data *output) +{ + *output = *frame; + + if (frame->timestamp < decklink->start_timestamp) { + uint64_t duration = (uint64_t)frame->frames * 1000000000 / + (uint64_t)decklink->audio_samplerate; + uint64_t end_ts = frame->timestamp + duration; + uint64_t cutoff; + + if (end_ts <= decklink->start_timestamp) + return false; + + cutoff = decklink->start_timestamp - frame->timestamp; + output->timestamp += cutoff; + + cutoff *= (uint64_t)decklink->audio_samplerate / 1000000000; + + for (size_t i = 0; i < decklink->audio_planes; i++) + output->data[i] += decklink->audio_size * + (uint32_t)cutoff; + + output->frames -= (uint32_t)cutoff; + } + + return true; +} + +static void decklink_output_raw_audio(void *data, struct audio_data *frames) +{ + auto *decklink = (DeckLinkOutput *)data; + struct audio_data in; + + if (!decklink->start_timestamp) + return; + + if (!prepare_audio(decklink, frames, &in)) + return; + + decklink->WriteAudio(&in); +} + +static bool decklink_output_device_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + const char *name = obs_data_get_string(settings, DEVICE_NAME); + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + const char *mode = obs_data_get_string(settings, MODE_NAME); + long long modeId = obs_data_get_int(settings, MODE_ID); + + size_t itemCount = obs_property_list_item_count(list); + bool itemFound = false; + + for (size_t i = 0; i < itemCount; i++) { + const char *curHash = obs_property_list_item_string(list, i); + if (strcmp(hash, curHash) == 0) { + itemFound = true; + break; + } + } + + if (!itemFound) { + obs_property_list_insert_string(list, 0, name, hash); + obs_property_list_item_disable(list, 0, true); + } + + obs_property_t *modeList = obs_properties_get(props, MODE_ID); + + obs_property_list_clear(modeList); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + if (!device) { + obs_property_list_add_int(modeList, mode, modeId); + obs_property_list_item_disable(modeList, 0, true); + } else { + const std::vector &modes = + device->GetOutputModes(); + + for (DeckLinkDeviceMode *mode : modes) { + obs_property_list_add_int(modeList, + mode->GetName().c_str(), + mode->GetId()); + } + } + + return true; +} + +static obs_properties_t *decklink_output_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + obs_properties_t *props = obs_properties_create(); + + obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, + TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_set_modified_callback(list, decklink_output_device_changed); + + fill_out_devices(list); + + obs_properties_add_list(props, + MODE_ID, TEXT_MODE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_properties_add_bool(props, AUTO_START, TEXT_AUTO_START); + + return props; +} + +static const char *decklink_output_get_name(void*) +{ + return obs_module_text("BlackmagicDevice"); +} + +struct obs_output_info create_decklink_output_info() +{ + struct obs_output_info decklink_output_info = {}; + + decklink_output_info.id = "decklink_output"; + decklink_output_info.flags = OBS_OUTPUT_AV; + decklink_output_info.get_name = decklink_output_get_name; + decklink_output_info.create = decklink_output_create; + decklink_output_info.destroy = decklink_output_destroy; + decklink_output_info.start = decklink_output_start; + decklink_output_info.stop = decklink_output_stop; + decklink_output_info.get_properties = decklink_output_properties; + decklink_output_info.raw_video = decklink_output_raw_video; + decklink_output_info.raw_audio = decklink_output_raw_audio; + decklink_output_info.update = decklink_output_update; + + return decklink_output_info; +} diff --git a/plugins/decklink/decklink-source.cpp b/plugins/decklink/decklink-source.cpp new file mode 100644 index 000000000..9e448c8f1 --- /dev/null +++ b/plugins/decklink/decklink-source.cpp @@ -0,0 +1,288 @@ +#include + +#include "const.h" + +#include "DecklinkInput.hpp" +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" +#include "decklink-devices.hpp" + +static void decklink_enable_buffering(DeckLinkInput *decklink, bool enabled) +{ + obs_source_t *source = decklink->GetSource(); + obs_source_set_async_unbuffered(source, !enabled); + decklink->buffering = enabled; +} + +static void decklink_deactivate_when_not_showing(DeckLinkInput *decklink, bool dwns) +{ + decklink->dwns = dwns; +} + +static void *decklink_create(obs_data_t *settings, obs_source_t *source) +{ + DeckLinkInput *decklink = new DeckLinkInput(source, deviceEnum); + + obs_source_set_async_decoupled(source, true); + decklink_enable_buffering(decklink, + obs_data_get_bool(settings, BUFFERING)); + + obs_source_update(source, settings); + return decklink; +} + +static void decklink_destroy(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + delete decklink; +} + +static void decklink_update(void *data, obs_data_t *settings) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + long long id = obs_data_get_int(settings, MODE_ID); + BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, + PIXEL_FORMAT); + video_colorspace colorSpace = (video_colorspace)obs_data_get_int(settings, + COLOR_SPACE); + video_range_type colorRange = (video_range_type)obs_data_get_int(settings, + COLOR_RANGE); + int chFmtInt = (int)obs_data_get_int(settings, CHANNEL_FORMAT); + + if (chFmtInt == 7) + chFmtInt = SPEAKERS_5POINT1; + else if (chFmtInt < SPEAKERS_UNKNOWN || chFmtInt > SPEAKERS_7POINT1) + chFmtInt = 2; + + speaker_layout channelFormat = (speaker_layout)chFmtInt; + + decklink_enable_buffering(decklink, + obs_data_get_bool(settings, BUFFERING)); + + decklink_deactivate_when_not_showing(decklink, + obs_data_get_bool(settings, DEACTIVATE_WNS)); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + decklink->SetPixelFormat(pixelFormat); + decklink->SetColorSpace(colorSpace); + decklink->SetColorRange(colorRange); + decklink->SetChannelFormat(channelFormat); + decklink->Activate(device, id); + decklink->hash = std::string(hash); +} + +static void decklink_show(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + obs_source_t *source = decklink->GetSource(); + bool showing = obs_source_showing(source); + if (decklink->dwns && showing && !decklink->Capturing()) { + ComPtr device; + device.Set(deviceEnum->FindByHash(decklink->hash.c_str())); + decklink->Activate(device, decklink->id); + } +} +static void decklink_hide(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + obs_source_t *source = decklink->GetSource(); + bool showing = obs_source_showing(source); + if (decklink->dwns && showing) + decklink->Deactivate(); +} + +static void decklink_get_defaults(obs_data_t *settings) +{ + obs_data_set_default_bool(settings, BUFFERING, false); + obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV); + obs_data_set_default_int(settings, COLOR_SPACE, VIDEO_CS_DEFAULT); + obs_data_set_default_int(settings, COLOR_RANGE, VIDEO_RANGE_DEFAULT); + obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO); +} + +static const char *decklink_get_name(void*) +{ + return obs_module_text("BlackmagicDevice"); +} + +static bool decklink_device_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + const char *name = obs_data_get_string(settings, DEVICE_NAME); + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + const char *mode = obs_data_get_string(settings, MODE_NAME); + long long modeId = obs_data_get_int(settings, MODE_ID); + + size_t itemCount = obs_property_list_item_count(list); + bool itemFound = false; + + for (size_t i = 0; i < itemCount; i++) { + const char *curHash = obs_property_list_item_string(list, i); + if (strcmp(hash, curHash) == 0) { + itemFound = true; + break; + } + } + + if (!itemFound) { + obs_property_list_insert_string(list, 0, name, hash); + obs_property_list_item_disable(list, 0, true); + } + + obs_property_t *modeList = obs_properties_get(props, MODE_ID); + obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); + + obs_property_list_clear(modeList); + + obs_property_list_clear(channelList); + obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_NONE, + SPEAKERS_UNKNOWN); + obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_0CH, + SPEAKERS_STEREO); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + if (!device) { + obs_property_list_add_int(modeList, mode, modeId); + obs_property_list_item_disable(modeList, 0, true); + } else { + const std::vector &modes = + device->GetInputModes(); + + for (DeckLinkDeviceMode *mode : modes) { + obs_property_list_add_int(modeList, + mode->GetName().c_str(), + mode->GetId()); + } + + if (device->GetMaxChannel() >= 8) { + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_2_1CH, SPEAKERS_2POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_4_0CH, SPEAKERS_4POINT0); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_4_1CH, SPEAKERS_4POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_5_1CH, SPEAKERS_5POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_7_1CH, SPEAKERS_7POINT1); + } + } + + return true; +} + +static bool color_format_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings); + +static bool mode_id_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + long long id = obs_data_get_int(settings, MODE_ID); + + list = obs_properties_get(props, PIXEL_FORMAT); + obs_property_set_visible(list, id != MODE_ID_AUTO); + + return color_format_changed(props, nullptr, settings); +} + +static bool color_format_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + long long id = obs_data_get_int(settings, MODE_ID); + BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, + PIXEL_FORMAT); + + list = obs_properties_get(props, COLOR_SPACE); + obs_property_set_visible(list, + id != MODE_ID_AUTO && pixelFormat == bmdFormat8BitYUV); + + list = obs_properties_get(props, COLOR_RANGE); + obs_property_set_visible(list, + id == MODE_ID_AUTO || pixelFormat == bmdFormat8BitYUV); + + return true; +} + +static obs_properties_t *decklink_get_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, + TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_set_modified_callback(list, decklink_device_changed); + + fill_out_devices(list); + + list = obs_properties_add_list(props, MODE_ID, TEXT_MODE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_modified_callback(list, mode_id_changed); + + list = obs_properties_add_list(props, PIXEL_FORMAT, + TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_set_modified_callback(list, color_format_changed); + + obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); + obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); + + list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT, VIDEO_CS_DEFAULT); + obs_property_list_add_int(list, "BT.601", VIDEO_CS_601); + obs_property_list_add_int(list, "BT.709", VIDEO_CS_709); + + list = obs_properties_add_list(props, COLOR_RANGE, TEXT_COLOR_RANGE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT, VIDEO_RANGE_DEFAULT); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL, VIDEO_RANGE_PARTIAL); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL, VIDEO_RANGE_FULL); + + list = obs_properties_add_list(props, CHANNEL_FORMAT, + TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE, + SPEAKERS_UNKNOWN); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH, + SPEAKERS_STEREO); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_1CH, + SPEAKERS_2POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_0CH, + SPEAKERS_4POINT0); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_1CH, + SPEAKERS_4POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_5_1CH, + SPEAKERS_5POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH, + SPEAKERS_7POINT1); + + obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); + + obs_properties_add_bool(props, DEACTIVATE_WNS, TEXT_DWNS); + + UNUSED_PARAMETER(data); + return props; +} + + +struct obs_source_info create_decklink_source_info() +{ + struct obs_source_info decklink_source_info = {}; + decklink_source_info.id = "decklink-input"; + decklink_source_info.type = OBS_SOURCE_TYPE_INPUT; + decklink_source_info.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE; + decklink_source_info.create = decklink_create; + decklink_source_info.destroy = decklink_destroy; + decklink_source_info.get_defaults = decklink_get_defaults; + decklink_source_info.get_name = decklink_get_name; + decklink_source_info.get_properties = decklink_get_properties; + decklink_source_info.update = decklink_update; + decklink_source_info.show = decklink_show; + decklink_source_info.hide = decklink_hide; + + return decklink_source_info; +} diff --git a/plugins/decklink/linux/CMakeLists.txt b/plugins/decklink/linux/CMakeLists.txt index 1ed5ae46d..67da0210a 100644 --- a/plugins/decklink/linux/CMakeLists.txt +++ b/plugins/decklink/linux/CMakeLists.txt @@ -21,8 +21,12 @@ set(linux-decklink-sdk_SOURCES ) set(linux-decklink_HEADERS + ../decklink-devices.hpp + ../const.h + ../DecklinkOutput.hpp ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp @@ -33,21 +37,29 @@ set(linux-decklink_HEADERS set(linux-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../decklink-source.cpp + ../decklink-output.cpp + ../DecklinkOutput.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ) add_library(linux-decklink MODULE ${linux-decklink_SOURCES} ${linux-decklink_HEADERS} ${linux-decklink-sdk_HEADERS} - ${linux-decklink-sdk_SOURCES}) + ${linux-decklink-sdk_SOURCES} + ) target_link_libraries(linux-decklink - libobs) + libobs + ) install_obs_plugin_with_data(linux-decklink ../data) diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h index 2eed7b7d8..807481466 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + /* DeckLink API */ #include @@ -70,6 +74,9 @@ BMD_CONST REFIID IID_IDeckLinkMutableVideoFrame = /* 69E2639F- BMD_CONST REFIID IID_IDeckLinkVideoFrame3DExtensions = /* DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7 */ {0xDA,0x0F,0x7E,0x4A,0xED,0xC7,0x48,0xA8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7}; BMD_CONST REFIID IID_IDeckLinkVideoFrameMetadataExtensions = /* D5973DC9-6432-46D0-8F0B-2496F8A1238F */ {0xD5,0x97,0x3D,0xC9,0x64,0x32,0x46,0xD0,0x8F,0x0B,0x24,0x96,0xF8,0xA1,0x23,0x8F}; BMD_CONST REFIID IID_IDeckLinkVideoInputFrame = /* 05CFE374-537C-4094-9A57-680525118F44 */ {0x05,0xCF,0xE3,0x74,0x53,0x7C,0x40,0x94,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacket = /* CC5BBF7E-029C-4D3B-9158-6000EF5E3670 */ {0xCC,0x5B,0xBF,0x7E,0x02,0x9C,0x4D,0x3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacketIterator = /* 3FC8994B-88FB-4C17-968F-9AAB69D964A7 */ {0x3F,0xC8,0x99,0x4B,0x88,0xFB,0x4C,0x17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7}; +BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillaryPackets = /* 6C186C0F-459E-41D8-AEE2-4812D81AEE68 */ {0x6C,0x18,0x6C,0x0F,0x45,0x9E,0x41,0xD8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68}; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillary = /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ {0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04}; BMD_CONST REFIID IID_IDeckLinkEncoderPacket = /* B693F36C-316E-4AF1-B6C2-F389A4BCA620 */ {0xB6,0x93,0xF3,0x6C,0x31,0x6E,0x4A,0xF1,0xB6,0xC2,0xF3,0x89,0xA4,0xBC,0xA6,0x20}; BMD_CONST REFIID IID_IDeckLinkEncoderVideoPacket = /* 4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0 */ {0x4E,0x7F,0xD9,0x44,0xE8,0xC7,0x4E,0xAC,0xB8,0xC0,0x7B,0x77,0xF8,0x0F,0x5A,0xE0}; @@ -113,9 +120,11 @@ enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameContainsHDRMetadata = 1 << 1, + bmdFrameContainsCintelMetadata = 1 << 2, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ + bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; @@ -159,10 +168,10 @@ enum _BMDDeckLinkCapturePassthroughMode { typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { - bmdOutputFrameCompleted, - bmdOutputFrameDisplayedLate, - bmdOutputFrameDropped, - bmdOutputFrameFlushed + bmdOutputFrameCompleted, + bmdOutputFrameDisplayedLate, + bmdOutputFrameDropped, + bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ @@ -199,9 +208,9 @@ enum _BMDAudioSampleType { typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { - bmdAudioOutputStreamContinuous, - bmdAudioOutputStreamContinuousDontResample, - bmdAudioOutputStreamTimestamped + bmdAudioOutputStreamContinuous, + bmdAudioOutputStreamContinuousDontResample, + bmdAudioOutputStreamTimestamped }; /* Enum BMDDisplayModeSupport - Output mode supported flags */ @@ -209,8 +218,17 @@ enum _BMDAudioOutputStreamType { typedef uint32_t BMDDisplayModeSupport; enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, - bmdDisplayModeSupported, - bmdDisplayModeSupportedWithConversion + bmdDisplayModeSupported, + bmdDisplayModeSupportedWithConversion +}; + +/* Enum BMDAncillaryPacketFormat - Ancillary packet format */ + +typedef uint32_t BMDAncillaryPacketFormat; +enum _BMDAncillaryPacketFormat { + bmdAncillaryPacketFormatUInt8 = /* 'ui08' */ 0x75693038, + bmdAncillaryPacketFormatUInt16 = /* 'ui16' */ 0x75693136, + bmdAncillaryPacketFormatYCbCr10 = /* 'v210' */ 0x76323130 }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ @@ -337,6 +355,37 @@ enum _BMDDeviceInterface { typedef uint32_t BMDDeckLinkFrameMetadataID; enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = /* 'eotf' */ 0x656F7466, // EOTF in range 0-7 as per CEA 861.3 + bmdDeckLinkFrameMetadataCintelFilmType = /* 'cfty' */ 0x63667479, // Current film type + bmdDeckLinkFrameMetadataCintelFilmGauge = /* 'cfga' */ 0x63666761, // Current film gauge + bmdDeckLinkFrameMetadataCintelOffsetDetectedHorizontal = /* 'odfh' */ 0x6F646668, // Horizontal offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelOffsetDetectedVertical = /* 'odfv' */ 0x6F646676, // Vertical offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelKeykodeLow = /* 'ckkl' */ 0x636B6B6C, // Raw keykode value - low 64 bits + bmdDeckLinkFrameMetadataCintelKeykodeHigh = /* 'ckkh' */ 0x636B6B68, // Raw keykode value - high 64 bits + bmdDeckLinkFrameMetadataCintelTile1Size = /* 'ct1s' */ 0x63743173, // Size in bytes of compressed raw tile 1 + bmdDeckLinkFrameMetadataCintelTile2Size = /* 'ct2s' */ 0x63743273, // Size in bytes of compressed raw tile 2 + bmdDeckLinkFrameMetadataCintelTile3Size = /* 'ct3s' */ 0x63743373, // Size in bytes of compressed raw tile 3 + bmdDeckLinkFrameMetadataCintelTile4Size = /* 'ct4s' */ 0x63743473, // Size in bytes of compressed raw tile 4 + bmdDeckLinkFrameMetadataCintelImageWidth = /* 'IWPx' */ 0x49575078, // Width in pixels of image + bmdDeckLinkFrameMetadataCintelImageHeight = /* 'IHPx' */ 0x49485078, // Height in pixels of image + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed = /* 'mrir' */ 0x6D726972, // Red in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed = /* 'mgir' */ 0x6D676972, // Green in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed = /* 'mbir' */ 0x6D626972, // Blue in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen = /* 'mrig' */ 0x6D726967, // Red in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen = /* 'mgig' */ 0x6D676967, // Green in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen = /* 'mbig' */ 0x6D626967, // Blue in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue = /* 'mrib' */ 0x6D726962, // Red in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue = /* 'mgib' */ 0x6D676962, // Green in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue = /* 'mbib' */ 0x6D626962, // Blue in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed = /* 'mlrr' */ 0x6D6C7272, // Red in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed = /* 'mlgr' */ 0x6D6C6772, // Green in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed = /* 'mlbr' */ 0x6D6C6272, // Blue in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen = /* 'mlrg' */ 0x6D6C7267, // Red in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen = /* 'mlgg' */ 0x6D6C6767, // Green in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen = /* 'mlbg' */ 0x6D6C6267, // Blue in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue = /* 'mlrb' */ 0x6D6C7262, // Red in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue = /* 'mlgb' */ 0x6D6C6762, // Green in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue = /* 'mlbb' */ 0x6D6C6262, // Blue in blue log masking parameter + bmdDeckLinkFrameMetadataCintelFilmFrameRate = /* 'cffr' */ 0x63666672, // Film frame rate bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = /* 'hdrx' */ 0x68647278, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = /* 'hdry' */ 0x68647279, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = /* 'hdgx' */ 0x68646778, // Green display primaries in range 0.0 - 1.0 @@ -348,7 +397,15 @@ enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = /* 'hdml' */ 0x68646D6C, // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = /* 'hmil' */ 0x686D696C, // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = /* 'mcll' */ 0x6D636C6C, // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 - bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C, // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal = /* 'otah' */ 0x6F746168, // Horizontal offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical = /* 'otav' */ 0x6F746176, // Vertical offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelGainRed = /* 'LfRd' */ 0x4C665264, // Red gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainGreen = /* 'LfGr' */ 0x4C664772, // Green gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainBlue = /* 'LfBl' */ 0x4C66426C, // Blue gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelLiftRed = /* 'GnRd' */ 0x476E5264, // Red lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftGreen = /* 'GnGr' */ 0x476E4772, // Green lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftBlue = /* 'GnBl' */ 0x476E426C // Blue lift parameter to apply after log and gain }; /* Enum BMDDuplexMode - Duplex for configurable ports */ @@ -390,18 +447,19 @@ enum _BMDDeckLinkAttributeID { /* Integers */ BMDDeckLinkMaximumAudioChannels = /* 'mach' */ 0x6D616368, - BMDDeckLinkMaximumAnalogAudioChannels = /* 'aach' */ 0x61616368, + BMDDeckLinkMaximumAnalogAudioInputChannels = /* 'iach' */ 0x69616368, + BMDDeckLinkMaximumAnalogAudioOutputChannels = /* 'aach' */ 0x61616368, BMDDeckLinkNumberOfSubDevices = /* 'nsbd' */ 0x6E736264, BMDDeckLinkSubDeviceIndex = /* 'subi' */ 0x73756269, BMDDeckLinkPersistentID = /* 'peid' */ 0x70656964, BMDDeckLinkDeviceGroupID = /* 'dgid' */ 0x64676964, BMDDeckLinkTopologicalID = /* 'toid' */ 0x746F6964, - BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, - BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, - BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, - BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, + BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, // Returns a BMDAudioConnection bit field + BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = /* 'vios' */ 0x76696F73, // Returns a BMDVideoIOSupport bit field - BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, + BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = /* 'dbus' */ 0x64627573, // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = /* 'airc' */ 0x61697263, BMDDeckLinkAudioInputXLRChannelCount = /* 'aixc' */ 0x61697863, @@ -455,11 +513,14 @@ enum _BMDDeckLinkStatusID { bmdDeckLinkStatusReferenceSignalFlags = /* 'reff' */ 0x72656666, bmdDeckLinkStatusDuplexMode = /* 'dupx' */ 0x64757078, bmdDeckLinkStatusBusy = /* 'busy' */ 0x62757379, + bmdDeckLinkStatusInterchangeablePanelType = /* 'icpt' */ 0x69637074, + bmdDeckLinkStatusDeviceTemperature = /* 'dtmp' */ 0x64746D70, /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = /* 'visl' */ 0x7669736C, - bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C + bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C, + bmdDeckLinkStatusReceivedEDID = /* 'edid' */ 0x65646964 }; /* Enum BMDDeckLinkVideoStatusFlags - */ @@ -480,6 +541,14 @@ enum _BMDDuplexStatus { bmdDuplexStatusInactive = /* 'inac' */ 0x696E6163 }; +/* Enum BMDPanelType - The type of interchangeable panel */ + +typedef uint32_t BMDPanelType; +enum _BMDPanelType { + bmdPanelNotDetected = /* 'npnl' */ 0x6E706E6C, + bmdPanelTeranexMiniSmartPanel = /* 'tmsm' */ 0x746D736D +}; + /* Enum BMDDeviceBusyState - Current device busy state */ typedef uint32_t BMDDeviceBusyState; @@ -535,6 +604,9 @@ class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoFrameMetadataExtensions; class IDeckLinkVideoInputFrame; +class IDeckLinkAncillaryPacket; +class IDeckLinkAncillaryPacketIterator; +class IDeckLinkVideoFrameAncillaryPackets; class IDeckLinkVideoFrameAncillary; class IDeckLinkEncoderPacket; class IDeckLinkEncoderVideoPacket; @@ -554,7 +626,7 @@ class IDeckLinkDiscovery; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ -class IDeckLinkVideoOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; @@ -566,7 +638,7 @@ class IDeckLinkVideoOutputCallback : public IUnknown /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -578,7 +650,7 @@ class IDeckLinkInputCallback : public IUnknown /* Interface IDeckLinkEncoderInputCallback - Frame arrival callback. */ -class IDeckLinkEncoderInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInputCallback : public IUnknown { public: virtual HRESULT VideoInputSignalChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -591,7 +663,7 @@ class IDeckLinkEncoderInputCallback : public IUnknown /* Interface IDeckLinkMemoryAllocator - Memory allocator for video frames. */ -class IDeckLinkMemoryAllocator : public IUnknown +class BMD_PUBLIC IDeckLinkMemoryAllocator : public IUnknown { public: virtual HRESULT AllocateBuffer (/* in */ uint32_t bufferSize, /* out */ void **allocatedBuffer) = 0; @@ -603,7 +675,7 @@ class IDeckLinkMemoryAllocator : public IUnknown /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ -class IDeckLinkAudioOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; @@ -611,7 +683,7 @@ class IDeckLinkAudioOutputCallback : public IUnknown /* Interface IDeckLinkIterator - enumerates installed DeckLink hardware */ -class IDeckLinkIterator : public IUnknown +class BMD_PUBLIC IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink **deckLinkInstance) = 0; @@ -619,7 +691,7 @@ class IDeckLinkIterator : public IUnknown /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ -class IDeckLinkAPIInformation : public IUnknown +class BMD_PUBLIC IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool *value) = 0; @@ -633,7 +705,7 @@ class IDeckLinkAPIInformation : public IUnknown /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput : public IUnknown +class BMD_PUBLIC IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -648,7 +720,7 @@ class IDeckLinkOutput : public IUnknown virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; - virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; + virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -690,7 +762,7 @@ class IDeckLinkOutput : public IUnknown /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput : public IUnknown +class BMD_PUBLIC IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -729,7 +801,7 @@ class IDeckLinkInput : public IUnknown /* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkEncoderInput : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -766,7 +838,7 @@ class IDeckLinkEncoderInput : public IUnknown /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ -class IDeckLinkVideoFrame : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; @@ -777,7 +849,7 @@ class IDeckLinkVideoFrame : public IUnknown virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) = 0; - virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; + virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred protected: virtual ~IDeckLinkVideoFrame () {} // call Release method to drop reference count @@ -785,7 +857,7 @@ class IDeckLinkVideoFrame : public IUnknown /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ -class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; @@ -801,7 +873,7 @@ class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface implemented on IDeckLinkVideoFrame to support 3D frames */ -class IDeckLinkVideoFrame3DExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; @@ -813,7 +885,7 @@ class IDeckLinkVideoFrame3DExtensions : public IUnknown /* Interface IDeckLinkVideoFrameMetadataExtensions - Optional interface implemented on IDeckLinkVideoFrame to support frame metadata such as HDMI HDR information */ -class IDeckLinkVideoFrameMetadataExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrameMetadataExtensions : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ int64_t *value) = 0; @@ -827,7 +899,7 @@ class IDeckLinkVideoFrameMetadataExtensions : public IUnknown /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -837,13 +909,56 @@ class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame virtual ~IDeckLinkVideoInputFrame () {} // call Release method to drop reference count }; -/* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ +/* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ + +class BMD_PUBLIC IDeckLinkAncillaryPacket : public IUnknown +{ +public: + + virtual HRESULT GetBytes (/* in */ BMDAncillaryPacketFormat format /* For output, only one format need be offered */, /* out */ const void **data /* Optional */, /* out */ uint32_t *size /* Optional */) = 0; + virtual uint8_t GetDID (void) = 0; + virtual uint8_t GetSDID (void) = 0; + virtual uint32_t GetLineNumber (void) = 0; // On output, zero is auto + virtual uint8_t GetDataStreamIndex (void) = 0; // Usually zero. Can only be 1 if non-SD and the first data stream is completely full + +protected: + virtual ~IDeckLinkAncillaryPacket () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ + +class BMD_PUBLIC IDeckLinkAncillaryPacketIterator : public IUnknown +{ +public: + virtual HRESULT Next (/* out */ IDeckLinkAncillaryPacket **packet) = 0; + +protected: + virtual ~IDeckLinkAncillaryPacketIterator () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +class BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets : public IUnknown +{ +public: + + virtual HRESULT GetPacketIterator (/* out */ IDeckLinkAncillaryPacketIterator **iterator) = 0; + virtual HRESULT GetFirstPacketByID (/* in */ uint8_t DID, /* in */ uint8_t SDID, /* out */ IDeckLinkAncillaryPacket **packet) = 0; + virtual HRESULT AttachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; // Implement IDeckLinkAncillaryPacket to output your own + virtual HRESULT DetachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; + virtual HRESULT DetachAllPackets (void) = 0; + +protected: + virtual ~IDeckLinkVideoFrameAncillaryPackets () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ -class IDeckLinkVideoFrameAncillary : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrameAncillary : public IUnknown { public: - virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; + virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; @@ -853,7 +968,7 @@ class IDeckLinkVideoFrameAncillary : public IUnknown /* Interface IDeckLinkEncoderPacket - Interface to encapsulate an encoded packet. */ -class IDeckLinkEncoderPacket : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderPacket : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void **buffer) = 0; @@ -867,7 +982,7 @@ class IDeckLinkEncoderPacket : public IUnknown /* Interface IDeckLinkEncoderVideoPacket - Provided by the IDeckLinkEncoderInput video packet arrival callback. */ -class IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket { public: virtual BMDPixelFormat GetPixelFormat (void) = 0; @@ -881,7 +996,7 @@ class IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket /* Interface IDeckLinkEncoderAudioPacket - Provided by the IDeckLinkEncoderInput audio packet arrival callback. */ -class IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket { public: virtual BMDAudioFormat GetAudioFormat (void) = 0; @@ -892,7 +1007,7 @@ class IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket /* Interface IDeckLinkH265NALPacket - Obtained through QueryInterface() on an IDeckLinkEncoderVideoPacket object */ -class IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket +class BMD_PUBLIC IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket { public: virtual HRESULT GetUnitType (/* out */ uint8_t *unitType) = 0; @@ -905,7 +1020,7 @@ class IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ -class IDeckLinkAudioInputPacket : public IUnknown +class BMD_PUBLIC IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; @@ -918,7 +1033,7 @@ class IDeckLinkAudioInputPacket : public IUnknown /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ -class IDeckLinkScreenPreviewCallback : public IUnknown +class BMD_PUBLIC IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; @@ -929,7 +1044,7 @@ class IDeckLinkScreenPreviewCallback : public IUnknown /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ -class IDeckLinkGLScreenPreviewHelper : public IUnknown +class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper : public IUnknown { public: @@ -946,7 +1061,7 @@ class IDeckLinkGLScreenPreviewHelper : public IUnknown /* Interface IDeckLinkNotificationCallback - DeckLink Notification Callback Interface */ -class IDeckLinkNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkNotificationCallback : public IUnknown { public: virtual HRESULT Notify (/* in */ BMDNotifications topic, /* in */ uint64_t param1, /* in */ uint64_t param2) = 0; @@ -954,7 +1069,7 @@ class IDeckLinkNotificationCallback : public IUnknown /* Interface IDeckLinkNotification - DeckLink Notification interface */ -class IDeckLinkNotification : public IUnknown +class BMD_PUBLIC IDeckLinkNotification : public IUnknown { public: virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0; @@ -963,7 +1078,7 @@ class IDeckLinkNotification : public IUnknown /* Interface IDeckLinkAttributes - DeckLink Attribute interface */ -class IDeckLinkAttributes : public IUnknown +class BMD_PUBLIC IDeckLinkAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool *value) = 0; @@ -977,7 +1092,7 @@ class IDeckLinkAttributes : public IUnknown /* Interface IDeckLinkStatus - DeckLink Status interface */ -class IDeckLinkStatus : public IUnknown +class BMD_PUBLIC IDeckLinkStatus : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkStatusID statusID, /* out */ bool *value) = 0; @@ -992,7 +1107,7 @@ class IDeckLinkStatus : public IUnknown /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ -class IDeckLinkKeyer : public IUnknown +class BMD_PUBLIC IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; @@ -1007,7 +1122,7 @@ class IDeckLinkKeyer : public IUnknown /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ -class IDeckLinkVideoConversion : public IUnknown +class BMD_PUBLIC IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; @@ -1018,7 +1133,7 @@ class IDeckLinkVideoConversion : public IUnknown /* Interface IDeckLinkDeviceNotificationCallback - DeckLink device arrival/removal notification callbacks */ -class IDeckLinkDeviceNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeviceNotificationCallback : public IUnknown { public: virtual HRESULT DeckLinkDeviceArrived (/* in */ IDeckLink* deckLinkDevice) = 0; @@ -1030,7 +1145,7 @@ class IDeckLinkDeviceNotificationCallback : public IUnknown /* Interface IDeckLinkDiscovery - DeckLink device discovery */ -class IDeckLinkDiscovery : public IUnknown +class BMD_PUBLIC IDeckLinkDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IDeckLinkDeviceNotificationCallback* deviceNotificationCallback) = 0; @@ -1044,11 +1159,12 @@ class IDeckLinkDiscovery : public IUnknown extern "C" { - IDeckLinkIterator* CreateDeckLinkIteratorInstance (void); - IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void); - IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void); - IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void); - IDeckLinkVideoConversion* CreateVideoConversionInstance (void); + IDeckLinkIterator* BMD_PUBLIC CreateDeckLinkIteratorInstance (void); + IDeckLinkDiscovery* BMD_PUBLIC CreateDeckLinkDiscoveryInstance (void); + IDeckLinkAPIInformation* BMD_PUBLIC CreateDeckLinkAPIInformationInstance (void); + IDeckLinkGLScreenPreviewHelper* BMD_PUBLIC CreateOpenGLScreenPreviewHelper (void); + IDeckLinkVideoConversion* BMD_PUBLIC CreateVideoConversionInstance (void); + IDeckLinkVideoFrameAncillaryPackets* BMD_PUBLIC CreateVideoFrameAncillaryPacketsInstance (void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame } diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h index dedf15e96..8cb6bac0b 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,12 +37,16 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations // Interface ID Declarations -BMD_CONST REFIID IID_IDeckLinkConfiguration = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; +BMD_CONST REFIID IID_IDeckLinkConfiguration = /* EF90380B-4AE5-4346-9077-E288E149F129 */ {0xEF,0x90,0x38,0x0B,0x4A,0xE5,0x43,0x46,0x90,0x77,0xE2,0x88,0xE1,0x49,0xF1,0x29}; BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration = /* 138050E5-C60A-4552-BF3F-0F358049327E */ {0x13,0x80,0x50,0xE5,0xC6,0x0A,0x45,0x52,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E}; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ @@ -54,10 +58,6 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigSwapSerialRxTx = /* 'ssrt' */ 0x73737274, - /* Video Input/Output Flags */ - - bmdDeckLinkConfigUse1080pNotPsF = /* 'fpro' */ 0x6670726F, - /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066, @@ -78,6 +78,12 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F, bmdDeckLinkConfigDownConversionOnAllAnalogOutput = /* 'caao' */ 0x6361616F, bmdDeckLinkConfigSMPTELevelAOutput = /* 'smta' */ 0x736D7461, + bmdDeckLinkConfigRec2020Output = /* 'rec2' */ 0x72656332, // Ensure output is Rec.2020 colorspace + bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = /* 'SDQS' */ 0x53445153, + + /* Video Output Flags */ + + bmdDeckLinkConfigOutput1080pAsPsF = /* 'pfpr' */ 0x70667072, /* Video Output Integers */ @@ -106,6 +112,10 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigUseDedicatedLTCInput = /* 'dltc' */ 0x646C7463, // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = /* '3dds' */ 0x33646473, + /* Video Input Flags */ + + bmdDeckLinkConfigCapture1080pAsPsF = /* 'cfpr' */ 0x63667072, + /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E, @@ -206,7 +216,7 @@ class IDeckLinkEncoderConfiguration; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ -class IDeckLinkConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; @@ -225,7 +235,7 @@ class IDeckLinkConfiguration : public IUnknown /* Interface IDeckLinkEncoderConfiguration - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ -class IDeckLinkEncoderConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h index 30bd5ae54..457f041e9 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ class IDeckLinkConfiguration_v10_2; /* Interface IDeckLinkConfiguration_v10_2 - DeckLink Configuration interface */ -class IDeckLinkConfiguration_v10_2 : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration_v10_2 : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h index 750387ffb..c4d340f6f 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h @@ -42,7 +42,7 @@ class IDeckLinkConfiguration_v10_4; /* Interface IDeckLinkConfiguration_v10_4 - DeckLink Configuration interface */ -class IDeckLinkConfiguration_v10_4 : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration_v10_4 : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h index da36e5022..684b83bf0 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ class IDeckLinkEncoderConfiguration_v10_5; /* Interface IDeckLinkEncoderConfiguration_v10_5 - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ -class IDeckLinkEncoderConfiguration_v10_5 : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderConfiguration_v10_5 : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h new file mode 100644 index 000000000..f507637a4 --- /dev/null +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h @@ -0,0 +1,62 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPICONFIGURATION_v10_9_H +#define BMD_DECKLINKAPICONFIGURATION_v10_9_H + +#include "DeckLinkAPIConfiguration.h" + +// Interface ID Declarations + +BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_9 = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; + +// +// Forward Declarations + +class IDeckLinkConfiguration_v10_9; + +/* Interface IDeckLinkConfiguration_v10_9 - DeckLink Configuration interface */ + +class BMD_PUBLIC IDeckLinkConfiguration_v10_9 : public IUnknown +{ +public: + virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; + virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0; + virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; + virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0; + virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; + virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0; + virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char *value) = 0; + virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char **value) = 0; + virtual HRESULT WriteConfigurationToPreferences (void) = 0; + +protected: + virtual ~IDeckLinkConfiguration_v10_9 () {} // call Release method to drop reference count +}; + + +#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_9_H) */ diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h index 8dc069c52..3d3f22117 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -149,7 +153,7 @@ class IDeckLinkDeckControl; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ -class IDeckLinkDeckControlStatusCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; @@ -163,7 +167,7 @@ class IDeckLinkDeckControlStatusCallback : public IUnknown /* Interface IDeckLinkDeckControl - Deck Control main interface */ -class IDeckLinkDeckControl : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h index d9253ffa3..e9164f913 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -50,7 +54,7 @@ class IDeckLink; /* Interface IDeckLink - represents a DeckLink device */ -class IDeckLink : public IUnknown +class BMD_PUBLIC IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ const char **modelName) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp index f6392864a..ff5e3b085 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -39,6 +39,7 @@ typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); +typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT; @@ -50,21 +51,22 @@ static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL; +static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL; void InitDeckLinkAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } - + gLoadedDeckLinkAPI = true; - - gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0002"); + + gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0003"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001"); @@ -73,15 +75,18 @@ void InitDeckLinkAPI (void) gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001"); if (!gCreateVideoConversionFunc) fprintf(stderr, "%s\n", dlerror()); - gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0001"); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0002"); if (!gCreateDeckLinkDiscoveryFunc) fprintf(stderr, "%s\n", dlerror()); + gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0001"); + if (!gCreateVideoFrameAncillaryPacketsFunc) + fprintf(stderr, "%s\n", dlerror()); } void InitDeckLinkPreviewAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { @@ -102,7 +107,7 @@ bool IsDeckLinkAPIPresent (void) IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); @@ -111,7 +116,7 @@ IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; return gCreateAPIInformationFunc(); @@ -121,7 +126,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); @@ -130,7 +135,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); @@ -139,8 +144,17 @@ IDeckLinkVideoConversion* CreateVideoConversionInstance (void) IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateDeckLinkDiscoveryFunc == NULL) return NULL; return gCreateDeckLinkDiscoveryFunc(); } + +IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance (void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoFrameAncillaryPacketsFunc == NULL) + return NULL; + return gCreateVideoFrameAncillaryPacketsFunc(); +} diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp new file mode 100644 index 000000000..6a038fb12 --- /dev/null +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp @@ -0,0 +1,146 @@ +/* -LICENSE-START- +** Copyright (c) 2009 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +**/ + +#include +#include +#include + +#include "DeckLinkAPI.h" + +#define kDeckLinkAPI_Name "libDeckLinkAPI.so" +#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so" + +typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); +typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); +typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); +typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); +typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); + +static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; +static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT; + +static bool gLoadedDeckLinkAPI = false; + +static CreateIteratorFunc gCreateIteratorFunc = NULL; +static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; +static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; +static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; +static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL; + +void InitDeckLinkAPI(void) +{ + void *libraryHandle; + + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW | RTLD_GLOBAL); + if (!libraryHandle) + { + fprintf(stderr, "%s\n", dlerror()); + return; + } + + gLoadedDeckLinkAPI = true; + + gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0002"); + if (!gCreateIteratorFunc) + fprintf(stderr, "%s\n", dlerror()); + gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001"); + if (!gCreateAPIInformationFunc) + fprintf(stderr, "%s\n", dlerror()); + gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001"); + if (!gCreateVideoConversionFunc) + fprintf(stderr, "%s\n", dlerror()); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0001"); + if (!gCreateDeckLinkDiscoveryFunc) + fprintf(stderr, "%s\n", dlerror()); +} + +void InitDeckLinkPreviewAPI(void) +{ + void *libraryHandle; + + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW | RTLD_GLOBAL); + if (!libraryHandle) + { + fprintf(stderr, "%s\n", dlerror()); + return; + } + gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0001"); + if (!gCreateOpenGLPreviewFunc) + fprintf(stderr, "%s\n", dlerror()); +} + +bool IsDeckLinkAPIPresent(void) +{ + // If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller + return gLoadedDeckLinkAPI; +} + +IDeckLinkIterator* CreateDeckLinkIteratorInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateIteratorFunc == NULL) + return NULL; + return gCreateIteratorFunc(); +} + +IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateAPIInformationFunc == NULL) + return NULL; + return gCreateAPIInformationFunc(); +} + +IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); + + if (gCreateOpenGLPreviewFunc == NULL) + return NULL; + return gCreateOpenGLPreviewFunc(); +} + +IDeckLinkVideoConversion* CreateVideoConversionInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoConversionFunc == NULL) + return NULL; + return gCreateVideoConversionFunc(); +} + +IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateDeckLinkDiscoveryFunc == NULL) + return NULL; + return gCreateDeckLinkDiscoveryFunc(); +} diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp index 4963060c8..9ec157f15 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -48,14 +48,14 @@ static CreateVideoConversionInstanceFunc_v7_6 gCreateVideoConversionFunc = NULL void InitDeckLinkAPI_v7_6 (void) { void *libraryHandle; - + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } - + gCreateIteratorFunc = (CreateIteratorFunc_v7_6)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); @@ -67,7 +67,7 @@ void InitDeckLinkAPI_v7_6 (void) void InitDeckLinkPreviewAPI_v7_6 (void) { void *libraryHandle; - + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { @@ -82,7 +82,7 @@ void InitDeckLinkPreviewAPI_v7_6 (void) IDeckLinkIterator* CreateDeckLinkIteratorInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); @@ -92,7 +92,7 @@ IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI_v7_6); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); @@ -101,7 +101,7 @@ IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void IDeckLinkVideoConversion_v7_6* CreateVideoConversionInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp index 770f14352..686fd672c 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -52,16 +52,16 @@ static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; void InitDeckLinkAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } - + gLoadedDeckLinkAPI = true; - + gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0001"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); @@ -76,7 +76,7 @@ void InitDeckLinkAPI (void) void InitDeckLinkPreviewAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { @@ -97,7 +97,7 @@ bool IsDeckLinkAPIPresent (void) IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); @@ -106,7 +106,7 @@ IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance (void) IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; return gCreateAPIInformationFunc(); @@ -116,7 +116,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); @@ -125,7 +125,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h index 6c5205127..817f828c9 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -65,12 +69,12 @@ enum _BMDDisplayMode { bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235, bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239, bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330, - bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, - bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, - bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530, bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539, bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz. + bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, + bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, + bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ @@ -78,19 +82,24 @@ enum _BMDDisplayMode { bmdModeHD720p5994 = /* 'hp59' */ 0x68703539, bmdModeHD720p60 = /* 'hp60' */ 0x68703630, - /* 2k Modes */ + /* 2K Modes */ bmdMode2k2398 = /* '2k23' */ 0x326B3233, bmdMode2k24 = /* '2k24' */ 0x326B3234, bmdMode2k25 = /* '2k25' */ 0x326B3235, - /* DCI Modes (output only) */ + /* 2K DCI Modes */ bmdMode2kDCI2398 = /* '2d23' */ 0x32643233, bmdMode2kDCI24 = /* '2d24' */ 0x32643234, bmdMode2kDCI25 = /* '2d25' */ 0x32643235, + bmdMode2kDCI2997 = /* '2d29' */ 0x32643239, + bmdMode2kDCI30 = /* '2d30' */ 0x32643330, + bmdMode2kDCI50 = /* '2d50' */ 0x32643530, + bmdMode2kDCI5994 = /* '2d59' */ 0x32643539, + bmdMode2kDCI60 = /* '2d60' */ 0x32643630, - /* 4k Modes */ + /* 4K UHD Modes */ bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233, bmdMode4K2160p24 = /* '4k24' */ 0x346B3234, @@ -101,11 +110,43 @@ enum _BMDDisplayMode { bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539, bmdMode4K2160p60 = /* '4k60' */ 0x346B3630, - /* DCI Modes (output only) */ + /* 4K DCI Modes */ bmdMode4kDCI2398 = /* '4d23' */ 0x34643233, bmdMode4kDCI24 = /* '4d24' */ 0x34643234, bmdMode4kDCI25 = /* '4d25' */ 0x34643235, + bmdMode4kDCI2997 = /* '4d29' */ 0x34643239, + bmdMode4kDCI30 = /* '4d30' */ 0x34643330, + bmdMode4kDCI50 = /* '4d50' */ 0x34643530, + bmdMode4kDCI5994 = /* '4d59' */ 0x34643539, + bmdMode4kDCI60 = /* '4d60' */ 0x34643630, + + /* 8K UHD Modes */ + + bmdMode8K4320p2398 = /* '8k23' */ 0x386B3233, + bmdMode8K4320p24 = /* '8k24' */ 0x386B3234, + bmdMode8K4320p25 = /* '8k25' */ 0x386B3235, + bmdMode8K4320p2997 = /* '8k29' */ 0x386B3239, + bmdMode8K4320p30 = /* '8k30' */ 0x386B3330, + bmdMode8K4320p50 = /* '8k50' */ 0x386B3530, + bmdMode8K4320p5994 = /* '8k59' */ 0x386B3539, + bmdMode8K4320p60 = /* '8k60' */ 0x386B3630, + + /* 8K DCI Modes */ + + bmdMode8kDCI2398 = /* '8d23' */ 0x38643233, + bmdMode8kDCI24 = /* '8d24' */ 0x38643234, + bmdMode8kDCI25 = /* '8d25' */ 0x38643235, + bmdMode8kDCI2997 = /* '8d29' */ 0x38643239, + bmdMode8kDCI30 = /* '8d30' */ 0x38643330, + bmdMode8kDCI50 = /* '8d50' */ 0x38643530, + bmdMode8kDCI5994 = /* '8d59' */ 0x38643539, + bmdMode8kDCI60 = /* '8d60' */ 0x38643630, + + /* RAW Modes for Cintel (input only) */ + + bmdModeCintelRAW = /* 'rwci' */ 0x72776369, // Frame size up to 4096x3072, variable frame rate + bmdModeCintelCompressedRAW = /* 'rwcc' */ 0x72776363, // Frame size up to 4096x3072, variable frame rate /* Special Modes */ @@ -140,7 +181,12 @@ enum _BMDPixelFormat { /* AVID DNxHR */ - bmdFormatDNxHR = /* 'AVdh' */ 0x41566468 + bmdFormatDNxHR = /* 'AVdh' */ 0x41566468, + + /* Cintel formats */ + + bmdFormat12BitRAWGRBG = /* 'r12p' */ 0x72313270, // 12-bit RAW data for bayer pattern GRBG + bmdFormat12BitRAWJPEG = /* 'r16p' */ 0x72313670 // 12-bit RAW data arranged in tiles and JPEG compressed }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ @@ -159,7 +205,7 @@ class IDeckLinkDisplayMode; /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ -class IDeckLinkDisplayModeIterator : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; @@ -170,7 +216,7 @@ class IDeckLinkDisplayModeIterator : public IUnknown /* Interface IDeckLinkDisplayMode - represents a display mode */ -class IDeckLinkDisplayMode : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ const char **name) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h index 979a9aa99..6749a8009 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations typedef int64_t BMDTimeValue; @@ -54,7 +58,8 @@ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, - bmdTimecodeFieldMark = 1 << 1 + bmdTimecodeFieldMark = 1 << 1, + bmdTimecodeColorFrame = 1 << 2 }; /* Enum BMDVideoConnection - Video connection types */ @@ -96,7 +101,7 @@ class IDeckLinkTimecode; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ -class IDeckLinkTimecode : public IUnknown +class BMD_PUBLIC IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h index dfd1799e5..995318d3f 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h @@ -7,14 +7,14 @@ * ** execute, and transmit the Software, and to prepare derivative works of the * ** Software, and to permit third-parties to whom the Software is furnished to * ** do so, all subject to the following: - * ** + * ** * ** The copyright notices in the Software and this entire statement, including * ** the above license grant, this restriction and the following disclaimer, * ** must be included in all copies of the Software, in whole or in part, and * ** all derivative works of the Software, unless such copies or derivative * ** works are solely in the form of machine-executable object code generated by * ** a source language processor. - * ** + * ** * ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -30,8 +30,8 @@ #ifndef __DeckLink_API_Version_h__ #define __DeckLink_API_Version_h__ -#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a080000 -#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.8" +#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a0b0000 +#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.11" #endif // __DeckLink_API_Version_h__ diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h index a4651330f..ad2ff32d7 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,7 +37,7 @@ typedef uint32_t BMDDeckLinkConfigurationID_v10_2; enum _BMDDeckLinkConfigurationID_v10_2 { /* Video output flags */ - + bmdDeckLinkConfig3GBpsVideoOutput_v10_2 = '3gbs', }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_9.h new file mode 100644 index 000000000..aec8c7c20 --- /dev/null +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_9.h @@ -0,0 +1,45 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPI_v10_9_H +#define BMD_DECKLINKAPI_v10_9_H + +#include "DeckLinkAPI.h" + +// Type Declarations + +/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ + +typedef uint32_t BMDDeckLinkConfigurationID_v10_9; +enum _BMDDeckLinkConfigurationID_v10_9 { + + /* Flags */ + + bmdDeckLinkConfig1080pNotPsF_v10_9 = 'fpro', +}; + +#endif /* defined(BMD_DECKLINKAPI_v10_9_H) */ diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h index 1602d6a30..b0c637cfe 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -66,14 +66,14 @@ class IDeckLinkVideoFrame_v7_1; class IDeckLinkVideoInputFrame_v7_1; class IDeckLinkAudioInputPacket_v7_1; -class IDeckLinkDisplayModeIterator_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next (IDeckLinkDisplayMode_v7_1* *deckLinkDisplayMode) = 0; }; -class IDeckLinkDisplayMode_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetName (const char **name) = 0; @@ -83,56 +83,56 @@ class IDeckLinkDisplayMode_v7_1 : public IUnknown virtual HRESULT STDMETHODCALLTYPE GetFrameRate (BMDTimeValue *frameDuration, BMDTimeScale *timeScale) = 0; }; -class IDeckLinkVideoOutputCallback_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted (IDeckLinkVideoFrame_v7_1* completedFrame, BMDOutputFrameCompletionResult result) = 0; }; -class IDeckLinkInputCallback_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived (IDeckLinkVideoInputFrame_v7_1* videoFrame, IDeckLinkAudioInputPacket_v7_1* audioPacket) = 0; }; // IDeckLinkOutput_v7_1. Created by QueryInterface from IDeckLink. -class IDeckLinkOutput_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v7_1 : public IUnknown { public: // Display mode predicates virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1* *iterator) = 0; - - + + // Video output virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput (BMDDisplayMode displayMode) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator (IDeckLinkMemoryAllocator* theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame (int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrameFromBuffer (void* buffer, int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; - + virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync (IDeckLinkVideoFrame_v7_1* theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame (IDeckLinkVideoFrame_v7_1* theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback (IDeckLinkVideoOutputCallback_v7_1* theCallback) = 0; - - + + // Audio output virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples (void* buffer, uint32_t sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetAudioCallback (IDeckLinkAudioOutputCallback* theCallback) = 0; - - + + // Output control virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback (BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback (BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale) = 0; @@ -140,23 +140,23 @@ class IDeckLinkOutput_v7_1 : public IUnknown }; // IDeckLinkInput_v7_1. Created by QueryInterface from IDeckLink. -class IDeckLinkInput_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1 **iterator) = 0; - + // Video input virtual HRESULT STDMETHODCALLTYPE EnableVideoInput (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput () = 0; - + // Audio input virtual HRESULT STDMETHODCALLTYPE EnableAudioInput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput () = 0; virtual HRESULT STDMETHODCALLTYPE ReadAudioSamples (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesRead, BMDTimeValue *audioPacketTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; + - // Input control virtual HRESULT STDMETHODCALLTYPE StartStreams () = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams () = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams () = 0; @@ -164,7 +164,7 @@ class IDeckLinkInput_v7_1 : public IUnknown }; // IDeckLinkVideoFrame_v7_1. Created by IDeckLinkOutput::CreateVideoFrame. -class IDeckLinkVideoFrame_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame_v7_1 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetWidth () = 0; @@ -176,19 +176,19 @@ class IDeckLinkVideoFrame_v7_1 : public IUnknown }; // IDeckLinkVideoInputFrame_v7_1. Provided by the IDeckLinkInput_v7_1 frame arrival callback. -class IDeckLinkVideoInputFrame_v7_1 : public IDeckLinkVideoFrame_v7_1 +class BMD_PUBLIC IDeckLinkVideoInputFrame_v7_1 : public IDeckLinkVideoFrame_v7_1 { public: virtual HRESULT STDMETHODCALLTYPE GetFrameTime (BMDTimeValue *frameTime, BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; }; // IDeckLinkAudioInputPacket_v7_1. Provided by the IDeckLinkInput_v7_1 callback. -class IDeckLinkAudioInputPacket_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkAudioInputPacket_v7_1 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetSampleCount () = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes (void* *buffer) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetAudioPacketTime (BMDTimeValue *packetTime, BMDTimeScale timeScale) = 0; }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h index c8b3da80f..bb3192ea4 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -53,7 +53,7 @@ class IDeckLinkVideoInputFrame_v7_3; /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput_v7_3 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v7_3 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -107,7 +107,7 @@ class IDeckLinkOutput_v7_3 : public IUnknown /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback_v7_3 : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback_v7_3 : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -122,7 +122,7 @@ class IDeckLinkInputCallback_v7_3 : public IUnknown /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput_v7_3 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v7_3 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -158,7 +158,7 @@ class IDeckLinkInput_v7_3 : public IUnknown /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame_v7_3 : public IDeckLinkVideoFrame_v7_6 +class BMD_PUBLIC IDeckLinkVideoInputFrame_v7_3 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h index 8bc9329d8..d98120651 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -83,7 +83,7 @@ class IDeckLinkVideoConversion_v7_6; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ -class IDeckLinkVideoOutputCallback_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback_v7_6 : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame_v7_6 *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; @@ -96,7 +96,7 @@ class IDeckLinkVideoOutputCallback_v7_6 : public IUnknown /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback_v7_6 : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -109,7 +109,7 @@ class IDeckLinkInputCallback_v7_6 : public IUnknown /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ -class IDeckLinkDisplayModeIterator_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator_v7_6 : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode_v7_6 **deckLinkDisplayMode) = 0; @@ -121,7 +121,7 @@ class IDeckLinkDisplayModeIterator_v7_6 : public IUnknown /* Interface IDeckLinkDisplayMode - represents a display mode */ -class IDeckLinkDisplayMode_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode_v7_6 : public IUnknown { public: virtual HRESULT GetName (/* out */ const char **name) = 0; @@ -138,7 +138,7 @@ class IDeckLinkDisplayMode_v7_6 : public IUnknown /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v7_6 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -194,7 +194,7 @@ class IDeckLinkOutput_v7_6 : public IUnknown /* Interface IDeckLinkInput_v7_6 - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v7_6 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -233,7 +233,7 @@ class IDeckLinkInput_v7_6 : public IUnknown /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ -class IDeckLinkTimecode_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkTimecode_v7_6 : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; @@ -248,7 +248,7 @@ class IDeckLinkTimecode_v7_6 : public IUnknown /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ -class IDeckLinkVideoFrame_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame_v7_6 : public IUnknown { public: virtual long GetWidth (void) = 0; @@ -268,7 +268,7 @@ class IDeckLinkVideoFrame_v7_6 : public IUnknown /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ -class IDeckLinkMutableVideoFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 +class BMD_PUBLIC IDeckLinkMutableVideoFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT SetFlags (BMDFrameFlags newFlags) = 0; @@ -284,7 +284,7 @@ class IDeckLinkMutableVideoFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 +class BMD_PUBLIC IDeckLinkVideoInputFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; @@ -297,7 +297,7 @@ class IDeckLinkVideoInputFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ -class IDeckLinkScreenPreviewCallback_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkScreenPreviewCallback_v7_6 : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame_v7_6 *theFrame) = 0; @@ -309,7 +309,7 @@ class IDeckLinkScreenPreviewCallback_v7_6 : public IUnknown /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ -class IDeckLinkGLScreenPreviewHelper_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper_v7_6 : public IUnknown { public: @@ -326,7 +326,7 @@ class IDeckLinkGLScreenPreviewHelper_v7_6 : public IUnknown /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ -class IDeckLinkVideoConversion_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoConversion_v7_6 : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame_v7_6* srcFrame, /* in */ IDeckLinkVideoFrame_v7_6* dstFrame) = 0; @@ -337,54 +337,54 @@ class IDeckLinkVideoConversion_v7_6 : public IUnknown /* Interface IDeckLinkConfiguration - Created by QueryInterface from IDeckLink. */ -class IDeckLinkConfiguration_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration_v7_6 : public IUnknown { public: virtual HRESULT GetConfigurationValidator (/* out */ IDeckLinkConfiguration_v7_6 **configObject) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; - + /* Video Output Configuration */ - + virtual HRESULT SetVideoOutputFormat (/* in */ BMDVideoConnection_v7_6 videoOutputConnection) = 0; virtual HRESULT IsVideoOutputActive (/* in */ BMDVideoConnection_v7_6 videoOutputConnection, /* out */ bool *active) = 0; - + virtual HRESULT SetAnalogVideoOutputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoOutputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT EnableFieldFlickerRemovalWhenPaused (/* in */ bool enable) = 0; virtual HRESULT IsEnabledFieldFlickerRemovalWhenPaused (/* out */ bool *enabled) = 0; - + virtual HRESULT Set444And3GBpsVideoOutput (/* in */ bool enable444VideoOutput, /* in */ bool enable3GbsOutput) = 0; virtual HRESULT Get444And3GBpsVideoOutput (/* out */ bool *is444VideoOutputEnabled, /* out */ bool *threeGbsOutputEnabled) = 0; - + virtual HRESULT SetVideoOutputConversionMode (/* in */ BMDVideoOutputConversionMode conversionMode) = 0; virtual HRESULT GetVideoOutputConversionMode (/* out */ BMDVideoOutputConversionMode *conversionMode) = 0; - + virtual HRESULT Set_HD1080p24_to_HD1080i5994_Conversion (/* in */ bool enable) = 0; virtual HRESULT Get_HD1080p24_to_HD1080i5994_Conversion (/* out */ bool *enabled) = 0; - + /* Video Input Configuration */ - + virtual HRESULT SetVideoInputFormat (/* in */ BMDVideoConnection_v7_6 videoInputFormat) = 0; virtual HRESULT GetVideoInputFormat (/* out */ BMDVideoConnection_v7_6 *videoInputFormat) = 0; - + virtual HRESULT SetAnalogVideoInputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoInputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT SetVideoInputConversionMode (/* in */ BMDVideoInputConversionMode conversionMode) = 0; virtual HRESULT GetVideoInputConversionMode (/* out */ BMDVideoInputConversionMode *conversionMode) = 0; - + virtual HRESULT SetBlackVideoOutputDuringCapture (/* in */ bool blackOutInCapture) = 0; virtual HRESULT GetBlackVideoOutputDuringCapture (/* out */ bool *blackOutInCapture) = 0; - + virtual HRESULT Set32PulldownSequenceInitialTimecodeFrame (/* in */ uint32_t aFrameTimecode) = 0; virtual HRESULT Get32PulldownSequenceInitialTimecodeFrame (/* out */ uint32_t *aFrameTimecode) = 0; - + virtual HRESULT SetVancSourceLineMapping (/* in */ uint32_t activeLine1VANCsource, /* in */ uint32_t activeLine2VANCsource, /* in */ uint32_t activeLine3VANCsource) = 0; virtual HRESULT GetVancSourceLineMapping (/* out */ uint32_t *activeLine1VANCsource, /* out */ uint32_t *activeLine2VANCsource, /* out */ uint32_t *activeLine3VANCsource) = 0; - + /* Audio Input Configuration */ - + virtual HRESULT SetAudioInputFormat (/* in */ BMDAudioConnection audioInputFormat) = 0; virtual HRESULT GetAudioInputFormat (/* out */ BMDAudioConnection *audioInputFormat) = 0; }; @@ -393,9 +393,9 @@ class IDeckLinkConfiguration_v7_6 : public IUnknown extern "C" { - IDeckLinkIterator* CreateDeckLinkIteratorInstance_v7_6 (void); - IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void); - IDeckLinkVideoConversion_v7_6* CreateVideoConversionInstance_v7_6 (void); + IDeckLinkIterator* BMD_PUBLIC CreateDeckLinkIteratorInstance_v7_6 (void); + IDeckLinkGLScreenPreviewHelper_v7_6* BMD_PUBLIC CreateOpenGLScreenPreviewHelper_v7_6 (void); + IDeckLinkVideoConversion_v7_6* BMD_PUBLIC CreateVideoConversionInstance_v7_6 (void); }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h index 406ce0a2c..c8e3faa28 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -42,7 +42,7 @@ class IDeckLinkDeckControl_v7_9; /* Interface IDeckLinkDeckControl_v7_9 - Deck Control main interface */ -class IDeckLinkDeckControl_v7_9 : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl_v7_9 : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; @@ -77,7 +77,7 @@ class IDeckLinkDeckControl_v7_9 : public IUnknown virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback *callback) = 0; - + protected: virtual ~IDeckLinkDeckControl_v7_9 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h index 832664c74..6cace7e10 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -39,7 +39,7 @@ /* Interface IDeckLink_v8_0 - represents a DeckLink device */ -class IDeckLink_v8_0 : public IUnknown +class BMD_PUBLIC IDeckLink_v8_0 : public IUnknown { public: virtual HRESULT GetModelName (/* out */ const char **modelName) = 0; @@ -47,14 +47,14 @@ class IDeckLink_v8_0 : public IUnknown /* Interface IDeckLinkIterator_v8_0 - enumerates installed DeckLink hardware */ -class IDeckLinkIterator_v8_0 : public IUnknown +class BMD_PUBLIC IDeckLinkIterator_v8_0 : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink_v8_0 **deckLinkInstance) = 0; }; extern "C" { - IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance_v8_0 (void); + IDeckLinkIterator_v8_0* BMD_PUBLIC CreateDeckLinkIteratorInstance_v8_0 (void); }; #endif // defined __cplusplus diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h index 7f68919b3..c7362aa4d 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: - ** + ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. - ** + ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -52,21 +52,21 @@ enum _BMDDeckControlVTRControlState_v8_1 { /* Interface IDeckLinkDeckControlStatusCallback_v8_1 - Deck control state change callback. */ -class IDeckLinkDeckControlStatusCallback_v8_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControlStatusCallback_v8_1 : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState_v8_1 newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; - + protected: virtual ~IDeckLinkDeckControlStatusCallback_v8_1 () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDeckControl_v8_1 - Deck Control main interface */ -class IDeckLinkDeckControl_v8_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl_v8_1 : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; @@ -102,7 +102,7 @@ class IDeckLinkDeckControl_v8_1 : public IUnknown virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback_v8_1 *callback) = 0; - + protected: virtual ~IDeckLinkDeckControl_v8_1 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h index 236d1823b..dd5f83f37 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput_v9_2 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v9_2 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h index 98d115d50..9a51bf242 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput_v9_9 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v9_9 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/LinuxCOM.h b/plugins/decklink/linux/decklink-sdk/LinuxCOM.h index 3041ea7e7..663602bc5 100644 --- a/plugins/decklink/linux/decklink-sdk/LinuxCOM.h +++ b/plugins/decklink/linux/decklink-sdk/LinuxCOM.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -29,7 +29,7 @@ #define __LINUX_COM_H_ struct REFIID -{ +{ unsigned char byte0; unsigned char byte1; unsigned char byte2; @@ -85,14 +85,19 @@ typedef void *LPVOID; #define IID_IUnknown (REFIID){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} #define IUnknownUUID IID_IUnknown +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + #ifdef __cplusplus -class IUnknown +class BMD_PUBLIC IUnknown { public: - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; - virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; - virtual ULONG STDMETHODCALLTYPE Release(void) = 0; + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; + virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; + virtual ULONG STDMETHODCALLTYPE Release(void) = 0; }; #endif -#endif +#endif + diff --git a/plugins/decklink/mac/CMakeLists.txt b/plugins/decklink/mac/CMakeLists.txt index d016470cd..2a2ed216f 100644 --- a/plugins/decklink/mac/CMakeLists.txt +++ b/plugins/decklink/mac/CMakeLists.txt @@ -5,9 +5,9 @@ if(DISABLE_DECKLINK) return() endif() -find_library(COREFOUNDATION CoreFoundation) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}") -include_directories(${COREFOUNDATION}) +find_library(COREFOUNDATION CoreFoundation) set(mac-decklink-sdk_HEADERS decklink-sdk/DeckLinkAPI.h @@ -17,16 +17,19 @@ set(mac-decklink-sdk_HEADERS decklink-sdk/DeckLinkAPIModes.h decklink-sdk/DeckLinkAPIStreaming.h decklink-sdk/DeckLinkAPITypes.h - decklink-sdk/DeckLinkAPIVersion.h - ) + decklink-sdk/DeckLinkAPIVersion.h) set(mac-decklink-sdk_SOURCES decklink-sdk/DeckLinkAPIDispatch.cpp ) set(mac-decklink_HEADERS + ../decklink-devices.hpp + ../const.h + ../DecklinkOutput.hpp ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp @@ -37,19 +40,34 @@ set(mac-decklink_HEADERS set(mac-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../decklink-output.cpp + ../decklink-source.cpp + ../DecklinkOutput.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ) + +list(APPEND decklink_HEADERS ${decklink_UI_HEADERS}) + +include_directories( + ${COREFOUNDATION} + "${CMAKE_SOURCE_DIR}/UI/obs-frontend-api") + +list(APPEND mac-decklink_HEADERS ${decklink_UI_HEADERS}) add_library(mac-decklink MODULE ${mac-decklink_SOURCES} ${mac-decklink_HEADERS} ${mac-decklink-sdk_HEADERS} - ${mac-decklink-sdk_SOURCES}) + ${mac-decklink-sdk_SOURCES} + ) target_link_libraries(mac-decklink libobs diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h index 3b0fc117c..2bba5a850 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + /* DeckLink API */ #include @@ -73,6 +77,9 @@ BMD_CONST REFIID IID_IDeckLinkMutableVideoFrame = /* 69E2639F- BMD_CONST REFIID IID_IDeckLinkVideoFrame3DExtensions = /* DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7 */ {0xDA,0x0F,0x7E,0x4A,0xED,0xC7,0x48,0xA8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7}; BMD_CONST REFIID IID_IDeckLinkVideoFrameMetadataExtensions = /* D5973DC9-6432-46D0-8F0B-2496F8A1238F */ {0xD5,0x97,0x3D,0xC9,0x64,0x32,0x46,0xD0,0x8F,0x0B,0x24,0x96,0xF8,0xA1,0x23,0x8F}; BMD_CONST REFIID IID_IDeckLinkVideoInputFrame = /* 05CFE374-537C-4094-9A57-680525118F44 */ {0x05,0xCF,0xE3,0x74,0x53,0x7C,0x40,0x94,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacket = /* CC5BBF7E-029C-4D3B-9158-6000EF5E3670 */ {0xCC,0x5B,0xBF,0x7E,0x02,0x9C,0x4D,0x3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacketIterator = /* 3FC8994B-88FB-4C17-968F-9AAB69D964A7 */ {0x3F,0xC8,0x99,0x4B,0x88,0xFB,0x4C,0x17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7}; +BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillaryPackets = /* 6C186C0F-459E-41D8-AEE2-4812D81AEE68 */ {0x6C,0x18,0x6C,0x0F,0x45,0x9E,0x41,0xD8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68}; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillary = /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ {0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04}; BMD_CONST REFIID IID_IDeckLinkEncoderPacket = /* B693F36C-316E-4AF1-B6C2-F389A4BCA620 */ {0xB6,0x93,0xF3,0x6C,0x31,0x6E,0x4A,0xF1,0xB6,0xC2,0xF3,0x89,0xA4,0xBC,0xA6,0x20}; BMD_CONST REFIID IID_IDeckLinkEncoderVideoPacket = /* 4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0 */ {0x4E,0x7F,0xD9,0x44,0xE8,0xC7,0x4E,0xAC,0xB8,0xC0,0x7B,0x77,0xF8,0x0F,0x5A,0xE0}; @@ -117,9 +124,11 @@ enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameContainsHDRMetadata = 1 << 1, + bmdFrameContainsCintelMetadata = 1 << 2, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ + bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; @@ -163,10 +172,10 @@ enum _BMDDeckLinkCapturePassthroughMode { typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { - bmdOutputFrameCompleted, - bmdOutputFrameDisplayedLate, - bmdOutputFrameDropped, - bmdOutputFrameFlushed + bmdOutputFrameCompleted, + bmdOutputFrameDisplayedLate, + bmdOutputFrameDropped, + bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ @@ -203,9 +212,9 @@ enum _BMDAudioSampleType { typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { - bmdAudioOutputStreamContinuous, - bmdAudioOutputStreamContinuousDontResample, - bmdAudioOutputStreamTimestamped + bmdAudioOutputStreamContinuous, + bmdAudioOutputStreamContinuousDontResample, + bmdAudioOutputStreamTimestamped }; /* Enum BMDDisplayModeSupport - Output mode supported flags */ @@ -213,8 +222,17 @@ enum _BMDAudioOutputStreamType { typedef uint32_t BMDDisplayModeSupport; enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, - bmdDisplayModeSupported, - bmdDisplayModeSupportedWithConversion + bmdDisplayModeSupported, + bmdDisplayModeSupportedWithConversion +}; + +/* Enum BMDAncillaryPacketFormat - Ancillary packet format */ + +typedef uint32_t BMDAncillaryPacketFormat; +enum _BMDAncillaryPacketFormat { + bmdAncillaryPacketFormatUInt8 = 'ui08', + bmdAncillaryPacketFormatUInt16 = 'ui16', + bmdAncillaryPacketFormatYCbCr10 = 'v210' }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ @@ -341,6 +359,37 @@ enum _BMDDeviceInterface { typedef uint32_t BMDDeckLinkFrameMetadataID; enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = 'eotf', // EOTF in range 0-7 as per CEA 861.3 + bmdDeckLinkFrameMetadataCintelFilmType = 'cfty', // Current film type + bmdDeckLinkFrameMetadataCintelFilmGauge = 'cfga', // Current film gauge + bmdDeckLinkFrameMetadataCintelOffsetDetectedHorizontal = 'odfh', // Horizontal offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelOffsetDetectedVertical = 'odfv', // Vertical offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelKeykodeLow = 'ckkl', // Raw keykode value - low 64 bits + bmdDeckLinkFrameMetadataCintelKeykodeHigh = 'ckkh', // Raw keykode value - high 64 bits + bmdDeckLinkFrameMetadataCintelTile1Size = 'ct1s', // Size in bytes of compressed raw tile 1 + bmdDeckLinkFrameMetadataCintelTile2Size = 'ct2s', // Size in bytes of compressed raw tile 2 + bmdDeckLinkFrameMetadataCintelTile3Size = 'ct3s', // Size in bytes of compressed raw tile 3 + bmdDeckLinkFrameMetadataCintelTile4Size = 'ct4s', // Size in bytes of compressed raw tile 4 + bmdDeckLinkFrameMetadataCintelImageWidth = 'IWPx', // Width in pixels of image + bmdDeckLinkFrameMetadataCintelImageHeight = 'IHPx', // Height in pixels of image + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed = 'mrir', // Red in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed = 'mgir', // Green in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed = 'mbir', // Blue in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen = 'mrig', // Red in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen = 'mgig', // Green in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen = 'mbig', // Blue in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue = 'mrib', // Red in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue = 'mgib', // Green in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue = 'mbib', // Blue in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed = 'mlrr', // Red in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed = 'mlgr', // Green in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed = 'mlbr', // Blue in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen = 'mlrg', // Red in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen = 'mlgg', // Green in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen = 'mlbg', // Blue in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue = 'mlrb', // Red in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue = 'mlgb', // Green in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue = 'mlbb', // Blue in blue log masking parameter + bmdDeckLinkFrameMetadataCintelFilmFrameRate = 'cffr', // Film frame rate bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = 'hdrx', // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = 'hdry', // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = 'hdgx', // Green display primaries in range 0.0 - 1.0 @@ -352,7 +401,15 @@ enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = 'hdml', // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = 'hmil', // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = 'mcll', // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 - bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = 'fall' // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = 'fall', // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal = 'otah', // Horizontal offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical = 'otav', // Vertical offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelGainRed = 'LfRd', // Red gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainGreen = 'LfGr', // Green gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainBlue = 'LfBl', // Blue gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelLiftRed = 'GnRd', // Red lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftGreen = 'GnGr', // Green lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftBlue = 'GnBl' // Blue lift parameter to apply after log and gain }; /* Enum BMDDuplexMode - Duplex for configurable ports */ @@ -394,18 +451,19 @@ enum _BMDDeckLinkAttributeID { /* Integers */ BMDDeckLinkMaximumAudioChannels = 'mach', - BMDDeckLinkMaximumAnalogAudioChannels = 'aach', + BMDDeckLinkMaximumAnalogAudioInputChannels = 'iach', + BMDDeckLinkMaximumAnalogAudioOutputChannels = 'aach', BMDDeckLinkNumberOfSubDevices = 'nsbd', BMDDeckLinkSubDeviceIndex = 'subi', BMDDeckLinkPersistentID = 'peid', BMDDeckLinkDeviceGroupID = 'dgid', BMDDeckLinkTopologicalID = 'toid', - BMDDeckLinkVideoOutputConnections = 'vocn', - BMDDeckLinkVideoInputConnections = 'vicn', - BMDDeckLinkAudioOutputConnections = 'aocn', - BMDDeckLinkAudioInputConnections = 'aicn', + BMDDeckLinkVideoOutputConnections = 'vocn', // Returns a BMDVideoConnection bit field + BMDDeckLinkVideoInputConnections = 'vicn', // Returns a BMDVideoConnection bit field + BMDDeckLinkAudioOutputConnections = 'aocn', // Returns a BMDAudioConnection bit field + BMDDeckLinkAudioInputConnections = 'aicn', // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = 'vios', // Returns a BMDVideoIOSupport bit field - BMDDeckLinkDeckControlConnections = 'dccn', + BMDDeckLinkDeckControlConnections = 'dccn', // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = 'dbus', // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = 'airc', BMDDeckLinkAudioInputXLRChannelCount = 'aixc', @@ -459,11 +517,14 @@ enum _BMDDeckLinkStatusID { bmdDeckLinkStatusReferenceSignalFlags = 'reff', bmdDeckLinkStatusDuplexMode = 'dupx', bmdDeckLinkStatusBusy = 'busy', + bmdDeckLinkStatusInterchangeablePanelType = 'icpt', + bmdDeckLinkStatusDeviceTemperature = 'dtmp', /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = 'visl', - bmdDeckLinkStatusReferenceSignalLocked = 'refl' + bmdDeckLinkStatusReferenceSignalLocked = 'refl', + bmdDeckLinkStatusReceivedEDID = 'edid' }; /* Enum BMDDeckLinkVideoStatusFlags - */ @@ -484,6 +545,14 @@ enum _BMDDuplexStatus { bmdDuplexStatusInactive = 'inac' }; +/* Enum BMDPanelType - The type of interchangeable panel */ + +typedef uint32_t BMDPanelType; +enum _BMDPanelType { + bmdPanelNotDetected = 'npnl', + bmdPanelTeranexMiniSmartPanel = 'tmsm' +}; + /* Enum BMDDeviceBusyState - Current device busy state */ typedef uint32_t BMDDeviceBusyState; @@ -539,6 +608,9 @@ class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoFrameMetadataExtensions; class IDeckLinkVideoInputFrame; +class IDeckLinkAncillaryPacket; +class IDeckLinkAncillaryPacketIterator; +class IDeckLinkVideoFrameAncillaryPackets; class IDeckLinkVideoFrameAncillary; class IDeckLinkEncoderPacket; class IDeckLinkEncoderVideoPacket; @@ -559,7 +631,7 @@ class IDeckLinkDiscovery; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ -class IDeckLinkVideoOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; @@ -571,7 +643,7 @@ class IDeckLinkVideoOutputCallback : public IUnknown /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -583,7 +655,7 @@ class IDeckLinkInputCallback : public IUnknown /* Interface IDeckLinkEncoderInputCallback - Frame arrival callback. */ -class IDeckLinkEncoderInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInputCallback : public IUnknown { public: virtual HRESULT VideoInputSignalChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -596,7 +668,7 @@ class IDeckLinkEncoderInputCallback : public IUnknown /* Interface IDeckLinkMemoryAllocator - Memory allocator for video frames. */ -class IDeckLinkMemoryAllocator : public IUnknown +class BMD_PUBLIC IDeckLinkMemoryAllocator : public IUnknown { public: virtual HRESULT AllocateBuffer (/* in */ uint32_t bufferSize, /* out */ void **allocatedBuffer) = 0; @@ -608,7 +680,7 @@ class IDeckLinkMemoryAllocator : public IUnknown /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ -class IDeckLinkAudioOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; @@ -616,7 +688,7 @@ class IDeckLinkAudioOutputCallback : public IUnknown /* Interface IDeckLinkIterator - enumerates installed DeckLink hardware */ -class IDeckLinkIterator : public IUnknown +class BMD_PUBLIC IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink **deckLinkInstance) = 0; @@ -624,7 +696,7 @@ class IDeckLinkIterator : public IUnknown /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ -class IDeckLinkAPIInformation : public IUnknown +class BMD_PUBLIC IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool *value) = 0; @@ -638,7 +710,7 @@ class IDeckLinkAPIInformation : public IUnknown /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput : public IUnknown +class BMD_PUBLIC IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -653,7 +725,7 @@ class IDeckLinkOutput : public IUnknown virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; - virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; + virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -695,7 +767,7 @@ class IDeckLinkOutput : public IUnknown /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput : public IUnknown +class BMD_PUBLIC IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -734,7 +806,7 @@ class IDeckLinkInput : public IUnknown /* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkEncoderInput : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -771,7 +843,7 @@ class IDeckLinkEncoderInput : public IUnknown /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ -class IDeckLinkVideoFrame : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; @@ -782,7 +854,7 @@ class IDeckLinkVideoFrame : public IUnknown virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) = 0; - virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; + virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred protected: virtual ~IDeckLinkVideoFrame () {} // call Release method to drop reference count @@ -790,7 +862,7 @@ class IDeckLinkVideoFrame : public IUnknown /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ -class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; @@ -806,7 +878,7 @@ class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface implemented on IDeckLinkVideoFrame to support 3D frames */ -class IDeckLinkVideoFrame3DExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; @@ -818,7 +890,7 @@ class IDeckLinkVideoFrame3DExtensions : public IUnknown /* Interface IDeckLinkVideoFrameMetadataExtensions - Optional interface implemented on IDeckLinkVideoFrame to support frame metadata such as HDMI HDR information */ -class IDeckLinkVideoFrameMetadataExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrameMetadataExtensions : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ int64_t *value) = 0; @@ -832,7 +904,7 @@ class IDeckLinkVideoFrameMetadataExtensions : public IUnknown /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -842,13 +914,56 @@ class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame virtual ~IDeckLinkVideoInputFrame () {} // call Release method to drop reference count }; -/* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ +/* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ + +class BMD_PUBLIC IDeckLinkAncillaryPacket : public IUnknown +{ +public: + + virtual HRESULT GetBytes (/* in */ BMDAncillaryPacketFormat format /* For output, only one format need be offered */, /* out */ const void **data /* Optional */, /* out */ uint32_t *size /* Optional */) = 0; + virtual uint8_t GetDID (void) = 0; + virtual uint8_t GetSDID (void) = 0; + virtual uint32_t GetLineNumber (void) = 0; // On output, zero is auto + virtual uint8_t GetDataStreamIndex (void) = 0; // Usually zero. Can only be 1 if non-SD and the first data stream is completely full + +protected: + virtual ~IDeckLinkAncillaryPacket () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ + +class BMD_PUBLIC IDeckLinkAncillaryPacketIterator : public IUnknown +{ +public: + virtual HRESULT Next (/* out */ IDeckLinkAncillaryPacket **packet) = 0; + +protected: + virtual ~IDeckLinkAncillaryPacketIterator () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +class BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets : public IUnknown +{ +public: + + virtual HRESULT GetPacketIterator (/* out */ IDeckLinkAncillaryPacketIterator **iterator) = 0; + virtual HRESULT GetFirstPacketByID (/* in */ uint8_t DID, /* in */ uint8_t SDID, /* out */ IDeckLinkAncillaryPacket **packet) = 0; + virtual HRESULT AttachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; // Implement IDeckLinkAncillaryPacket to output your own + virtual HRESULT DetachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; + virtual HRESULT DetachAllPackets (void) = 0; + +protected: + virtual ~IDeckLinkVideoFrameAncillaryPackets () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ -class IDeckLinkVideoFrameAncillary : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrameAncillary : public IUnknown { public: - virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; + virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; @@ -858,7 +973,7 @@ class IDeckLinkVideoFrameAncillary : public IUnknown /* Interface IDeckLinkEncoderPacket - Interface to encapsulate an encoded packet. */ -class IDeckLinkEncoderPacket : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderPacket : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void **buffer) = 0; @@ -872,7 +987,7 @@ class IDeckLinkEncoderPacket : public IUnknown /* Interface IDeckLinkEncoderVideoPacket - Provided by the IDeckLinkEncoderInput video packet arrival callback. */ -class IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket { public: virtual BMDPixelFormat GetPixelFormat (void) = 0; @@ -886,7 +1001,7 @@ class IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket /* Interface IDeckLinkEncoderAudioPacket - Provided by the IDeckLinkEncoderInput audio packet arrival callback. */ -class IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket { public: virtual BMDAudioFormat GetAudioFormat (void) = 0; @@ -897,7 +1012,7 @@ class IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket /* Interface IDeckLinkH265NALPacket - Obtained through QueryInterface() on an IDeckLinkEncoderVideoPacket object */ -class IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket +class BMD_PUBLIC IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket { public: virtual HRESULT GetUnitType (/* out */ uint8_t *unitType) = 0; @@ -910,7 +1025,7 @@ class IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ -class IDeckLinkAudioInputPacket : public IUnknown +class BMD_PUBLIC IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; @@ -923,7 +1038,7 @@ class IDeckLinkAudioInputPacket : public IUnknown /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ -class IDeckLinkScreenPreviewCallback : public IUnknown +class BMD_PUBLIC IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; @@ -934,7 +1049,7 @@ class IDeckLinkScreenPreviewCallback : public IUnknown /* Interface IDeckLinkCocoaScreenPreviewCallback - Screen preview callback for Cocoa-based applications */ -class IDeckLinkCocoaScreenPreviewCallback : public IDeckLinkScreenPreviewCallback +class BMD_PUBLIC IDeckLinkCocoaScreenPreviewCallback : public IDeckLinkScreenPreviewCallback { public: @@ -944,7 +1059,7 @@ class IDeckLinkCocoaScreenPreviewCallback : public IDeckLinkScreenPreviewCallbac /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ -class IDeckLinkGLScreenPreviewHelper : public IUnknown +class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper : public IUnknown { public: @@ -961,7 +1076,7 @@ class IDeckLinkGLScreenPreviewHelper : public IUnknown /* Interface IDeckLinkNotificationCallback - DeckLink Notification Callback Interface */ -class IDeckLinkNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkNotificationCallback : public IUnknown { public: virtual HRESULT Notify (/* in */ BMDNotifications topic, /* in */ uint64_t param1, /* in */ uint64_t param2) = 0; @@ -969,7 +1084,7 @@ class IDeckLinkNotificationCallback : public IUnknown /* Interface IDeckLinkNotification - DeckLink Notification interface */ -class IDeckLinkNotification : public IUnknown +class BMD_PUBLIC IDeckLinkNotification : public IUnknown { public: virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0; @@ -978,7 +1093,7 @@ class IDeckLinkNotification : public IUnknown /* Interface IDeckLinkAttributes - DeckLink Attribute interface */ -class IDeckLinkAttributes : public IUnknown +class BMD_PUBLIC IDeckLinkAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool *value) = 0; @@ -992,7 +1107,7 @@ class IDeckLinkAttributes : public IUnknown /* Interface IDeckLinkStatus - DeckLink Status interface */ -class IDeckLinkStatus : public IUnknown +class BMD_PUBLIC IDeckLinkStatus : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkStatusID statusID, /* out */ bool *value) = 0; @@ -1007,7 +1122,7 @@ class IDeckLinkStatus : public IUnknown /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ -class IDeckLinkKeyer : public IUnknown +class BMD_PUBLIC IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; @@ -1022,7 +1137,7 @@ class IDeckLinkKeyer : public IUnknown /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ -class IDeckLinkVideoConversion : public IUnknown +class BMD_PUBLIC IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; @@ -1033,7 +1148,7 @@ class IDeckLinkVideoConversion : public IUnknown /* Interface IDeckLinkDeviceNotificationCallback - DeckLink device arrival/removal notification callbacks */ -class IDeckLinkDeviceNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeviceNotificationCallback : public IUnknown { public: virtual HRESULT DeckLinkDeviceArrived (/* in */ IDeckLink* deckLinkDevice) = 0; @@ -1045,7 +1160,7 @@ class IDeckLinkDeviceNotificationCallback : public IUnknown /* Interface IDeckLinkDiscovery - DeckLink device discovery */ -class IDeckLinkDiscovery : public IUnknown +class BMD_PUBLIC IDeckLinkDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IDeckLinkDeviceNotificationCallback* deviceNotificationCallback) = 0; @@ -1059,12 +1174,13 @@ class IDeckLinkDiscovery : public IUnknown extern "C" { - IDeckLinkIterator* CreateDeckLinkIteratorInstance (void); - IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void); - IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void); - IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void); - IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* /* (NSView*) */ parentView); - IDeckLinkVideoConversion* CreateVideoConversionInstance (void); + IDeckLinkIterator* BMD_PUBLIC CreateDeckLinkIteratorInstance (void); + IDeckLinkDiscovery* BMD_PUBLIC CreateDeckLinkDiscoveryInstance (void); + IDeckLinkAPIInformation* BMD_PUBLIC CreateDeckLinkAPIInformationInstance (void); + IDeckLinkGLScreenPreviewHelper* BMD_PUBLIC CreateOpenGLScreenPreviewHelper (void); + IDeckLinkCocoaScreenPreviewCallback* BMD_PUBLIC CreateCocoaScreenPreview (void* /* (NSView*) */ parentView); + IDeckLinkVideoConversion* BMD_PUBLIC CreateVideoConversionInstance (void); + IDeckLinkVideoFrameAncillaryPackets* BMD_PUBLIC CreateVideoFrameAncillaryPacketsInstance (void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h index 31b652ba6..d62ce4fa7 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,12 +37,16 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations // Interface ID Declarations -BMD_CONST REFIID IID_IDeckLinkConfiguration = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; +BMD_CONST REFIID IID_IDeckLinkConfiguration = /* EF90380B-4AE5-4346-9077-E288E149F129 */ {0xEF,0x90,0x38,0x0B,0x4A,0xE5,0x43,0x46,0x90,0x77,0xE2,0x88,0xE1,0x49,0xF1,0x29}; BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration = /* 138050E5-C60A-4552-BF3F-0F358049327E */ {0x13,0x80,0x50,0xE5,0xC6,0x0A,0x45,0x52,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E}; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ @@ -54,10 +58,6 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigSwapSerialRxTx = 'ssrt', - /* Video Input/Output Flags */ - - bmdDeckLinkConfigUse1080pNotPsF = 'fpro', - /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = '3dpf', @@ -78,6 +78,12 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigLowLatencyVideoOutput = 'llvo', bmdDeckLinkConfigDownConversionOnAllAnalogOutput = 'caao', bmdDeckLinkConfigSMPTELevelAOutput = 'smta', + bmdDeckLinkConfigRec2020Output = 'rec2', // Ensure output is Rec.2020 colorspace + bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = 'SDQS', + + /* Video Output Flags */ + + bmdDeckLinkConfigOutput1080pAsPsF = 'pfpr', /* Video Output Integers */ @@ -106,6 +112,10 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigUseDedicatedLTCInput = 'dltc', // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = '3dds', + /* Video Input Flags */ + + bmdDeckLinkConfigCapture1080pAsPsF = 'cfpr', + /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = 'vicn', @@ -206,7 +216,7 @@ class IDeckLinkEncoderConfiguration; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ -class IDeckLinkConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; @@ -225,7 +235,7 @@ class IDeckLinkConfiguration : public IUnknown /* Interface IDeckLinkEncoderConfiguration - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ -class IDeckLinkEncoderConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h index ca7f81520..2c989a069 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h index 29695cfe7..0808367d5 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h new file mode 100644 index 000000000..e49a3ee3b --- /dev/null +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h @@ -0,0 +1,62 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPICONFIGURATION_v10_9_H +#define BMD_DECKLINKAPICONFIGURATION_v10_9_H + +#include "DeckLinkAPIConfiguration.h" + +// Interface ID Declarations + +BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_9 = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; + +// +// Forward Declarations + +class IDeckLinkConfiguration_v10_9; + +/* Interface IDeckLinkConfiguration_v10_9 - DeckLink Configuration interface */ + +class IDeckLinkConfiguration_v10_9 : public IUnknown +{ +public: + virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; + virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0; + virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; + virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0; + virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; + virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0; + virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ CFStringRef value) = 0; + virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ CFStringRef *value) = 0; + virtual HRESULT WriteConfigurationToPreferences (void) = 0; + +protected: + virtual ~IDeckLinkConfiguration_v10_9 () {} // call Release method to drop reference count +}; + + +#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_9_H) */ diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h index 6237ab758..6de333fda 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -149,7 +153,7 @@ class IDeckLinkDeckControl; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ -class IDeckLinkDeckControlStatusCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; @@ -163,7 +167,7 @@ class IDeckLinkDeckControlStatusCallback : public IUnknown /* Interface IDeckLinkDeckControl - Deck Control main interface */ -class IDeckLinkDeckControl : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h index 3715a0384..d63960d05 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -50,7 +54,7 @@ class IDeckLink; /* Interface IDeckLink - represents a DeckLink device */ -class IDeckLink : public IUnknown +class BMD_PUBLIC IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ CFStringRef *modelName) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp index 92e41b2a0..50be5c94a 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -42,6 +42,7 @@ typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(v typedef IDeckLinkCocoaScreenPreviewCallback* (*CreateCocoaScreenPreviewFunc)(void*); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); +typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static CFBundleRef gDeckLinkAPIBundleRef = NULL; @@ -51,6 +52,7 @@ static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateCocoaScreenPreviewFunc gCreateCocoaPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc= NULL; +static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL; void InitDeckLinkAPI (void) @@ -63,12 +65,13 @@ void InitDeckLinkAPI (void) gDeckLinkAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); if (gDeckLinkAPIBundleRef != NULL) { - gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0002")); + gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0003")); gCreateAPIInformationFunc = (CreateAPIInformationFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkAPIInformationInstance_0001")); gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateOpenGLScreenPreviewHelper_0001")); gCreateCocoaPreviewFunc = (CreateCocoaScreenPreviewFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateCocoaScreenPreview_0001")); gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoConversionInstance_0001")); - gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkDiscoveryInstance_0001")); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkDiscoveryInstance_0002")); + gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoFrameAncillaryPacketsInstance_0001")); } CFRelease(bundleURL); } @@ -79,70 +82,80 @@ bool IsDeckLinkAPIPresent (void) // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller if (gDeckLinkAPIBundleRef != NULL) return true; - + return false; } IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; - + return gCreateIteratorFunc(); } IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; - + return gCreateAPIInformationFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; - + return gCreateOpenGLPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateCocoaPreviewFunc == NULL) return NULL; - + return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; - + return gCreateVideoConversionFunc(); } IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateDeckLinkDiscoveryFunc == NULL) return NULL; - + return gCreateDeckLinkDiscoveryFunc(); } +IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance (void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoFrameAncillaryPacketsFunc == NULL) + return NULL; + + return gCreateVideoFrameAncillaryPacketsFunc(); +} + #define kBMDStreamingAPI_BundlePath "/Library/Application Support/Blackmagic Design/Streaming/BMDStreamingAPI.bundle" @@ -164,7 +177,7 @@ void InitBMDStreamingAPI(void) gBMDStreamingAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); if (gBMDStreamingAPIBundleRef != NULL) { - gCreateDiscoveryFunc = (CreateDiscoveryFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingDiscoveryInstance_0001")); + gCreateDiscoveryFunc = (CreateDiscoveryFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingDiscoveryInstance_0002")); gCreateNALParserFunc = (CreateNALParserFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingH264NALParser_0001")); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp new file mode 100644 index 000000000..6449a08e0 --- /dev/null +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp @@ -0,0 +1,193 @@ +/* -LICENSE-START- +** Copyright (c) 2009 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ +/* DeckLinkAPIDispatch.cpp */ + +#include "DeckLinkAPI.h" +#include + +#if BLACKMAGIC_DECKLINK_API_MAGIC != 1 +#error The DeckLink API version of DeckLinkAPIDispatch.cpp is not the same version as DeckLinkAPI.h +#endif + +#define kDeckLinkAPI_BundlePath "/Library/Frameworks/DeckLinkAPI.framework" + + +typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); +typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); +typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); +typedef IDeckLinkCocoaScreenPreviewCallback* (*CreateCocoaScreenPreviewFunc)(void*); +typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); +typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); + +static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; +static CFBundleRef gDeckLinkAPIBundleRef = NULL; +static CreateIteratorFunc gCreateIteratorFunc = NULL; +static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; +static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; +static CreateCocoaScreenPreviewFunc gCreateCocoaPreviewFunc = NULL; +static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; +static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL; + + +void InitDeckLinkAPI(void) +{ + CFURLRef bundleURL; + + bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kDeckLinkAPI_BundlePath), kCFURLPOSIXPathStyle, true); + if (bundleURL != NULL) + { + gDeckLinkAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); + if (gDeckLinkAPIBundleRef != NULL) + { + gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0002")); + gCreateAPIInformationFunc = (CreateAPIInformationFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkAPIInformationInstance_0001")); + gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateOpenGLScreenPreviewHelper_0001")); + gCreateCocoaPreviewFunc = (CreateCocoaScreenPreviewFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateCocoaScreenPreview_0001")); + gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoConversionInstance_0001")); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkDiscoveryInstance_0001")); + } + CFRelease(bundleURL); + } +} + +bool IsDeckLinkAPIPresent(void) +{ + // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller + if (gDeckLinkAPIBundleRef != NULL) + return true; + + return false; +} + +IDeckLinkIterator* CreateDeckLinkIteratorInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateIteratorFunc == NULL) + return NULL; + + return gCreateIteratorFunc(); +} + +IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateAPIInformationFunc == NULL) + return NULL; + + return gCreateAPIInformationFunc(); +} + +IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateOpenGLPreviewFunc == NULL) + return NULL; + + return gCreateOpenGLPreviewFunc(); +} + +IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview(void* parentView) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateCocoaPreviewFunc == NULL) + return NULL; + + return gCreateCocoaPreviewFunc(parentView); +} + +IDeckLinkVideoConversion* CreateVideoConversionInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoConversionFunc == NULL) + return NULL; + + return gCreateVideoConversionFunc(); +} + +IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateDeckLinkDiscoveryFunc == NULL) + return NULL; + + return gCreateDeckLinkDiscoveryFunc(); +} + + +#define kBMDStreamingAPI_BundlePath "/Library/Application Support/Blackmagic Design/Streaming/BMDStreamingAPI.bundle" + +typedef IBMDStreamingDiscovery* (*CreateDiscoveryFunc)(void); +typedef IBMDStreamingH264NALParser* (*CreateNALParserFunc)(void); + +static pthread_once_t gBMDStreamingOnceControl = PTHREAD_ONCE_INIT; +static CFBundleRef gBMDStreamingAPIBundleRef = NULL; +static CreateDiscoveryFunc gCreateDiscoveryFunc = NULL; +static CreateNALParserFunc gCreateNALParserFunc = NULL; + +void InitBMDStreamingAPI(void) +{ + CFURLRef bundleURL; + + bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kBMDStreamingAPI_BundlePath), kCFURLPOSIXPathStyle, true); + if (bundleURL != NULL) + { + gBMDStreamingAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); + if (gBMDStreamingAPIBundleRef != NULL) + { + gCreateDiscoveryFunc = (CreateDiscoveryFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingDiscoveryInstance_0001")); + gCreateNALParserFunc = (CreateNALParserFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingH264NALParser_0001")); + } + + CFRelease(bundleURL); + } +} + +IBMDStreamingDiscovery* CreateBMDStreamingDiscoveryInstance() +{ + pthread_once(&gBMDStreamingOnceControl, InitBMDStreamingAPI); + + if (gCreateDiscoveryFunc == NULL) + return NULL; + + return gCreateDiscoveryFunc(); +} + +IBMDStreamingH264NALParser* CreateBMDStreamingH264NALParser() +{ + pthread_once(&gBMDStreamingOnceControl, InitBMDStreamingAPI); + + if (gCreateNALParserFunc == NULL) + return NULL; + + return gCreateNALParserFunc(); +} diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp index 8b0c0cc88..4b11bca41 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -66,40 +66,40 @@ void InitDeckLinkAPI_v7_6 (void) IDeckLinkIterator* CreateDeckLinkIteratorInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateIteratorFunc == NULL) return NULL; - + return gCreateIteratorFunc(); } IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; - + return gCreateOpenGLPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback_v7_6* CreateCocoaScreenPreview_v7_6 (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateCocoaPreviewFunc == NULL) return NULL; - + return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion_v7_6* CreateVideoConversionInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateVideoConversionFunc == NULL) return NULL; - + return gCreateVideoConversionFunc(); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp index 898d739a8..cd6857aa9 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -75,57 +75,57 @@ bool IsDeckLinkAPIPresent (void) // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller if (gDeckLinkAPIBundleRef != NULL) return true; - + return false; } IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; - + return gCreateIteratorFunc(); } IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; - + return gCreateAPIInformationFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; - + return gCreateOpenGLPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateCocoaPreviewFunc == NULL) return NULL; - + return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; - + return gCreateVideoConversionFunc(); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h index 3bc342949..b61118281 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -65,12 +69,12 @@ enum _BMDDisplayMode { bmdModeHD1080p25 = 'Hp25', bmdModeHD1080p2997 = 'Hp29', bmdModeHD1080p30 = 'Hp30', - bmdModeHD1080i50 = 'Hi50', - bmdModeHD1080i5994 = 'Hi59', - bmdModeHD1080i6000 = 'Hi60', // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = 'Hp50', bmdModeHD1080p5994 = 'Hp59', bmdModeHD1080p6000 = 'Hp60', // N.B. This _really_ is 60.00 Hz. + bmdModeHD1080i50 = 'Hi50', + bmdModeHD1080i5994 = 'Hi59', + bmdModeHD1080i6000 = 'Hi60', // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ @@ -78,19 +82,24 @@ enum _BMDDisplayMode { bmdModeHD720p5994 = 'hp59', bmdModeHD720p60 = 'hp60', - /* 2k Modes */ + /* 2K Modes */ bmdMode2k2398 = '2k23', bmdMode2k24 = '2k24', bmdMode2k25 = '2k25', - /* DCI Modes (output only) */ + /* 2K DCI Modes */ bmdMode2kDCI2398 = '2d23', bmdMode2kDCI24 = '2d24', bmdMode2kDCI25 = '2d25', + bmdMode2kDCI2997 = '2d29', + bmdMode2kDCI30 = '2d30', + bmdMode2kDCI50 = '2d50', + bmdMode2kDCI5994 = '2d59', + bmdMode2kDCI60 = '2d60', - /* 4k Modes */ + /* 4K UHD Modes */ bmdMode4K2160p2398 = '4k23', bmdMode4K2160p24 = '4k24', @@ -101,11 +110,43 @@ enum _BMDDisplayMode { bmdMode4K2160p5994 = '4k59', bmdMode4K2160p60 = '4k60', - /* DCI Modes (output only) */ + /* 4K DCI Modes */ bmdMode4kDCI2398 = '4d23', bmdMode4kDCI24 = '4d24', bmdMode4kDCI25 = '4d25', + bmdMode4kDCI2997 = '4d29', + bmdMode4kDCI30 = '4d30', + bmdMode4kDCI50 = '4d50', + bmdMode4kDCI5994 = '4d59', + bmdMode4kDCI60 = '4d60', + + /* 8K UHD Modes */ + + bmdMode8K4320p2398 = '8k23', + bmdMode8K4320p24 = '8k24', + bmdMode8K4320p25 = '8k25', + bmdMode8K4320p2997 = '8k29', + bmdMode8K4320p30 = '8k30', + bmdMode8K4320p50 = '8k50', + bmdMode8K4320p5994 = '8k59', + bmdMode8K4320p60 = '8k60', + + /* 8K DCI Modes */ + + bmdMode8kDCI2398 = '8d23', + bmdMode8kDCI24 = '8d24', + bmdMode8kDCI25 = '8d25', + bmdMode8kDCI2997 = '8d29', + bmdMode8kDCI30 = '8d30', + bmdMode8kDCI50 = '8d50', + bmdMode8kDCI5994 = '8d59', + bmdMode8kDCI60 = '8d60', + + /* RAW Modes for Cintel (input only) */ + + bmdModeCintelRAW = 'rwci', // Frame size up to 4096x3072, variable frame rate + bmdModeCintelCompressedRAW = 'rwcc', // Frame size up to 4096x3072, variable frame rate /* Special Modes */ @@ -140,7 +181,12 @@ enum _BMDPixelFormat { /* AVID DNxHR */ - bmdFormatDNxHR = 'AVdh' + bmdFormatDNxHR = 'AVdh', + + /* Cintel formats */ + + bmdFormat12BitRAWGRBG = 'r12p', // 12-bit RAW data for bayer pattern GRBG + bmdFormat12BitRAWJPEG = 'r16p' // 12-bit RAW data arranged in tiles and JPEG compressed }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ @@ -159,7 +205,7 @@ class IDeckLinkDisplayMode; /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ -class IDeckLinkDisplayModeIterator : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; @@ -170,7 +216,7 @@ class IDeckLinkDisplayModeIterator : public IUnknown /* Interface IDeckLinkDisplayMode - represents a display mode */ -class IDeckLinkDisplayMode : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ CFStringRef *name) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h index 6bb5168ff..b5f16bb37 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -92,8 +96,8 @@ enum _BMDStreamingEncodingFrameRate { typedef uint32_t BMDStreamingEncodingSupport; enum _BMDStreamingEncodingSupport { bmdStreamingEncodingModeNotSupported = 0, - bmdStreamingEncodingModeSupported, - bmdStreamingEncodingModeSupportedWithChanges + bmdStreamingEncodingModeSupported, + bmdStreamingEncodingModeSupportedWithChanges }; /* Enum BMDStreamingVideoCodec - Video codecs */ @@ -188,7 +192,7 @@ class IBMDStreamingH264NALParser; /* Interface IBMDStreamingDeviceNotificationCallback - Device notification callbacks. */ -class IBMDStreamingDeviceNotificationCallback : public IUnknown +class BMD_PUBLIC IBMDStreamingDeviceNotificationCallback : public IUnknown { public: virtual HRESULT StreamingDeviceArrived (/* in */ IDeckLink* device) = 0; @@ -201,7 +205,7 @@ class IBMDStreamingDeviceNotificationCallback : public IUnknown /* Interface IBMDStreamingH264InputCallback - H264 input callbacks. */ -class IBMDStreamingH264InputCallback : public IUnknown +class BMD_PUBLIC IBMDStreamingH264InputCallback : public IUnknown { public: virtual HRESULT H264NALPacketArrived (/* in */ IBMDStreamingH264NALPacket* nalPacket) = 0; @@ -217,7 +221,7 @@ class IBMDStreamingH264InputCallback : public IUnknown /* Interface IBMDStreamingDiscovery - Installs device notifications */ -class IBMDStreamingDiscovery : public IUnknown +class BMD_PUBLIC IBMDStreamingDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IBMDStreamingDeviceNotificationCallback* theCallback) = 0; @@ -229,7 +233,7 @@ class IBMDStreamingDiscovery : public IUnknown /* Interface IBMDStreamingVideoEncodingMode - Represents an encoded video mode. */ -class IBMDStreamingVideoEncodingMode : public IUnknown +class BMD_PUBLIC IBMDStreamingVideoEncodingMode : public IUnknown { public: virtual HRESULT GetName (/* out */ CFStringRef *name) = 0; @@ -252,7 +256,7 @@ class IBMDStreamingVideoEncodingMode : public IUnknown /* Interface IBMDStreamingMutableVideoEncodingMode - Represents a mutable encoded video mode. */ -class IBMDStreamingMutableVideoEncodingMode : public IBMDStreamingVideoEncodingMode +class BMD_PUBLIC IBMDStreamingMutableVideoEncodingMode : public IBMDStreamingVideoEncodingMode { public: virtual HRESULT SetSourceRect (/* in */ uint32_t posX, /* in */ uint32_t posY, /* in */ uint32_t width, /* in */ uint32_t height) = 0; @@ -268,7 +272,7 @@ class IBMDStreamingMutableVideoEncodingMode : public IBMDStreamingVideoEncodingM /* Interface IBMDStreamingVideoEncodingModePresetIterator - Enumerates encoding mode presets */ -class IBMDStreamingVideoEncodingModePresetIterator : public IUnknown +class BMD_PUBLIC IBMDStreamingVideoEncodingModePresetIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IBMDStreamingVideoEncodingMode** videoEncodingMode) = 0; @@ -279,7 +283,7 @@ class IBMDStreamingVideoEncodingModePresetIterator : public IUnknown /* Interface IBMDStreamingDeviceInput - Created by QueryInterface from IDeckLink */ -class IBMDStreamingDeviceInput : public IUnknown +class BMD_PUBLIC IBMDStreamingDeviceInput : public IUnknown { public: @@ -309,7 +313,7 @@ class IBMDStreamingDeviceInput : public IUnknown /* Interface IBMDStreamingH264NALPacket - Represent an H.264 NAL packet */ -class IBMDStreamingH264NALPacket : public IUnknown +class BMD_PUBLIC IBMDStreamingH264NALPacket : public IUnknown { public: virtual long GetPayloadSize (void) = 0; @@ -324,7 +328,7 @@ class IBMDStreamingH264NALPacket : public IUnknown /* Interface IBMDStreamingAudioPacket - Represents a chunk of audio data */ -class IBMDStreamingAudioPacket : public IUnknown +class BMD_PUBLIC IBMDStreamingAudioPacket : public IUnknown { public: virtual BMDStreamingAudioCodec GetCodec (void) = 0; @@ -339,7 +343,7 @@ class IBMDStreamingAudioPacket : public IUnknown /* Interface IBMDStreamingMPEG2TSPacket - Represent an MPEG2 Transport Stream packet */ -class IBMDStreamingMPEG2TSPacket : public IUnknown +class BMD_PUBLIC IBMDStreamingMPEG2TSPacket : public IUnknown { public: virtual long GetPayloadSize (void) = 0; @@ -351,7 +355,7 @@ class IBMDStreamingMPEG2TSPacket : public IUnknown /* Interface IBMDStreamingH264NALParser - For basic NAL parsing */ -class IBMDStreamingH264NALParser : public IUnknown +class BMD_PUBLIC IBMDStreamingH264NALParser : public IUnknown { public: virtual HRESULT IsNALSequenceParameterSet (/* in */ IBMDStreamingH264NALPacket* nal) = 0; @@ -366,8 +370,8 @@ class IBMDStreamingH264NALParser : public IUnknown extern "C" { - IBMDStreamingDiscovery* CreateBMDStreamingDiscoveryInstance (void); - IBMDStreamingH264NALParser* CreateBMDStreamingH264NALParser (void); + IBMDStreamingDiscovery* BMD_PUBLIC CreateBMDStreamingDiscoveryInstance (void); + IBMDStreamingH264NALParser* BMD_PUBLIC CreateBMDStreamingH264NALParser (void); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h index 04da49f1f..9739cf8cb 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations typedef int64_t BMDTimeValue; @@ -54,7 +58,8 @@ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, - bmdTimecodeFieldMark = 1 << 1 + bmdTimecodeFieldMark = 1 << 1, + bmdTimecodeColorFrame = 1 << 2 }; /* Enum BMDVideoConnection - Video connection types */ @@ -96,7 +101,7 @@ class IDeckLinkTimecode; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ -class IDeckLinkTimecode : public IUnknown +class BMD_PUBLIC IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h index 55b24b1b5..995318d3f 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h @@ -7,14 +7,14 @@ * ** execute, and transmit the Software, and to prepare derivative works of the * ** Software, and to permit third-parties to whom the Software is furnished to * ** do so, all subject to the following: - * ** + * ** * ** The copyright notices in the Software and this entire statement, including * ** the above license grant, this restriction and the following disclaimer, * ** must be included in all copies of the Software, in whole or in part, and * ** all derivative works of the Software, unless such copies or derivative * ** works are solely in the form of machine-executable object code generated by * ** a source language processor. - * ** + * ** * ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -30,7 +30,8 @@ #ifndef __DeckLink_API_Version_h__ #define __DeckLink_API_Version_h__ -#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a080000 -#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.8" +#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a0b0000 +#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.11" #endif // __DeckLink_API_Version_h__ + diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h index 29ff82012..e5c95e476 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,7 +37,7 @@ typedef uint32_t BMDDeckLinkConfigurationID_v10_2; enum _BMDDeckLinkConfigurationID_v10_2 { /* Video output flags */ - + bmdDeckLinkConfig3GBpsVideoOutput_v10_2 = '3gbs', }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_9.h new file mode 100644 index 000000000..aec8c7c20 --- /dev/null +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_9.h @@ -0,0 +1,45 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPI_v10_9_H +#define BMD_DECKLINKAPI_v10_9_H + +#include "DeckLinkAPI.h" + +// Type Declarations + +/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ + +typedef uint32_t BMDDeckLinkConfigurationID_v10_9; +enum _BMDDeckLinkConfigurationID_v10_9 { + + /* Flags */ + + bmdDeckLinkConfig1080pNotPsF_v10_9 = 'fpro', +}; + +#endif /* defined(BMD_DECKLINKAPI_v10_9_H) */ diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h index 50e0d11f5..68d0beb31 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -102,37 +102,37 @@ class IDeckLinkOutput_v7_1 : public IUnknown // Display mode predicates virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1* *iterator) = 0; - - + + // Video output virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput (BMDDisplayMode displayMode) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator (IDeckLinkMemoryAllocator* theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame (int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrameFromBuffer (void* buffer, int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; - + virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync (IDeckLinkVideoFrame_v7_1* theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame (IDeckLinkVideoFrame_v7_1* theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback (IDeckLinkVideoOutputCallback_v7_1* theCallback) = 0; - - + + // Audio output virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples (void* buffer, uint32_t sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetAudioCallback (IDeckLinkAudioOutputCallback* theCallback) = 0; - - + + // Output control virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback (BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback (BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale) = 0; @@ -145,17 +145,17 @@ class IDeckLinkInput_v7_1 : public IUnknown public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1 **iterator) = 0; - + // Video input virtual HRESULT STDMETHODCALLTYPE EnableVideoInput (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput () = 0; - + // Audio input virtual HRESULT STDMETHODCALLTYPE EnableAudioInput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput () = 0; virtual HRESULT STDMETHODCALLTYPE ReadAudioSamples (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesRead, BMDTimeValue *audioPacketTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; - + // Input control virtual HRESULT STDMETHODCALLTYPE StartStreams () = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams () = 0; @@ -188,7 +188,7 @@ class IDeckLinkAudioInputPacket_v7_1 : public IUnknown public: virtual long STDMETHODCALLTYPE GetSampleCount () = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes (void* *buffer) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetAudioPacketTime (BMDTimeValue *packetTime, BMDTimeScale timeScale) = 0; }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h index c8b3da80f..430a905a9 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h index 93d7c04b7..a90d497a7 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -356,49 +356,49 @@ class IDeckLinkConfiguration_v7_6 : public IUnknown public: virtual HRESULT GetConfigurationValidator (/* out */ IDeckLinkConfiguration_v7_6 **configObject) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; - + /* Video Output Configuration */ - + virtual HRESULT SetVideoOutputFormat (/* in */ BMDVideoConnection_v7_6 videoOutputConnection) = 0; virtual HRESULT IsVideoOutputActive (/* in */ BMDVideoConnection_v7_6 videoOutputConnection, /* out */ bool *active) = 0; - + virtual HRESULT SetAnalogVideoOutputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoOutputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT EnableFieldFlickerRemovalWhenPaused (/* in */ bool enable) = 0; virtual HRESULT IsEnabledFieldFlickerRemovalWhenPaused (/* out */ bool *enabled) = 0; - + virtual HRESULT Set444And3GBpsVideoOutput (/* in */ bool enable444VideoOutput, /* in */ bool enable3GbsOutput) = 0; virtual HRESULT Get444And3GBpsVideoOutput (/* out */ bool *is444VideoOutputEnabled, /* out */ bool *threeGbsOutputEnabled) = 0; - + virtual HRESULT SetVideoOutputConversionMode (/* in */ BMDVideoOutputConversionMode conversionMode) = 0; virtual HRESULT GetVideoOutputConversionMode (/* out */ BMDVideoOutputConversionMode *conversionMode) = 0; - + virtual HRESULT Set_HD1080p24_to_HD1080i5994_Conversion (/* in */ bool enable) = 0; virtual HRESULT Get_HD1080p24_to_HD1080i5994_Conversion (/* out */ bool *enabled) = 0; - + /* Video Input Configuration */ - + virtual HRESULT SetVideoInputFormat (/* in */ BMDVideoConnection_v7_6 videoInputFormat) = 0; virtual HRESULT GetVideoInputFormat (/* out */ BMDVideoConnection_v7_6 *videoInputFormat) = 0; - + virtual HRESULT SetAnalogVideoInputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoInputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT SetVideoInputConversionMode (/* in */ BMDVideoInputConversionMode conversionMode) = 0; virtual HRESULT GetVideoInputConversionMode (/* out */ BMDVideoInputConversionMode *conversionMode) = 0; - + virtual HRESULT SetBlackVideoOutputDuringCapture (/* in */ bool blackOutInCapture) = 0; virtual HRESULT GetBlackVideoOutputDuringCapture (/* out */ bool *blackOutInCapture) = 0; - + virtual HRESULT Set32PulldownSequenceInitialTimecodeFrame (/* in */ uint32_t aFrameTimecode) = 0; virtual HRESULT Get32PulldownSequenceInitialTimecodeFrame (/* out */ uint32_t *aFrameTimecode) = 0; - + virtual HRESULT SetVancSourceLineMapping (/* in */ uint32_t activeLine1VANCsource, /* in */ uint32_t activeLine2VANCsource, /* in */ uint32_t activeLine3VANCsource) = 0; virtual HRESULT GetVancSourceLineMapping (/* out */ uint32_t *activeLine1VANCsource, /* out */ uint32_t *activeLine2VANCsource, /* out */ uint32_t *activeLine3VANCsource) = 0; - + /* Audio Input Configuration */ - + virtual HRESULT SetAudioInputFormat (/* in */ BMDAudioConnection audioInputFormat) = 0; virtual HRESULT GetAudioInputFormat (/* out */ BMDAudioConnection *audioInputFormat) = 0; }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h index 327a38257..c882f6186 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h index b4e8f2106..ea061834f 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h index e1444cd09..0eea69532 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: - ** + ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. - ** + ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -59,7 +59,7 @@ class IDeckLinkDeckControlStatusCallback_v8_1 : public IUnknown virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState_v8_1 newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; - + protected: virtual ~IDeckLinkDeckControlStatusCallback_v8_1 () {}; // call Release method to drop reference count }; @@ -102,7 +102,7 @@ class IDeckLinkDeckControl_v8_1 : public IUnknown virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback_v8_1 *callback) = 0; - + protected: virtual ~IDeckLinkDeckControl_v8_1 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h index 236d1823b..ab2aa0320 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h index 3fa3a50bb..966c8ad7e 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -47,51 +47,51 @@ class IDeckLinkOutput_v9_9 : public IUnknown public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0; - + virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; - + /* Video Output */ - + virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0; virtual HRESULT DisableVideoOutput (void) = 0; - + virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; - + virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback *theCallback) = 0; virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t *bufferedFrameCount) = 0; - + /* Audio Output */ - + virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT DisableAudioOutput (void) = 0; - + virtual HRESULT WriteAudioSamplesSync (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT BeginAudioPreroll (void) = 0; virtual HRESULT EndAudioPreroll (void) = 0; virtual HRESULT ScheduleAudioSamples (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t *bufferedSampleFrameCount) = 0; virtual HRESULT FlushBufferedAudioSamples (void) = 0; - + virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback *theCallback) = 0; - + /* Output Control */ - + virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0; virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue *actualStopTime, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool *active) = 0; virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *streamTime, /* out */ double *playbackSpeed) = 0; virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus *referenceStatus) = 0; - + /* Hardware Timing */ - + virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0; - + protected: virtual ~IDeckLinkOutput_v9_9 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/mac/platform.cpp b/plugins/decklink/mac/platform.cpp index 02e8a59ba..21ca4ec98 100644 --- a/plugins/decklink/mac/platform.cpp +++ b/plugins/decklink/mac/platform.cpp @@ -1,22 +1,17 @@ #include "../platform.hpp" +#include bool DeckLinkStringToStdString(decklink_string_t input, std::string& output) { const CFStringRef string = static_cast(input); - const CFIndex length = CFStringGetLength(string); - const CFIndex maxLength = CFStringGetMaximumSizeForEncoding(length, - kCFStringEncodingASCII) + 1; - char * const buffer = new char[maxLength]; + char *buffer = cfstr_copy_cstr(string, kCFStringEncodingASCII); - const bool result = CFStringGetCString(string, buffer, maxLength, - kCFStringEncodingASCII); - - if (result) + if (buffer) output = std::string(buffer); - delete[] buffer; + bfree(buffer); CFRelease(string); - return result; + return (buffer != NULL); } diff --git a/plugins/decklink/plugin-main.cpp b/plugins/decklink/plugin-main.cpp index 78c9b252d..023de7b1e 100644 --- a/plugins/decklink/plugin-main.cpp +++ b/plugins/decklink/plugin-main.cpp @@ -1,287 +1,18 @@ -#include "decklink.hpp" -#include "decklink-device.hpp" -#include "decklink-device-discovery.hpp" - #include +#include "decklink-devices.hpp" OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US") - -#define DEVICE_HASH "device_hash" -#define DEVICE_NAME "device_name" -#define MODE_ID "mode_id" -#define MODE_NAME "mode_name" -#define CHANNEL_FORMAT "channel_format" -#define PIXEL_FORMAT "pixel_format" -#define COLOR_SPACE "color_space" -#define COLOR_RANGE "color_range" -#define BUFFERING "buffering" - -#define TEXT_DEVICE obs_module_text("Device") -#define TEXT_MODE obs_module_text("Mode") -#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat") -#define TEXT_COLOR_SPACE obs_module_text("ColorSpace") -#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default") -#define TEXT_COLOR_RANGE obs_module_text("ColorRange") -#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default") -#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial") -#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full") -#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat") -#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None") -#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch") -#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch") -#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch") -#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch") -#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch") -#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch") -#define TEXT_BUFFERING obs_module_text("Buffering") - -static DeckLinkDeviceDiscovery *deviceEnum = nullptr; - -static void decklink_enable_buffering(DeckLink *decklink, bool enabled) -{ - obs_source_t *source = decklink->GetSource(); - obs_source_set_async_unbuffered(source, !enabled); - decklink->buffering = enabled; -} - -static void *decklink_create(obs_data_t *settings, obs_source_t *source) -{ - DeckLink *decklink = new DeckLink(source, deviceEnum); - - obs_source_set_async_decoupled(source, true); - decklink_enable_buffering(decklink, - obs_data_get_bool(settings, BUFFERING)); - - obs_source_update(source, settings); - return decklink; -} - -static void decklink_destroy(void *data) -{ - DeckLink *decklink = (DeckLink *)data; - delete decklink; -} - -static void decklink_update(void *data, obs_data_t *settings) -{ - DeckLink *decklink = (DeckLink *)data; - const char *hash = obs_data_get_string(settings, DEVICE_HASH); - long long id = obs_data_get_int(settings, MODE_ID); - BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, - PIXEL_FORMAT); - video_colorspace colorSpace = (video_colorspace)obs_data_get_int(settings, - COLOR_SPACE); - video_range_type colorRange = (video_range_type)obs_data_get_int(settings, - COLOR_RANGE); - int chFmtInt = (int)obs_data_get_int(settings, CHANNEL_FORMAT); - - if (chFmtInt == 7) { - chFmtInt = SPEAKERS_5POINT1; - } else if (chFmtInt < SPEAKERS_UNKNOWN || chFmtInt > SPEAKERS_7POINT1) { - chFmtInt = 2; - } - - speaker_layout channelFormat = (speaker_layout)chFmtInt; - - decklink_enable_buffering(decklink, - obs_data_get_bool(settings, BUFFERING)); - - ComPtr device; - device.Set(deviceEnum->FindByHash(hash)); - - decklink->SetPixelFormat(pixelFormat); - decklink->SetColorSpace(colorSpace); - decklink->SetColorRange(colorRange); - decklink->SetChannelFormat(channelFormat); - decklink->Activate(device, id); -} - -static void decklink_get_defaults(obs_data_t *settings) -{ - obs_data_set_default_bool(settings, BUFFERING, false); - obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV); - obs_data_set_default_int(settings, COLOR_SPACE, VIDEO_CS_DEFAULT); - obs_data_set_default_int(settings, COLOR_RANGE, VIDEO_RANGE_DEFAULT); - obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO); -} - -static const char *decklink_get_name(void*) +MODULE_EXPORT const char *obs_module_description(void) { - return obs_module_text("BlackmagicDevice"); + return "Blackmagic DeckLink source"; } -static bool decklink_device_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - const char *name = obs_data_get_string(settings, DEVICE_NAME); - const char *hash = obs_data_get_string(settings, DEVICE_HASH); - const char *mode = obs_data_get_string(settings, MODE_NAME); - long long modeId = obs_data_get_int(settings, MODE_ID); - - size_t itemCount = obs_property_list_item_count(list); - bool itemFound = false; - - for (size_t i = 0; i < itemCount; i++) { - const char *curHash = obs_property_list_item_string(list, i); - if (strcmp(hash, curHash) == 0) { - itemFound = true; - break; - } - } - - if (!itemFound) { - obs_property_list_insert_string(list, 0, name, hash); - obs_property_list_item_disable(list, 0, true); - } - - obs_property_t *modeList = obs_properties_get(props, MODE_ID); - obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); - - obs_property_list_clear(modeList); - - obs_property_list_clear(channelList); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_NONE, - SPEAKERS_UNKNOWN); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_0CH, - SPEAKERS_STEREO); - - ComPtr device; - device.Set(deviceEnum->FindByHash(hash)); - - if (!device) { - obs_property_list_add_int(modeList, mode, modeId); - obs_property_list_item_disable(modeList, 0, true); - } else { - const std::vector &modes = - device->GetModes(); +extern struct obs_source_info create_decklink_source_info(); +struct obs_source_info decklink_source_info; - for (DeckLinkDeviceMode *mode : modes) { - obs_property_list_add_int(modeList, - mode->GetName().c_str(), - mode->GetId()); - } - - if (device->GetMaxChannel() >= 8) { - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_1CH, - SPEAKERS_2POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_4_0CH, - SPEAKERS_4POINT0); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_4_1CH, - SPEAKERS_4POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_5_1CH, - SPEAKERS_5POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_7_1CH, - SPEAKERS_7POINT1); - } - } - - return true; -} - -static void fill_out_devices(obs_property_t *list) -{ - deviceEnum->Lock(); - - const std::vector &devices = deviceEnum->GetDevices(); - for (DeckLinkDevice *device : devices) { - obs_property_list_add_string(list, - device->GetDisplayName().c_str(), - device->GetHash().c_str()); - } - - deviceEnum->Unlock(); -} - -static bool color_format_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings); - -static bool mode_id_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - long long id = obs_data_get_int(settings, MODE_ID); - - list = obs_properties_get(props, PIXEL_FORMAT); - obs_property_set_visible(list, id != MODE_ID_AUTO); - - return color_format_changed(props, nullptr, settings); -} - -static bool color_format_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - long long id = obs_data_get_int(settings, MODE_ID); - BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, - PIXEL_FORMAT); - - list = obs_properties_get(props, COLOR_SPACE); - obs_property_set_visible(list, - id != MODE_ID_AUTO && pixelFormat == bmdFormat8BitYUV); - - list = obs_properties_get(props, COLOR_RANGE); - obs_property_set_visible(list, - id == MODE_ID_AUTO || pixelFormat == bmdFormat8BitYUV); - - return true; -} - -static obs_properties_t *decklink_get_properties(void *data) -{ - obs_properties_t *props = obs_properties_create(); - - obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, - TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - obs_property_set_modified_callback(list, decklink_device_changed); - - fill_out_devices(list); - - list = obs_properties_add_list(props, MODE_ID, TEXT_MODE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_modified_callback(list, mode_id_changed); - - list = obs_properties_add_list(props, PIXEL_FORMAT, - TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_modified_callback(list, color_format_changed); - - obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); - obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); - - list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT, VIDEO_CS_DEFAULT); - obs_property_list_add_int(list, "BT.601", VIDEO_CS_601); - obs_property_list_add_int(list, "BT.709", VIDEO_CS_709); - - list = obs_properties_add_list(props, COLOR_RANGE, TEXT_COLOR_RANGE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT, VIDEO_RANGE_DEFAULT); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL, VIDEO_RANGE_PARTIAL); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL, VIDEO_RANGE_FULL); - - list = obs_properties_add_list(props, CHANNEL_FORMAT, - TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE, - SPEAKERS_UNKNOWN); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH, - SPEAKERS_STEREO); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_1CH, - SPEAKERS_2POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_0CH, - SPEAKERS_4POINT0); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_1CH, - SPEAKERS_4POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_5_1CH, - SPEAKERS_5POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH, - SPEAKERS_7POINT1); - - obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); - - UNUSED_PARAMETER(data); - return props; -} +extern struct obs_output_info create_decklink_output_info(); +struct obs_output_info decklink_output_info; bool obs_module_load(void) { @@ -289,19 +20,11 @@ bool obs_module_load(void) if (!deviceEnum->Init()) return true; - struct obs_source_info info = {}; - info.id = "decklink-input"; - info.type = OBS_SOURCE_TYPE_INPUT; - info.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | - OBS_SOURCE_DO_NOT_DUPLICATE; - info.create = decklink_create; - info.destroy = decklink_destroy; - info.get_defaults = decklink_get_defaults; - info.get_name = decklink_get_name; - info.get_properties = decklink_get_properties; - info.update = decklink_update; + decklink_source_info = create_decklink_source_info(); + obs_register_source(&decklink_source_info); - obs_register_source(&info); + decklink_output_info = create_decklink_output_info(); + obs_register_output(&decklink_output_info); return true; } diff --git a/plugins/decklink/win/CMakeLists.txt b/plugins/decklink/win/CMakeLists.txt index af9464510..19ecaaeb3 100644 --- a/plugins/decklink/win/CMakeLists.txt +++ b/plugins/decklink/win/CMakeLists.txt @@ -16,8 +16,12 @@ set(win-decklink-sdk_HEADERS ) set(win-decklink_HEADERS + ../decklink-devices.hpp + ../DecklinkOutput.hpp + ../const.h ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp @@ -28,25 +32,34 @@ set(win-decklink_HEADERS set(win-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../DecklinkOutput.cpp + ../decklink-source.cpp + ../decklink-output.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ) add_idl_files(win-decklink-sdk_GENERATED_FILES - ${win-decklink-sdk_IDLS}) + ${win-decklink-sdk_IDLS} + ) include_directories( - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_BINARY_DIR} +) add_library(win-decklink MODULE ${win-decklink_SOURCES} ${win-decklink_HEADERS} ${win-decklink-sdk_HEADERS} - ${win-decklink-sdk_GENERATED_FILES}) + ${win-decklink-sdk_GENERATED_FILES} + ) target_link_libraries(win-decklink libobs) diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl index 786d68d0d..abaea8eb7 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -95,9 +95,11 @@ typedef [v1_enum] enum _BMDPacketType { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameContainsHDRMetadata = 1 << 1, + bmdFrameContainsCintelMetadata = 1 << 2, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ + bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; @@ -136,10 +138,10 @@ typedef [v1_enum] enum _BMDPacketType { /* Enum BMDOutputFrameCompletionResult - Frame Completion Callback */ typedef [v1_enum] enum _BMDOutputFrameCompletionResult { - bmdOutputFrameCompleted, - bmdOutputFrameDisplayedLate, - bmdOutputFrameDropped, - bmdOutputFrameFlushed + bmdOutputFrameCompleted, + bmdOutputFrameDisplayedLate, + bmdOutputFrameDropped, + bmdOutputFrameFlushed } BMDOutputFrameCompletionResult; /* Enum BMDReferenceStatus - GenLock input status */ @@ -171,19 +173,27 @@ typedef [v1_enum] enum _BMDAudioSampleType { /* Enum BMDAudioOutputStreamType - Audio output stream type */ typedef [v1_enum] enum _BMDAudioOutputStreamType { - bmdAudioOutputStreamContinuous, - bmdAudioOutputStreamContinuousDontResample, - bmdAudioOutputStreamTimestamped + bmdAudioOutputStreamContinuous, + bmdAudioOutputStreamContinuousDontResample, + bmdAudioOutputStreamTimestamped } BMDAudioOutputStreamType; /* Enum BMDDisplayModeSupport - Output mode supported flags */ typedef [v1_enum] enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, - bmdDisplayModeSupported, - bmdDisplayModeSupportedWithConversion + bmdDisplayModeSupported, + bmdDisplayModeSupportedWithConversion } BMDDisplayModeSupport; +/* Enum BMDAncillaryPacketFormat - Ancillary packet format */ + +typedef [v1_enum] enum _BMDAncillaryPacketFormat { + bmdAncillaryPacketFormatUInt8 = /* 'ui08' */ 0x75693038, + bmdAncillaryPacketFormatUInt16 = /* 'ui16' */ 0x75693136, + bmdAncillaryPacketFormatYCbCr10 = /* 'v210' */ 0x76323130 +} BMDAncillaryPacketFormat; + /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ typedef [v1_enum] enum _BMDTimecodeFormat { @@ -296,6 +306,37 @@ typedef [v1_enum] enum _BMDDeviceInterface { typedef [v1_enum] enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = /* 'eotf' */ 0x656F7466, // EOTF in range 0-7 as per CEA 861.3 + bmdDeckLinkFrameMetadataCintelFilmType = /* 'cfty' */ 0x63667479, // Current film type + bmdDeckLinkFrameMetadataCintelFilmGauge = /* 'cfga' */ 0x63666761, // Current film gauge + bmdDeckLinkFrameMetadataCintelOffsetDetectedHorizontal = /* 'odfh' */ 0x6F646668, // Horizontal offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelOffsetDetectedVertical = /* 'odfv' */ 0x6F646676, // Vertical offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelKeykodeLow = /* 'ckkl' */ 0x636B6B6C, // Raw keykode value - low 64 bits + bmdDeckLinkFrameMetadataCintelKeykodeHigh = /* 'ckkh' */ 0x636B6B68, // Raw keykode value - high 64 bits + bmdDeckLinkFrameMetadataCintelTile1Size = /* 'ct1s' */ 0x63743173, // Size in bytes of compressed raw tile 1 + bmdDeckLinkFrameMetadataCintelTile2Size = /* 'ct2s' */ 0x63743273, // Size in bytes of compressed raw tile 2 + bmdDeckLinkFrameMetadataCintelTile3Size = /* 'ct3s' */ 0x63743373, // Size in bytes of compressed raw tile 3 + bmdDeckLinkFrameMetadataCintelTile4Size = /* 'ct4s' */ 0x63743473, // Size in bytes of compressed raw tile 4 + bmdDeckLinkFrameMetadataCintelImageWidth = /* 'IWPx' */ 0x49575078, // Width in pixels of image + bmdDeckLinkFrameMetadataCintelImageHeight = /* 'IHPx' */ 0x49485078, // Height in pixels of image + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed = /* 'mrir' */ 0x6D726972, // Red in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed = /* 'mgir' */ 0x6D676972, // Green in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed = /* 'mbir' */ 0x6D626972, // Blue in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen = /* 'mrig' */ 0x6D726967, // Red in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen = /* 'mgig' */ 0x6D676967, // Green in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen = /* 'mbig' */ 0x6D626967, // Blue in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue = /* 'mrib' */ 0x6D726962, // Red in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue = /* 'mgib' */ 0x6D676962, // Green in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue = /* 'mbib' */ 0x6D626962, // Blue in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed = /* 'mlrr' */ 0x6D6C7272, // Red in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed = /* 'mlgr' */ 0x6D6C6772, // Green in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed = /* 'mlbr' */ 0x6D6C6272, // Blue in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen = /* 'mlrg' */ 0x6D6C7267, // Red in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen = /* 'mlgg' */ 0x6D6C6767, // Green in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen = /* 'mlbg' */ 0x6D6C6267, // Blue in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue = /* 'mlrb' */ 0x6D6C7262, // Red in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue = /* 'mlgb' */ 0x6D6C6762, // Green in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue = /* 'mlbb' */ 0x6D6C6262, // Blue in blue log masking parameter + bmdDeckLinkFrameMetadataCintelFilmFrameRate = /* 'cffr' */ 0x63666672, // Film frame rate bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = /* 'hdrx' */ 0x68647278, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = /* 'hdry' */ 0x68647279, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = /* 'hdgx' */ 0x68646778, // Green display primaries in range 0.0 - 1.0 @@ -307,7 +348,15 @@ typedef [v1_enum] enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = /* 'hdml' */ 0x68646D6C, // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = /* 'hmil' */ 0x686D696C, // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = /* 'mcll' */ 0x6D636C6C, // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 - bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C, // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal = /* 'otah' */ 0x6F746168, // Horizontal offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical = /* 'otav' */ 0x6F746176, // Vertical offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelGainRed = /* 'LfRd' */ 0x4C665264, // Red gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainGreen = /* 'LfGr' */ 0x4C664772, // Green gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainBlue = /* 'LfBl' */ 0x4C66426C, // Blue gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelLiftRed = /* 'GnRd' */ 0x476E5264, // Red lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftGreen = /* 'GnGr' */ 0x476E4772, // Green lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftBlue = /* 'GnBl' */ 0x476E426C // Blue lift parameter to apply after log and gain } BMDDeckLinkFrameMetadataID; /* Enum BMDDuplexMode - Duplex for configurable ports */ @@ -347,18 +396,19 @@ typedef [v1_enum] enum _BMDDeckLinkAttributeID { /* Integers */ BMDDeckLinkMaximumAudioChannels = /* 'mach' */ 0x6D616368, - BMDDeckLinkMaximumAnalogAudioChannels = /* 'aach' */ 0x61616368, + BMDDeckLinkMaximumAnalogAudioInputChannels = /* 'iach' */ 0x69616368, + BMDDeckLinkMaximumAnalogAudioOutputChannels = /* 'aach' */ 0x61616368, BMDDeckLinkNumberOfSubDevices = /* 'nsbd' */ 0x6E736264, BMDDeckLinkSubDeviceIndex = /* 'subi' */ 0x73756269, BMDDeckLinkPersistentID = /* 'peid' */ 0x70656964, BMDDeckLinkDeviceGroupID = /* 'dgid' */ 0x64676964, BMDDeckLinkTopologicalID = /* 'toid' */ 0x746F6964, - BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, - BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, - BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, - BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, + BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, // Returns a BMDAudioConnection bit field + BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = /* 'vios' */ 0x76696F73, // Returns a BMDVideoIOSupport bit field - BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, + BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = /* 'dbus' */ 0x64627573, // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = /* 'airc' */ 0x61697263, BMDDeckLinkAudioInputXLRChannelCount = /* 'aixc' */ 0x61697863, @@ -410,11 +460,14 @@ typedef [v1_enum] enum _BMDDeckLinkStatusID { bmdDeckLinkStatusReferenceSignalFlags = /* 'reff' */ 0x72656666, bmdDeckLinkStatusDuplexMode = /* 'dupx' */ 0x64757078, bmdDeckLinkStatusBusy = /* 'busy' */ 0x62757379, + bmdDeckLinkStatusInterchangeablePanelType = /* 'icpt' */ 0x69637074, + bmdDeckLinkStatusDeviceTemperature = /* 'dtmp' */ 0x64746D70, /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = /* 'visl' */ 0x7669736C, - bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C + bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C, + bmdDeckLinkStatusReceivedEDID = /* 'edid' */ 0x65646964 } BMDDeckLinkStatusID; /* Enum BMDDeckLinkVideoStatusFlags - */ @@ -433,6 +486,13 @@ typedef [v1_enum] enum _BMDDuplexStatus { bmdDuplexStatusInactive = /* 'inac' */ 0x696E6163 } BMDDuplexStatus; +/* Enum BMDPanelType - The type of interchangeable panel */ + +typedef [v1_enum] enum _BMDPanelType { + bmdPanelNotDetected = /* 'npnl' */ 0x6E706E6C, + bmdPanelTeranexMiniSmartPanel = /* 'tmsm' */ 0x746D736D +} BMDPanelType; + /* Enum BMDDeviceBusyState - Current device busy state */ [v1_enum] enum _BMDDeviceBusyState { @@ -482,6 +542,9 @@ interface IDeckLinkMutableVideoFrame; interface IDeckLinkVideoFrame3DExtensions; interface IDeckLinkVideoFrameMetadataExtensions; interface IDeckLinkVideoInputFrame; +interface IDeckLinkAncillaryPacket; +interface IDeckLinkAncillaryPacketIterator; +interface IDeckLinkVideoFrameAncillaryPackets; interface IDeckLinkVideoFrameAncillary; interface IDeckLinkEncoderPacket; interface IDeckLinkEncoderVideoPacket; @@ -542,7 +605,7 @@ interface IDeckLinkDiscovery; [ object, uuid(B36EB6E7-9D29-4AA8-92EF-843B87A289E8), - local, + local, helpstring("Memory allocator for video frames.") ] interface IDeckLinkMemoryAllocator : IUnknown { @@ -558,7 +621,7 @@ interface IDeckLinkDiscovery; [ object, uuid(403C681B-7F46-4A12-B993-2BB127084EE6), - local, + local, helpstring("Optional callback to allow audio samples to be pulled as required.") ] interface IDeckLinkAudioOutputCallback : IUnknown { @@ -595,7 +658,7 @@ interface IDeckLinkDiscovery; [ object, uuid(CC5C8A6E-3F2F-4B3A-87EA-FD78AF300564), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput : IUnknown { @@ -611,7 +674,7 @@ interface IDeckLinkDiscovery; HRESULT SetVideoOutputFrameMemoryAllocator([in] IDeckLinkMemoryAllocator *theAllocator); HRESULT CreateVideoFrame([in] int width, [in] int height, [in] int rowBytes, [in] BMDPixelFormat pixelFormat, [in] BMDFrameFlags flags, [out] IDeckLinkMutableVideoFrame **outFrame); - HRESULT CreateAncillaryData([in] BMDPixelFormat pixelFormat, [out] IDeckLinkVideoFrameAncillary **outBuffer); + HRESULT CreateAncillaryData([in] BMDPixelFormat pixelFormat, [out] IDeckLinkVideoFrameAncillary **outBuffer); // Use of IDeckLinkVideoFrameAncillaryPackets is preferred HRESULT DisplayVideoFrameSync([in] IDeckLinkVideoFrame *theFrame); HRESULT ScheduleVideoFrame([in] IDeckLinkVideoFrame *theFrame, [in] BMDTimeValue displayTime, [in] BMDTimeValue displayDuration, [in] BMDTimeScale timeScale); @@ -729,7 +792,7 @@ interface IDeckLinkDiscovery; [ object, uuid(3F716FE0-F023-4111-BE5D-EF4414C05B17), - local, + local, helpstring("Interface to encapsulate a video frame; can be caller-implemented.") ] interface IDeckLinkVideoFrame : IUnknown { @@ -741,7 +804,7 @@ interface IDeckLinkDiscovery; HRESULT GetBytes([out] void **buffer); HRESULT GetTimecode([in] BMDTimecodeFormat format, [out] IDeckLinkTimecode **timecode); - HRESULT GetAncillaryData([out] IDeckLinkVideoFrameAncillary **ancillary); + HRESULT GetAncillaryData([out] IDeckLinkVideoFrameAncillary **ancillary); // Use of IDeckLinkVideoFrameAncillaryPackets is preferred }; /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ @@ -749,7 +812,7 @@ interface IDeckLinkDiscovery; [ object, uuid(69E2639F-40DA-4E19-B6F2-20ACE815C390), - local, + local, helpstring("Created by IDeckLinkOutput::CreateVideoFrame.") ] interface IDeckLinkMutableVideoFrame : IDeckLinkVideoFrame { @@ -766,7 +829,7 @@ interface IDeckLinkDiscovery; [ object, uuid(DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7), - local, + local, helpstring("Optional interface implemented on IDeckLinkVideoFrame to support 3D frames") ] interface IDeckLinkVideoFrame3DExtensions : IUnknown { @@ -779,7 +842,7 @@ interface IDeckLinkDiscovery; [ object, uuid(D5973DC9-6432-46D0-8F0B-2496F8A1238F), - local, + local, helpstring("Optional interface implemented on IDeckLinkVideoFrame to support frame metadata such as HDMI HDR information") ] interface IDeckLinkVideoFrameMetadataExtensions : IUnknown { @@ -794,7 +857,7 @@ interface IDeckLinkDiscovery; [ object, uuid(05CFE374-537C-4094-9A57-680525118F44), - local, + local, helpstring("Provided by the IDeckLinkVideoInput frame arrival callback.") ] interface IDeckLinkVideoInputFrame : IDeckLinkVideoFrame { @@ -802,17 +865,61 @@ interface IDeckLinkDiscovery; HRESULT GetHardwareReferenceTimestamp([in] BMDTimeScale timeScale, [out] BMDTimeValue *frameTime, [out] BMDTimeValue *frameDuration); }; -/* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ +/* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ [ object, - uuid(732E723C-D1A4-4E29-9E8E-4A88797A0004), - local, + uuid(CC5BBF7E-029C-4D3B-9158-6000EF5E3670), + helpstring("On output, user needs to implement this interface") +] interface IDeckLinkAncillaryPacket : IUnknown +{ + + HRESULT GetBytes([in] BMDAncillaryPacketFormat format /* For output, only one format need be offered */, [out] const void **data /* Optional */, [out] unsigned int *size /* Optional */); + unsigned char GetDID(void); + unsigned char GetSDID(void); + unsigned int GetLineNumber(void); // On output, zero is auto + unsigned char GetDataStreamIndex(void); // Usually zero. Can only be 1 if non-SD and the first data stream is completely full +}; + +/* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ + +[ + object, + uuid(3FC8994B-88FB-4C17-968F-9AAB69D964A7), + helpstring("Enumerates ancillary packets") +] interface IDeckLinkAncillaryPacketIterator : IUnknown +{ + HRESULT Next([out] IDeckLinkAncillaryPacket **packet); +}; + +/* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +[ + object, + uuid(6C186C0F-459E-41D8-AEE2-4812D81AEE68), + local, helpstring("Obtained through QueryInterface() on an IDeckLinkVideoFrame object.") +] interface IDeckLinkVideoFrameAncillaryPackets : IUnknown +{ + + HRESULT GetPacketIterator([out] IDeckLinkAncillaryPacketIterator **iterator); + HRESULT GetFirstPacketByID([in] unsigned char DID, [in] unsigned char SDID, [out] IDeckLinkAncillaryPacket **packet); + HRESULT AttachPacket([in] IDeckLinkAncillaryPacket *packet); // Implement IDeckLinkAncillaryPacket to output your own + HRESULT DetachPacket([in] IDeckLinkAncillaryPacket *packet); + HRESULT DetachAllPackets(void); +}; + +/* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +[ + object, + uuid(732E723C-D1A4-4E29-9E8E-4A88797A0004), + local, + helpstring("Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object.") ] interface IDeckLinkVideoFrameAncillary : IUnknown { - HRESULT GetBufferForVerticalBlankingLine([in] unsigned int lineNumber, [out] void **buffer); + HRESULT GetBufferForVerticalBlankingLine([in] unsigned int lineNumber, [out] void **buffer); // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes BMDPixelFormat GetPixelFormat(void); BMDDisplayMode GetDisplayMode(void); }; @@ -822,7 +929,7 @@ interface IDeckLinkDiscovery; [ object, uuid(B693F36C-316E-4AF1-B6C2-F389A4BCA620), - local, + local, helpstring("Interface to encapsulate an encoded packet.") ] interface IDeckLinkEncoderPacket : IUnknown { @@ -837,7 +944,7 @@ interface IDeckLinkDiscovery; [ object, uuid(4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0), - local, + local, helpstring("Provided by the IDeckLinkEncoderInput video packet arrival callback.") ] interface IDeckLinkEncoderVideoPacket : IDeckLinkEncoderPacket { @@ -852,7 +959,7 @@ interface IDeckLinkDiscovery; [ object, uuid(49E8EDC8-693B-4E14-8EF6-12C658F5A07A), - local, + local, helpstring("Provided by the IDeckLinkEncoderInput audio packet arrival callback.") ] interface IDeckLinkEncoderAudioPacket : IDeckLinkEncoderPacket { @@ -864,7 +971,7 @@ interface IDeckLinkDiscovery; [ object, uuid(639C8E0B-68D5-4BDE-A6D4-95F3AEAFF2E7), - local, + local, helpstring("Obtained through QueryInterface() on an IDeckLinkEncoderVideoPacket object") ] interface IDeckLinkH265NALPacket : IDeckLinkEncoderVideoPacket { @@ -878,7 +985,7 @@ interface IDeckLinkDiscovery; [ object, uuid(E43D5870-2894-11DE-8C30-0800200C9A66), - local, + local, helpstring("Provided by the IDeckLinkInput callback.") ] interface IDeckLinkAudioInputPacket : IUnknown { @@ -892,7 +999,7 @@ interface IDeckLinkDiscovery; [ object, uuid(B1D3F49A-85FE-4C5D-95C8-0B5D5DCCD438), - local, + local, helpstring("Screen preview callback") ] interface IDeckLinkScreenPreviewCallback : IUnknown { @@ -904,7 +1011,7 @@ interface IDeckLinkDiscovery; [ object, uuid(504E2209-CAC7-4C1A-9FB4-C5BB6274D22F), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkGLScreenPreviewHelper : IUnknown { @@ -922,7 +1029,7 @@ interface IDeckLinkDiscovery; [ object, uuid(2094B522-D1A1-40C0-9AC7-1C012218EF02), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkDX9ScreenPreviewHelper : IUnknown { @@ -937,7 +1044,7 @@ interface IDeckLinkDiscovery; [ object, uuid(b002a1ec-070d-4288-8289-bd5d36e5ff0d), - local, + local, helpstring("DeckLink Notification Callback Interface") ] interface IDeckLinkNotificationCallback : IUnknown { @@ -949,7 +1056,7 @@ interface IDeckLinkDiscovery; [ object, uuid(0a1fb207-e215-441b-9b19-6fa1575946c5), - local, + local, helpstring("DeckLink Notification interface") ] interface IDeckLinkNotification : IUnknown { @@ -962,7 +1069,7 @@ interface IDeckLinkDiscovery; [ object, uuid(ABC11843-D966-44CB-96E2-A1CB5D3135C4), - local, + local, helpstring("DeckLink Attribute interface") ] interface IDeckLinkAttributes : IUnknown { @@ -977,7 +1084,7 @@ interface IDeckLinkDiscovery; [ object, uuid(5F558200-4028-49BC-BEAC-DB3FA4A96E46), - local, + local, helpstring("DeckLink Status interface") ] interface IDeckLinkStatus : IUnknown { @@ -993,7 +1100,7 @@ interface IDeckLinkDiscovery; [ object, uuid(89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3), - local, + local, helpstring("DeckLink Keyer interface") ] interface IDeckLinkKeyer : IUnknown { @@ -1009,7 +1116,7 @@ interface IDeckLinkDiscovery; [ object, uuid(3BBCB8A2-DA2C-42D9-B5D8-88083644E99A), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkVideoConversion : IUnknown { @@ -1045,7 +1152,7 @@ interface IDeckLinkDiscovery; importlib("stdole2.tlb"); [ - uuid(1F2E109A-8F4F-49E4-9203-135595CB6FA5), + uuid(87D2693F-8D4A-45C7-B43F-10ACBA25E68F), helpstring("CDeckLinkIterator Class") ] coclass CDeckLinkIterator { @@ -1085,15 +1192,26 @@ importlib("stdole2.tlb"); }; [ - uuid(1073A05C-D885-47E9-B3C6-129B3F9F648B), + uuid(652615D4-26CD-4514-B161-2FD5072ED008), helpstring("CDeckLinkDiscovery Class") ] coclass CDeckLinkDiscovery { [default] interface IDeckLinkDiscovery; }; +[ + uuid(F891AD29-D0C2-46E9-A926-4E2D0DD8CFAD), + helpstring("CDeckLinkVideoFrameAncillaryPackets Class") +] coclass CDeckLinkVideoFrameAncillaryPackets +{ + [default] interface IDeckLinkVideoFrameAncillaryPackets; +}; + // import deprecated interfaces +#include "DeckLinkAPI_v10_9.idl" +#include "DeckLinkAPIStreaming_v10_8.idl" +#include "DeckLinkAPI_v10_8.idl" #include "DeckLinkAPI_v10_6.idl" #include "DeckLinkAPI_v10_5.idl" #include "DeckLinkAPI_v10_4.idl" diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl index 4e271e49c..10908c486 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -50,10 +50,6 @@ typedef [v1_enum] enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigSwapSerialRxTx = /* 'ssrt' */ 0x73737274, - /* Video Input/Output Flags */ - - bmdDeckLinkConfigUse1080pNotPsF = /* 'fpro' */ 0x6670726F, - /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066, @@ -74,6 +70,12 @@ typedef [v1_enum] enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F, bmdDeckLinkConfigDownConversionOnAllAnalogOutput = /* 'caao' */ 0x6361616F, bmdDeckLinkConfigSMPTELevelAOutput = /* 'smta' */ 0x736D7461, + bmdDeckLinkConfigRec2020Output = /* 'rec2' */ 0x72656332, // Ensure output is Rec.2020 colorspace + bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = /* 'SDQS' */ 0x53445153, + + /* Video Output Flags */ + + bmdDeckLinkConfigOutput1080pAsPsF = /* 'pfpr' */ 0x70667072, /* Video Output Integers */ @@ -102,6 +104,10 @@ typedef [v1_enum] enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigUseDedicatedLTCInput = /* 'dltc' */ 0x646C7463, // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = /* '3dds' */ 0x33646473, + /* Video Input Flags */ + + bmdDeckLinkConfigCapture1080pAsPsF = /* 'cfpr' */ 0x63667072, + /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E, @@ -203,8 +209,8 @@ interface IDeckLinkEncoderConfiguration; [ object, - uuid(CB71734A-FE37-4E8D-8E13-802133A1C3F2), - local, + uuid(EF90380B-4AE5-4346-9077-E288E149F129), + local, helpstring("DeckLink Configuration interface") ] interface IDeckLinkConfiguration : IUnknown { @@ -224,7 +230,7 @@ interface IDeckLinkEncoderConfiguration; [ object, uuid(138050E5-C60A-4552-BF3F-0F358049327E), - local, + local, helpstring("DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput") ] interface IDeckLinkEncoderConfiguration : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl index cba3f2ec0..bd5e58723 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl index 8b9db8ed5..b9df968c8 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl index e4f4c35e0..ad3d2a450 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -63,12 +63,12 @@ typedef [v1_enum] enum _BMDDisplayMode { bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235, bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239, bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330, - bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, - bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, - bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530, bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539, bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz. + bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, + bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, + bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ @@ -76,19 +76,24 @@ typedef [v1_enum] enum _BMDDisplayMode { bmdModeHD720p5994 = /* 'hp59' */ 0x68703539, bmdModeHD720p60 = /* 'hp60' */ 0x68703630, - /* 2k Modes */ + /* 2K Modes */ bmdMode2k2398 = /* '2k23' */ 0x326B3233, bmdMode2k24 = /* '2k24' */ 0x326B3234, bmdMode2k25 = /* '2k25' */ 0x326B3235, - /* DCI Modes (output only) */ + /* 2K DCI Modes */ bmdMode2kDCI2398 = /* '2d23' */ 0x32643233, bmdMode2kDCI24 = /* '2d24' */ 0x32643234, bmdMode2kDCI25 = /* '2d25' */ 0x32643235, + bmdMode2kDCI2997 = /* '2d29' */ 0x32643239, + bmdMode2kDCI30 = /* '2d30' */ 0x32643330, + bmdMode2kDCI50 = /* '2d50' */ 0x32643530, + bmdMode2kDCI5994 = /* '2d59' */ 0x32643539, + bmdMode2kDCI60 = /* '2d60' */ 0x32643630, - /* 4k Modes */ + /* 4K UHD Modes */ bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233, bmdMode4K2160p24 = /* '4k24' */ 0x346B3234, @@ -99,11 +104,43 @@ typedef [v1_enum] enum _BMDDisplayMode { bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539, bmdMode4K2160p60 = /* '4k60' */ 0x346B3630, - /* DCI Modes (output only) */ + /* 4K DCI Modes */ bmdMode4kDCI2398 = /* '4d23' */ 0x34643233, bmdMode4kDCI24 = /* '4d24' */ 0x34643234, bmdMode4kDCI25 = /* '4d25' */ 0x34643235, + bmdMode4kDCI2997 = /* '4d29' */ 0x34643239, + bmdMode4kDCI30 = /* '4d30' */ 0x34643330, + bmdMode4kDCI50 = /* '4d50' */ 0x34643530, + bmdMode4kDCI5994 = /* '4d59' */ 0x34643539, + bmdMode4kDCI60 = /* '4d60' */ 0x34643630, + + /* 8K UHD Modes */ + + bmdMode8K4320p2398 = /* '8k23' */ 0x386B3233, + bmdMode8K4320p24 = /* '8k24' */ 0x386B3234, + bmdMode8K4320p25 = /* '8k25' */ 0x386B3235, + bmdMode8K4320p2997 = /* '8k29' */ 0x386B3239, + bmdMode8K4320p30 = /* '8k30' */ 0x386B3330, + bmdMode8K4320p50 = /* '8k50' */ 0x386B3530, + bmdMode8K4320p5994 = /* '8k59' */ 0x386B3539, + bmdMode8K4320p60 = /* '8k60' */ 0x386B3630, + + /* 8K DCI Modes */ + + bmdMode8kDCI2398 = /* '8d23' */ 0x38643233, + bmdMode8kDCI24 = /* '8d24' */ 0x38643234, + bmdMode8kDCI25 = /* '8d25' */ 0x38643235, + bmdMode8kDCI2997 = /* '8d29' */ 0x38643239, + bmdMode8kDCI30 = /* '8d30' */ 0x38643330, + bmdMode8kDCI50 = /* '8d50' */ 0x38643530, + bmdMode8kDCI5994 = /* '8d59' */ 0x38643539, + bmdMode8kDCI60 = /* '8d60' */ 0x38643630, + + /* RAW Modes for Cintel (input only) */ + + bmdModeCintelRAW = /* 'rwci' */ 0x72776369, // Frame size up to 4096x3072, variable frame rate + bmdModeCintelCompressedRAW = /* 'rwcc' */ 0x72776363, // Frame size up to 4096x3072, variable frame rate /* Special Modes */ @@ -136,7 +173,12 @@ typedef [v1_enum] enum _BMDPixelFormat { /* AVID DNxHR */ - bmdFormatDNxHR = /* 'AVdh' */ 0x41566468 + bmdFormatDNxHR = /* 'AVdh' */ 0x41566468, + + /* Cintel formats */ + + bmdFormat12BitRAWGRBG = /* 'r12p' */ 0x72313270, // 12-bit RAW data for bayer pattern GRBG + bmdFormat12BitRAWJPEG = /* 'r16p' */ 0x72313670 // 12-bit RAW data arranged in tiles and JPEG compressed } BMDPixelFormat; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl index b609e5d1f..28ddeb51d 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -77,8 +77,8 @@ typedef [v1_enum] enum _BMDStreamingEncodingFrameRate { typedef [v1_enum] enum _BMDStreamingEncodingSupport { bmdStreamingEncodingModeNotSupported = 0, - bmdStreamingEncodingModeSupported, - bmdStreamingEncodingModeSupportedWithChanges + bmdStreamingEncodingModeSupported, + bmdStreamingEncodingModeSupportedWithChanges } BMDStreamingEncodingSupport; /* Enum BMDStreamingVideoCodec - Video codecs */ @@ -346,7 +346,7 @@ interface IBMDStreamingH264NALParser; importlib("stdole2.tlb"); [ - uuid(0CAA31F6-8A26-40B0-86A4-BF58DCCA710C), + uuid(23A4EDF5-A0E5-432C-94EF-3BABB5F81C82), helpstring("CBMDStreamingDiscovery Class") ] coclass CBMDStreamingDiscovery { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming_v10_8.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming_v10_8.idl new file mode 100644 index 000000000..7d8aa5af7 --- /dev/null +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming_v10_8.idl @@ -0,0 +1,40 @@ +/* -LICENSE-START- +** Copyright (c) 2016 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + + + +/* Coclasses */ + +importlib("stdole2.tlb"); + +[ + uuid(0CAA31F6-8A26-40B0-86A4-BF58DCCA710C), + helpstring("CBMDStreamingDiscovery Class") +] coclass CBMDStreamingDiscovery_v10_8 +{ + [default] interface IBMDStreamingDiscovery; +}; diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl index 35915b458..4f41e03dc 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -53,7 +53,8 @@ cpp_quote("#endif") [v1_enum] enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, - bmdTimecodeFieldMark = 1 << 1 + bmdTimecodeFieldMark = 1 << 1, + bmdTimecodeColorFrame = 1 << 2 }; /* Enum BMDVideoConnection - Video connection types */ diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h b/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h index dfd1799e5..995318d3f 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h @@ -7,14 +7,14 @@ * ** execute, and transmit the Software, and to prepare derivative works of the * ** Software, and to permit third-parties to whom the Software is furnished to * ** do so, all subject to the following: - * ** + * ** * ** The copyright notices in the Software and this entire statement, including * ** the above license grant, this restriction and the following disclaimer, * ** must be included in all copies of the Software, in whole or in part, and * ** all derivative works of the Software, unless such copies or derivative * ** works are solely in the form of machine-executable object code generated by * ** a source language processor. - * ** + * ** * ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -30,8 +30,8 @@ #ifndef __DeckLink_API_Version_h__ #define __DeckLink_API_Version_h__ -#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a080000 -#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.8" +#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a0b0000 +#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.11" #endif // __DeckLink_API_Version_h__ diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl index 12b625099..02a58a297 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -57,7 +57,7 @@ interface IDeckLinkConfiguration_v10_2; [ object, uuid(C679A35B-610C-4D09-B748-1D0478100FC0), - local, + local, helpstring("DeckLink Configuration interface") ] interface IDeckLinkConfiguration_v10_2 : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl index 7348e491d..1e89d3989 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl @@ -45,7 +45,7 @@ interface IDeckLinkEncoderConfiguration_v10_5; [ object, uuid(67455668-0848-45DF-8D8E-350A77C9A028), - local, + local, helpstring("DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput") ] interface IDeckLinkEncoderConfiguration_v10_5 : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_8.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_8.idl new file mode 100644 index 000000000..c23b99365 --- /dev/null +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_8.idl @@ -0,0 +1,46 @@ +/* -LICENSE-START- +** Copyright (c) 2016 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ +/* DeckLinkAPI_v10_8.idl */ + + +importlib("stdole2.tlb"); + +[ + uuid(1F2E109A-8F4F-49E4-9203-135595CB6FA5), + helpstring("CDeckLinkIterator_v10_8 Class") +] coclass CDeckLinkIterator_v10_8 +{ + [default] interface IDeckLinkIterator; +}; + +[ + uuid(1073A05C-D885-47E9-B3C6-129B3F9F648B), + helpstring("CDeckLinkDiscovery_v10_8 Class") +] coclass CDeckLinkDiscovery_v10_8 +{ + [default] interface IDeckLinkDiscovery; +}; diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_9.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_9.idl new file mode 100644 index 000000000..5506387b4 --- /dev/null +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_9.idl @@ -0,0 +1,61 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ +/* DeckLinkAPI_v10_9.idl */ + +/* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ + +typedef [v1_enum] enum _BMDDeckLinkConfigurationID_v10_9 { + + /* Video output flags */ + + bmdDeckLinkConfig1080pNotPsF_v10_9 = /* 'fpro' */ 0x6670726F, + +} BMDDeckLinkConfigurationID_v10_9; + +// Forward Declarations + +interface IDeckLinkConfiguration_v10_9; + +/* Interface IDeckLinkConfiguration_v10_9 - DeckLink Configuration interface */ + +[ + object, + uuid(CB71734A-FE37-4E8D-8E13-802133A1C3F2), + local, + helpstring("DeckLink Configuration interface") +] interface IDeckLinkConfiguration_v10_9 : IUnknown +{ + HRESULT SetFlag([in] BMDDeckLinkConfigurationID cfgID, [in] BOOL value); + HRESULT GetFlag([in] BMDDeckLinkConfigurationID cfgID, [out] BOOL *value); + HRESULT SetInt([in] BMDDeckLinkConfigurationID cfgID, [in] LONGLONG value); + HRESULT GetInt([in] BMDDeckLinkConfigurationID cfgID, [out] LONGLONG *value); + HRESULT SetFloat([in] BMDDeckLinkConfigurationID cfgID, [in] double value); + HRESULT GetFloat([in] BMDDeckLinkConfigurationID cfgID, [out] double *value); + HRESULT SetString([in] BMDDeckLinkConfigurationID cfgID, [in] BSTR value); + HRESULT GetString([in] BMDDeckLinkConfigurationID cfgID, [out] BSTR *value); + HRESULT WriteConfigurationToPreferences(void); +}; diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl index e21b5a80a..0fcc88edb 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -31,7 +31,7 @@ interface IDeckLinkVideoFrame_v7_1; interface IDeckLinkVideoInputFrame_v7_1; interface IDeckLinkAudioInputPacket_v7_1; - + [object, uuid(B28131B6-59AC-4857-B5AC-CD75D5883E2F), helpstring("IDeckLinkDisplayModeIterator_v7_1 enumerates over supported input/output display modes.")] interface IDeckLinkDisplayModeIterator_v7_1 : IUnknown @@ -50,21 +50,21 @@ long GetHeight (); HRESULT GetFrameRate ([out] BMDTimeValue *frameDuration, [out] BMDTimeScale *timeScale); }; - + [object, uuid(EBD01AFA-E4B0-49C6-A01D-EDB9D1B55FD9), helpstring("IDeckLinkVideoOutputCallback. Frame completion callback.")] interface IDeckLinkVideoOutputCallback_v7_1 : IUnknown { HRESULT ScheduledFrameCompleted ([in] IDeckLinkVideoFrame_v7_1* completedFrame, [in] BMDOutputFrameCompletionResult result); }; - + [object, uuid(7F94F328-5ED4-4E9F-9729-76A86BDC99CC), helpstring("IDeckLinkInputCallback_v7_1. Frame arrival callback.")] interface IDeckLinkInputCallback_v7_1 : IUnknown { HRESULT VideoInputFrameArrived ([in] IDeckLinkVideoInputFrame_v7_1* videoFrame, [in] IDeckLinkAudioInputPacket_v7_1* audioPacket); }; - + [object, uuid(AE5B3E9B-4E1E-4535-B6E8-480FF52F6CE5), local, helpstring("IDeckLinkOutput_v7_1. Created by QueryInterface from IDeckLink.")] @@ -72,11 +72,11 @@ { HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, [out] BMDDisplayModeSupport *result); HRESULT GetDisplayModeIterator ([out] IDeckLinkDisplayModeIterator_v7_1 **iterator); - + // Video output HRESULT EnableVideoOutput (BMDDisplayMode displayMode); HRESULT DisableVideoOutput (); - + HRESULT SetVideoOutputFrameMemoryAllocator ([in] IDeckLinkMemoryAllocator* theAllocator); HRESULT CreateVideoFrame (int width, int height, int rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1** outFrame); HRESULT CreateVideoFrameFromBuffer (void* buffer, int width, int height, int rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1** outFrame); @@ -84,22 +84,22 @@ HRESULT DisplayVideoFrameSync (IDeckLinkVideoFrame_v7_1* theFrame); HRESULT ScheduleVideoFrame (IDeckLinkVideoFrame_v7_1* theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale); HRESULT SetScheduledFrameCompletionCallback ([in] IDeckLinkVideoOutputCallback_v7_1* theCallback); - + // Audio output HRESULT EnableAudioOutput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned int channelCount); HRESULT DisableAudioOutput (); - + HRESULT WriteAudioSamplesSync (void* buffer, unsigned int sampleFrameCount, [out] unsigned int *sampleFramesWritten); - + HRESULT BeginAudioPreroll (); HRESULT EndAudioPreroll (); HRESULT ScheduleAudioSamples (void* buffer, unsigned int sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, [out] unsigned int *sampleFramesWritten); - + HRESULT GetBufferedAudioSampleFrameCount ( [out] unsigned int *bufferedSampleCount); HRESULT FlushBufferedAudioSamples (); - + HRESULT SetAudioCallback ( [in] IDeckLinkAudioOutputCallback* theCallback); - + // Output control HRESULT StartScheduledPlayback (BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed); HRESULT StopScheduledPlayback (BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale); @@ -112,24 +112,24 @@ { HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, [out] BMDDisplayModeSupport *result); HRESULT GetDisplayModeIterator ([out] IDeckLinkDisplayModeIterator_v7_1 **iterator); - + // Video input HRESULT EnableVideoInput (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags); HRESULT DisableVideoInput (); - + // Audio input HRESULT EnableAudioInput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned int channelCount); HRESULT DisableAudioInput (); HRESULT ReadAudioSamples (void* buffer, unsigned int sampleFrameCount, [out] unsigned int *sampleFramesRead, [out] BMDTimeValue *audioPacketTime, BMDTimeScale timeScale); HRESULT GetBufferedAudioSampleFrameCount ( [out] unsigned int *bufferedSampleCount); - + // Input control HRESULT StartStreams (); HRESULT StopStreams (); HRESULT PauseStreams (); HRESULT SetCallback ([in] IDeckLinkInputCallback_v7_1* theCallback); }; - + [object, uuid(333F3A10-8C2D-43CF-B79D-46560FEEA1CE), local, helpstring("IDeckLinkVideoFrame_v7_1. Created by IDeckLinkVideoOutput::CreateVideoFrame.")] interface IDeckLinkVideoFrame_v7_1 : IUnknown @@ -141,14 +141,14 @@ BMDFrameFlags GetFlags (); HRESULT GetBytes (void* *buffer); }; - + [object, uuid(C8B41D95-8848-40EE-9B37-6E3417FB114B), local, helpstring("IDeckLinkVideoInputFrame_v7_1. Provided by the IDeckLinkVideoInput frame arrival callback.")] interface IDeckLinkVideoInputFrame_v7_1 : IDeckLinkVideoFrame_v7_1 { HRESULT GetFrameTime (BMDTimeValue *frameTime, BMDTimeValue *frameDuration, BMDTimeScale timeScale); }; - + [object, uuid(C86DE4F6-A29F-42E3-AB3A-1363E29F0788), local, helpstring("IDeckLinkAudioInputPacket_v7_1. Provided by the IDeckLinkInput callback.")] interface IDeckLinkAudioInputPacket_v7_1 : IUnknown @@ -157,4 +157,4 @@ HRESULT GetBytes (void* *buffer); HRESULT GetAudioPacketTime (BMDTimeValue *packetTime, BMDTimeScale timeScale); }; - + diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl index 9dfa497e5..3f3e00bd5 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -55,7 +55,7 @@ interface IDeckLinkVideoInputFrame_v7_3; [ object, uuid(271C65E3-C323-4344-A30F-D908BCB20AA3), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput_v7_3 : IUnknown { @@ -146,7 +146,7 @@ interface IDeckLinkVideoInputFrame_v7_3; [ object, uuid(CF317790-2894-11DE-8C30-0800200C9A66), - local, + local, helpstring("Provided by the IDeckLinkVideoInput frame arrival callback.") ] interface IDeckLinkVideoInputFrame_v7_3 : IDeckLinkVideoFrame_v7_6 { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl index 8ed7b73a2..c9b9b1877 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -114,7 +114,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(29228142-EB8C-4141-A621-F74026450955), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput_v7_6 : IUnknown { @@ -225,7 +225,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(A8D8238E-6B18-4196-99E1-5AF717B83D32), - local, + local, helpstring("Interface to encapsulate a video frame; can be caller-implemented.") ] interface IDeckLinkVideoFrame_v7_6 : IUnknown { @@ -246,7 +246,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(46FCEE00-B4E6-43D0-91C0-023A7FCEB34F), - local, + local, helpstring("Created by IDeckLinkOutput::CreateVideoFrame.") ] interface IDeckLinkMutableVideoFrame_v7_6 : IDeckLinkVideoFrame_v7_6 { @@ -263,7 +263,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(9A74FA41-AE9F-47AC-8CF4-01F42DD59965), - local, + local, helpstring("Provided by the IDeckLinkVideoInput frame arrival callback.") ] interface IDeckLinkVideoInputFrame_v7_6 : IDeckLinkVideoFrame_v7_6 { @@ -277,7 +277,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(373F499D-4B4D-4518-AD22-6354E5A5825E), - local, + local, helpstring("Screen preview callback") ] interface IDeckLinkScreenPreviewCallback_v7_6 : IUnknown { @@ -290,7 +290,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(BA575CD9-A15E-497B-B2C2-F9AFE7BE4EBA), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkGLScreenPreviewHelper_v7_6 : IUnknown { @@ -308,7 +308,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(3EB504C9-F97D-40FE-A158-D407D48CB53B), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkVideoConversion_v7_6 : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl index 722984a29..b2228999e 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl index a2da181c6..079f60bf2 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl index 59b9a2180..5d163ae45 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl index e918e0405..6c6179a68 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl index a5e143978..c908e4260 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -33,7 +33,7 @@ [ object, uuid(A3EF0963-0862-44ED-92A9-EE89ABF431C7), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput_v9_9 : IUnknown { diff --git a/plugins/image-source/data/locale/el-GR.ini b/plugins/image-source/data/locale/el-GR.ini index 5922cbfab..952ab1b1a 100644 --- a/plugins/image-source/data/locale/el-GR.ini +++ b/plugins/image-source/data/locale/el-GR.ini @@ -9,11 +9,25 @@ SlideShow.Files="Αρχεία εικόνων" SlideShow.CustomSize="Μέγεθος Οριοθέτησης/Αναλογία" SlideShow.CustomSize.Auto="Αυτόματο" SlideShow.Randomize="Τυχαιοποίηση αναπαραγωγής" +SlideShow.Loop="Επανάληψη" SlideShow.Transition="Μετάβαση" SlideShow.Transition.Cut="Αποκοπή" SlideShow.Transition.Fade="Ξεθώριασμα" SlideShow.Transition.Swipe="Σύρσιμο" SlideShow.Transition.Slide="Ολίσθηση" +SlideShow.PlaybackBehavior="Συμπεριφορά Ορατότητας" +SlideShow.PlaybackBehavior.StopRestart="Διακοπή όταν δεν είναι ορατή, επανεκκίνηση όταν είναι ορατή" +SlideShow.PlaybackBehavior.PauseUnpause="Παύση όταν δεν είναι ορατή, συνέχεια όταν είναι ορατή" +SlideShow.PlaybackBehavior.AlwaysPlay="Αναπαραγωγή πάντα, ακόμα και αν δεν είναι ορατή" +SlideShow.SlideMode="Λειτουργία παρουσίασης διαφανειών" +SlideShow.SlideMode.Auto="Αυτόματα" +SlideShow.SlideMode.Manual="Χειροκίνητα (χρήση συντομέυσεων για τον έλεγχο slideshow)" +SlideShow.PlayPause="Αναπαραγωγή/Παύση" +SlideShow.Restart="Επανεκκίνηση" +SlideShow.Stop="Διακοπή" +SlideShow.NextSlide="Επόμενη διαφάνεια" +SlideShow.PreviousSlide="Προηγούμενη διαφάνεια" +SlideShow.HideWhenDone="Απόκρυψη όταν η παρουσίαση τελειώσει" ColorSource="Πηγή χρώματος" ColorSource.Color="Χρώμα" diff --git a/plugins/image-source/data/locale/et-EE.ini b/plugins/image-source/data/locale/et-EE.ini index e4b17927a..cec51a43c 100644 --- a/plugins/image-source/data/locale/et-EE.ini +++ b/plugins/image-source/data/locale/et-EE.ini @@ -6,12 +6,17 @@ SlideShow="Piltide slaidiesitus" SlideShow.TransitionSpeed="Ülemineku kiirus (millisekundites)" SlideShow.SlideTime="Aeg slaidide vahel (millisekundites)" SlideShow.Files="Pildifailid" +SlideShow.CustomSize.Auto="Automaatne" SlideShow.Randomize="Juhuslik taasesitus" +SlideShow.Loop="Korda" SlideShow.Transition="Üleminek" SlideShow.Transition.Cut="Ilmuv" SlideShow.Transition.Fade="Hajuv" SlideShow.Transition.Swipe="Pühkiv" SlideShow.Transition.Slide="Sisselendav" +SlideShow.PlaybackBehavior.AlwaysPlay="Mängi alati, isegi siis kui pole nähtav" +SlideShow.SlideMode.Auto="Automaatne" +SlideShow.PlayPause="Esita/Peata" ColorSource="Värvi allikas" ColorSource.Color="Värv" diff --git a/plugins/image-source/data/locale/fil-PH.ini b/plugins/image-source/data/locale/fil-PH.ini new file mode 100644 index 000000000..4284e5d3e --- /dev/null +++ b/plugins/image-source/data/locale/fil-PH.ini @@ -0,0 +1,36 @@ +ImageInput="Larawan" +File="Dokumentong Larawan" +UnloadWhenNotShowing="Mag-ibis ng imahe kapag hindi nagpapakita" + +SlideShow="Ipakita ang Slide ng Larawan" +SlideShow.TransitionSpeed="Bilis ng Paglipat (milliseconds)" +SlideShow.SlideTime="Oras sa Pagitan ng Mga Slide (millisecond)" +SlideShow.Files="Dokumentong Larawan" +SlideShow.CustomSize="Bounding Size / Aspect Ratio" +SlideShow.CustomSize.Auto="Awtomatikong" +SlideShow.Randomize="Simulan ang Pag-playback" +SlideShow.Loop="Silo" +SlideShow.Transition="Paglipat" +SlideShow.Transition.Cut="Kunin" +SlideShow.Transition.Fade="Fade" +SlideShow.Transition.Swipe="Mag-swipe" +SlideShow.Transition.Slide="Slide" +SlideShow.PlaybackBehavior="Pag-uugali ng Pagtingin" +SlideShow.PlaybackBehavior.StopRestart="Itigil kapag hindi nakikita, i-restart kapag nakikita" +SlideShow.PlaybackBehavior.PauseUnpause="I-pause kapag hindi nakikita, i-unpause kapag nakikita" +SlideShow.PlaybackBehavior.AlwaysPlay="Laging i-play kahit na hindi nakikita" +SlideShow.SlideMode="Slide Mode" +SlideShow.SlideMode.Auto="Awtomatikong" +SlideShow.SlideMode.Manual="Mano-manong (Gamitin ang mga hotkey upang kontrolin ang slideshow)" +SlideShow.PlayPause="I-play / I-pause" +SlideShow.Restart="I-restart" +SlideShow.Stop="Itigil" +SlideShow.NextSlide="Susunod na Slide" +SlideShow.PreviousSlide="Nakaraang Slide" +SlideShow.HideWhenDone="Itago kapag tapos na ang slideshow" + +ColorSource="Pinagmulan ng Kulay" +ColorSource.Color="Kulay" +ColorSource.Width="Lapad" +ColorSource.Height="Taas" + diff --git a/plugins/image-source/data/locale/gd-GB.ini b/plugins/image-source/data/locale/gd-GB.ini new file mode 100644 index 000000000..717437954 --- /dev/null +++ b/plugins/image-source/data/locale/gd-GB.ini @@ -0,0 +1,25 @@ +ImageInput="Dealbh" +File="Faidhle deilbh" +UnloadWhenNotShowing="Dì-luchdaich an dealbh mur eil e ’ga shealltainn" + +SlideShow="Taisbeanadh shleamhnagan dhealbhan" +SlideShow.TransitionSpeed="Luaths an tar-mhùthaidh (mille-dhiog)" +SlideShow.SlideTime="An ùine eadar na sleamhnagan (mille-dhiog)" +SlideShow.Files="Faidhlichean deilbh" +SlideShow.CustomSize="Meud an iadhaidh/Co-mheas deilbh" +SlideShow.CustomSize.Auto="Fèin-obrachail" +SlideShow.Loop="Lùb" +SlideShow.Transition="Tar-mhùthadh" +SlideShow.Transition.Cut="Gearr às" +SlideShow.Transition.Fade="Crìon" +SlideShow.Transition.Swipe="Grad-shlaighd" +SlideShow.Transition.Slide="Sleamhnaich" +SlideShow.SlideMode.Auto="Fèin-obrachail" +SlideShow.PlayPause="Cluich/Cuir ’na stad" +SlideShow.Restart="Ath-thòisich" +SlideShow.Stop="Cuir stad air" + +ColorSource.Color="Dath" +ColorSource.Width="Leud" +ColorSource.Height="Àirde" + diff --git a/plugins/image-source/data/locale/ka-GE.ini b/plugins/image-source/data/locale/ka-GE.ini new file mode 100644 index 000000000..f4736c7fc --- /dev/null +++ b/plugins/image-source/data/locale/ka-GE.ini @@ -0,0 +1,36 @@ +ImageInput="სურათი" +File="სურათის ფაილი" +UnloadWhenNotShowing="სურათის ამოღება, თუ არ გამოჩნდება" + +SlideShow="სლაიდშოუ" +SlideShow.TransitionSpeed="გადასვლის სიჩქარე (მილიწამებში)" +SlideShow.SlideTime="დაყოვნება სლაიდებს შორის (მილიწამებში)" +SlideShow.Files="სურათების ფაილები" +SlideShow.CustomSize="ჩარჩოს ზომა/გვერდების თანაფარდობა" +SlideShow.CustomSize.Auto="ავტომატური" +SlideShow.Randomize="შემთხვევითი შერჩევა" +SlideShow.Loop="გამეორება" +SlideShow.Transition="გადასვლები" +SlideShow.Transition.Cut="მოჭრა" +SlideShow.Transition.Fade="მილევა" +SlideShow.Transition.Swipe="შენაცვლება" +SlideShow.Transition.Slide="გადაწევა" +SlideShow.PlaybackBehavior="ხილვადობის მიხედვით მოქმედება" +SlideShow.PlaybackBehavior.StopRestart="შეწყვეტა თუ უხილავია, თავიდან გაშვება თუ ხილულია" +SlideShow.PlaybackBehavior.PauseUnpause="შეჩერება თუ უხილავია, გაშვება თუ ხილულია" +SlideShow.PlaybackBehavior.AlwaysPlay="ყოველთვის გაშვება, ხილვადობის მიუხედავად" +SlideShow.SlideMode="სლაიდების მართვა" +SlideShow.SlideMode.Auto="ავტომატური" +SlideShow.SlideMode.Manual="ხელით (ღილაკების გამოყენება სლაიდშოუს სამართავად)" +SlideShow.PlayPause="გაშვება/შეჩერება" +SlideShow.Restart="თავიდან გაშვება" +SlideShow.Stop="შეწყვეტა" +SlideShow.NextSlide="მომდევნო სლაიდი" +SlideShow.PreviousSlide="წინა სლაიდი" +SlideShow.HideWhenDone="დამალვა სლაიდშოუს დასრულებისას" + +ColorSource="ფონის ფერი" +ColorSource.Color="ფერი" +ColorSource.Width="სიგანე" +ColorSource.Height="სიმაღლე" + diff --git a/plugins/image-source/data/locale/ro-RO.ini b/plugins/image-source/data/locale/ro-RO.ini index 8b5ba985d..12e7f3852 100644 --- a/plugins/image-source/data/locale/ro-RO.ini +++ b/plugins/image-source/data/locale/ro-RO.ini @@ -5,9 +5,15 @@ UnloadWhenNotShowing="Eliberează din memorie imaginea când nu este afișată" SlideShow="Slide Show de imagini" SlideShow.TransitionSpeed="Viteza de tranziție (milisecunde)" SlideShow.SlideTime="Timpul dintre diapozitive (milisecunde)" +SlideShow.Files="Fișiere Imagine" +SlideShow.CustomSize.Auto="Automat" +SlideShow.Loop="Buclă" SlideShow.Transition="Tranziție" SlideShow.Transition.Cut="Decupare" SlideShow.Transition.Slide="Diapozitiv" +SlideShow.SlideMode.Auto="Automat" +SlideShow.NextSlide="Următorul diapozitiv" +SlideShow.PreviousSlide="Diapozitiv anterior" ColorSource.Color="Culoare" ColorSource.Width="Lățime" diff --git a/plugins/image-source/data/locale/ru-RU.ini b/plugins/image-source/data/locale/ru-RU.ini index 3f626a5b4..b74e4eed3 100644 --- a/plugins/image-source/data/locale/ru-RU.ini +++ b/plugins/image-source/data/locale/ru-RU.ini @@ -11,7 +11,7 @@ SlideShow.CustomSize.Auto="Автоматически" SlideShow.Randomize="Случайное воспроизведение" SlideShow.Loop="Повтор" SlideShow.Transition="Переход" -SlideShow.Transition.Cut="Обрезать" +SlideShow.Transition.Cut="Обрезка" SlideShow.Transition.Fade="Затухание" SlideShow.Transition.Swipe="Перемещение" SlideShow.Transition.Slide="Сдвиг" diff --git a/plugins/image-source/data/locale/tl-PH.ini b/plugins/image-source/data/locale/tl-PH.ini new file mode 100644 index 000000000..67a530b7f --- /dev/null +++ b/plugins/image-source/data/locale/tl-PH.ini @@ -0,0 +1,36 @@ +ImageInput="Imahe" +File="Imahe ng File" +UnloadWhenNotShowing="Mag-ibis ng imahe kapag hindi nagpapakita" + +SlideShow="Ipakita ang pag-slide ng Larawan" +SlideShow.TransitionSpeed="Bilis ng Paglipat (milsegundo)" +SlideShow.SlideTime="Oras sa Pagitan ng mga Slide (milsegundo)" +SlideShow.Files="Mga Imahe ng File" +SlideShow.CustomSize="Karatig na Sukat/Ayos ng Ratio" +SlideShow.CustomSize.Auto="Awtomatiko" +SlideShow.Randomize="Pagkumpara sa Pag-play pabalik" +SlideShow.Loop="Silo" +SlideShow.Transition="Paglipat" +SlideShow.Transition.Cut="Kunin" +SlideShow.Transition.Fade="Kumupas" +SlideShow.Transition.Swipe="Mag-swipe" +SlideShow.Transition.Slide="Mag-slide" +SlideShow.PlaybackBehavior="Paggiging kita ng Pag-uugali" +SlideShow.PlaybackBehavior.StopRestart="Itigil kapag hindi ito makita, i-restart kapag nakikita na" +SlideShow.PlaybackBehavior.PauseUnpause="I-pause kapag hindi makita, i-unpause kapag kita na" +SlideShow.PlaybackBehavior.AlwaysPlay="Laging paandarin kahit hindi nakikita" +SlideShow.SlideMode="Naka-slide Mode" +SlideShow.SlideMode.Auto="Awtomatiko" +SlideShow.SlideMode.Manual="Mano-mano (gamitin ang mga hotkey para makontrol ang pagpapakita ng slide)" +SlideShow.PlayPause="Paganahin/I-pause" +SlideShow.Restart="I-restart" +SlideShow.Stop="Itigil" +SlideShow.NextSlide="Susunod na Slide" +SlideShow.PreviousSlide="Ang nakaraang Slide" +SlideShow.HideWhenDone="Itago kapag ang pagpapakita ng slide ay tapos na" + +ColorSource="Pinagmulan ng kulay" +ColorSource.Color="Kulay" +ColorSource.Width="Lapad" +ColorSource.Height="Taas" + diff --git a/plugins/image-source/image-source.c b/plugins/image-source/image-source.c index b4643252f..cb23504cf 100644 --- a/plugins/image-source/image-source.c +++ b/plugins/image-source/image-source.c @@ -214,12 +214,14 @@ static void image_source_tick(void *data, float seconds) static const char *image_filter = - "All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif);;" + "All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif *.psd);;" "BMP Files (*.bmp);;" "Targa Files (*.tga);;" "PNG Files (*.png);;" "JPEG Files (*.jpeg *.jpg);;" - "GIF Files (*.gif)"; + "GIF Files (*.gif);;" + "PSD Files (*.psd);;" + "All Files (*.*)"; static obs_properties_t *image_source_properties(void *data) { @@ -268,6 +270,10 @@ static struct obs_source_info image_source_info = { OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("image-source", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Image/color/slideshow sources"; +} extern struct obs_source_info slideshow_info; extern struct obs_source_info color_source_info; diff --git a/plugins/image-source/obs-slideshow.c b/plugins/image-source/obs-slideshow.c index e8316e93f..f5b20ee29 100644 --- a/plugins/image-source/obs-slideshow.c +++ b/plugins/image-source/obs-slideshow.c @@ -57,6 +57,9 @@ /* ------------------------------------------------------------------------- */ +#define MOD(a,b) ((((a)%(b))+(b))%(b)) +#define MAX_LOADED 11 /* needs to be an odd number */ + struct image_file_data { char *path; obs_source_t *source; @@ -75,7 +78,6 @@ struct slideshow { bool loop; bool restart_on_activate; bool pause_on_deactivate; - bool restart; bool manual; bool hide; bool use_cut; @@ -87,13 +89,14 @@ struct slideshow { obs_source_t *transition; float elapsed; - size_t cur_item; + int cur_item; uint32_t cx; uint32_t cy; pthread_mutex_t mutex; DARRAY(struct image_file_data) files; + DARRAY(char*) paths; enum behavior behavior; @@ -164,7 +167,18 @@ static void free_files(struct darray *array) static inline size_t random_file(struct slideshow *ss) { - return (size_t)rand() % ss->files.num; + return (size_t)rand() % ss->paths.num; +} + +static void free_paths(struct darray *array) +{ + DARRAY(char*) paths; + paths.da = *array; + + for (size_t i = 0; i < paths.num; i++) + bfree(paths.array[i]); + + da_free(paths); } /* ------------------------------------------------------------------------- */ @@ -176,7 +190,7 @@ static const char *ss_getname(void *unused) } static void add_file(struct slideshow *ss, struct darray *array, - const char *path, uint32_t *cx, uint32_t *cy) + const char *path, uint32_t *cx, uint32_t *cy, bool next) { DARRAY(struct image_file_data) new_files; struct image_file_data data; @@ -199,7 +213,11 @@ static void add_file(struct slideshow *ss, struct darray *array, data.path = bstrdup(path); data.source = new_source; - da_push_back(new_files, &data); + + if (next) + da_push_back(new_files, &data); + else + da_insert(new_files, 0, &data); if (new_cx > *cx) *cx = new_cx; if (new_cy > *cy) *cy = new_cy; @@ -208,6 +226,48 @@ static void add_file(struct slideshow *ss, struct darray *array, *array = new_files.da; } +static void clear_buffer(struct slideshow *ss, bool next) +{ + if (ss->paths.num <= MAX_LOADED || !ss->paths.num || !ss->files.num) + return; + + if (next) { + bfree(ss->files.array[0].path); + obs_source_release(ss->files.array[0].source); + da_erase(ss->files, 0); + } else { + bfree(ss->files.array[ss->files.num - 1].path); + obs_source_release(ss->files.array[ss->files.num - 1].source); + da_pop_back(ss->files); + } + + size_t index = 0; + + if (ss->randomize) + index = random_file(ss); + else if (!ss->randomize && next) + index = MOD((ss->cur_item + ((MAX_LOADED / 2) + 1)), + ss->paths.num); + else if (!ss->randomize && !next) + index = MOD((ss->cur_item - ((MAX_LOADED / 2) + 1)), + ss->paths.num); + + uint32_t cx, cy; + add_file(ss, &ss->files.da, ss->paths.array[index], &cx, &cy, + next); +} + +static void add_path(struct darray *array, const char *path) +{ + DARRAY(char*) new_paths; + new_paths.da = *array; + + const char *new_path = bstrdup(path); + da_push_back(new_paths, &new_path); + + *array = new_paths.da; +} + static bool valid_extension(const char *ext) { if (!ext) @@ -222,35 +282,54 @@ static bool valid_extension(const char *ext) static inline bool item_valid(struct slideshow *ss) { - return ss->files.num && ss->cur_item < ss->files.num; + return ss->files.num && ss->paths.num && + (size_t)ss->cur_item < ss->paths.num; } -static void do_transition(void *data, bool to_null) +static void do_transition(void *data, bool to_null, bool next) { struct slideshow *ss = data; bool valid = item_valid(ss); - if (valid && ss->use_cut) - obs_transition_set(ss->transition, - ss->files.array[ss->cur_item].source); - - else if (valid && !to_null) - obs_transition_start(ss->transition, - OBS_TRANSITION_MODE_AUTO, + if (to_null) { + obs_transition_start(ss->transition, OBS_TRANSITION_MODE_AUTO, ss->tr_speed, - ss->files.array[ss->cur_item].source); + NULL); + return; + } - else + if (!valid) + return; + + clear_buffer(ss, next); + + obs_source_t *source; + + if (next && ss->paths.num > MAX_LOADED) + source = ss->files.array[(MAX_LOADED / 2) + 1].source; + else if (!next && ss->paths.num > MAX_LOADED) + source = ss->files.array[(MAX_LOADED / 2) - 1].source; + else if (ss->paths.num <= MAX_LOADED) + source = ss->files.array[(size_t)ss->cur_item].source; + + if (!source) + return; + + if (ss->use_cut) + obs_transition_set(ss->transition, source); + + else if (!to_null) obs_transition_start(ss->transition, OBS_TRANSITION_MODE_AUTO, - ss->tr_speed, - NULL); + ss->tr_speed, source); } static void ss_update(void *data, obs_data_t *settings) { DARRAY(struct image_file_data) new_files; DARRAY(struct image_file_data) old_files; + DARRAY(char*) new_paths; + DARRAY(char*) old_paths; obs_source_t *new_tr = NULL; obs_source_t *old_tr = NULL; struct slideshow *ss = data; @@ -260,6 +339,8 @@ static void ss_update(void *data, obs_data_t *settings) uint32_t new_speed; uint32_t cx = 0; uint32_t cy = 0; + uint32_t last_cx = 0; + uint32_t last_cy = 0; size_t count; const char *behavior; const char *mode; @@ -268,6 +349,7 @@ static void ss_update(void *data, obs_data_t *settings) /* get settings data */ da_init(new_files); + da_init(new_paths); behavior = obs_data_get_string(settings, S_BEHAVIOR); @@ -333,14 +415,13 @@ static void ss_update(void *data, obs_data_t *settings) dstr_copy(&dir_path, path); dstr_cat_ch(&dir_path, '/'); dstr_cat(&dir_path, ent->d_name); - add_file(ss, &new_files.da, dir_path.array, - &cx, &cy); + add_path(&new_paths.da, dir_path.array); } dstr_free(&dir_path); os_closedir(dir); } else { - add_file(ss, &new_files.da, path, &cx, &cy); + add_path(&new_paths.da, path); } obs_data_release(item); @@ -353,6 +434,8 @@ static void ss_update(void *data, obs_data_t *settings) old_files.da = ss->files.da; ss->files.da = new_files.da; + old_paths.da = ss->paths.da; + ss->paths.da = new_paths.da; if (new_tr) { old_tr = ss->transition; ss->transition = new_tr; @@ -369,12 +452,31 @@ static void ss_update(void *data, obs_data_t *settings) pthread_mutex_unlock(&ss->mutex); + if (ss->paths.num > MAX_LOADED && !ss->randomize) { + for (int i = -(MAX_LOADED / 2); i <= (MAX_LOADED / 2); i++) { + size_t index = MOD(i, ss->paths.num); + add_file(ss, &ss->files.da, ss->paths.array[index], + &cx, &cy, true); + } + } else if (ss->paths.num > MAX_LOADED && ss->randomize) { + for (size_t i = 0; i < MAX_LOADED; i++) { + size_t index = random_file(ss); + add_file(ss, &ss->files.da, ss->paths.array[index], + &cx, &cy, true); + } + } else if (ss->paths.num <= MAX_LOADED) { + for (size_t i = 0; i < ss->paths.num; i++) + add_file(ss, &ss->files.da, ss->paths.array[i], + &cx, &cy, true); + } + /* ------------------------------------- */ /* clean up and restart transition */ if (old_tr) obs_source_release(old_tr); free_files(&old_files.da); + free_paths(&old_paths.da); /* ------------------------- */ @@ -418,21 +520,32 @@ static void ss_update(void *data, obs_data_t *settings) /* ------------------------- */ + obs_data_t *priv_settings = obs_source_get_private_settings(ss->source); + last_cx = (uint32_t)obs_data_get_int(priv_settings, "last_cx"); + last_cy = (uint32_t)obs_data_get_int(priv_settings, "last_cy"); + + if (ss->randomize && last_cx > 0 && last_cy > 0) { + cx = last_cx; + cy = last_cy; + } + + obs_data_set_int(priv_settings, "last_cx", cx); + obs_data_set_int(priv_settings, "last_cy", cy); + obs_data_release(priv_settings); + ss->cx = cx; ss->cy = cy; - ss->cur_item = 0; ss->elapsed = 0.0f; + ss->cur_item = 0; obs_transition_set_size(ss->transition, cx, cy); obs_transition_set_alignment(ss->transition, OBS_ALIGN_CENTER); obs_transition_set_scale_type(ss->transition, OBS_TRANSITION_SCALE_ASPECT); - if (ss->randomize && ss->files.num) - ss->cur_item = random_file(ss); if (new_tr) obs_source_add_active_child(ss->source, new_tr); if (ss->files.num) - do_transition(ss, false); + do_transition(ss, false, true); obs_data_array_release(array); } @@ -449,14 +562,15 @@ static void ss_restart(void *data) { struct slideshow *ss = data; - ss->elapsed = 0.0f; - ss->cur_item = 0; + ss->stop = false; + ss->use_cut = true; + ss->restart_on_activate = false; - obs_transition_set(ss->transition, - ss->files.array[ss->cur_item].source); + obs_data_t *settings = obs_source_get_settings(ss->source); + ss_update(ss, settings); + obs_data_release(settings); - ss->stop = false; - ss->paused = false; + ss->use_cut = false; } static void ss_stop(void *data) @@ -466,7 +580,7 @@ static void ss_stop(void *data) ss->elapsed = 0.0f; ss->cur_item = 0; - do_transition(ss, true); + do_transition(ss, true, true); ss->stop = true; ss->paused = false; } @@ -475,28 +589,26 @@ static void ss_next_slide(void *data) { struct slideshow *ss = data; - if (!ss->files.num) + if (!ss->paths.num) return; - if (++ss->cur_item >= ss->files.num) + if (++ss->cur_item >= (int)ss->paths.num) ss->cur_item = 0; - do_transition(ss, false); + do_transition(ss, false, true); } static void ss_previous_slide(void *data) { struct slideshow *ss = data; - if (!ss->files.num) + if (!ss->paths.num) return; - if (ss->cur_item == 0) - ss->cur_item = ss->files.num - 1; - else - --ss->cur_item; + if (--ss->cur_item < 0) + ss->cur_item = (int)(ss->paths.num - 1); - do_transition(ss, false); + do_transition(ss, false, false); } static void play_pause_hotkey(void *data, obs_hotkey_id id, @@ -571,6 +683,7 @@ static void ss_destroy(void *data) obs_source_release(ss->transition); free_files(&ss->files.da); + free_paths(&ss->paths.da); pthread_mutex_destroy(&ss->mutex); bfree(ss); } @@ -644,13 +757,8 @@ static void ss_video_tick(void *data, float seconds) if (!ss->transition || !ss->slide_time) return; - if (ss->restart_on_activate && !ss->randomize && ss->use_cut) { - ss->elapsed = 0.0f; - ss->cur_item = 0; - do_transition(ss, false); - ss->restart_on_activate = false; - ss->use_cut = false; - ss->stop = false; + if (ss->restart_on_activate && ss->use_cut) { + ss_restart(ss); return; } @@ -659,13 +767,13 @@ static void ss_video_tick(void *data, float seconds) /* ----------------------------------------------------- */ /* fade to transparency when the file list becomes empty */ - if (!ss->files.num) { + if (!ss->paths.num) { obs_source_t* active_transition_source = obs_transition_get_active_source(ss->transition); if (active_transition_source) { obs_source_release(active_transition_source); - do_transition(ss, true); + do_transition(ss, true, true); } } @@ -676,29 +784,17 @@ static void ss_video_tick(void *data, float seconds) if (ss->elapsed > ss->slide_time) { ss->elapsed -= ss->slide_time; - if (!ss->loop && ss->cur_item == ss->files.num - 1) { + if (!ss->loop && ss->cur_item == (int)ss->paths.num - 1 && + !ss->randomize) { if (ss->hide) - do_transition(ss, true); + do_transition(ss, true, true); else - do_transition(ss, false); + do_transition(ss, false, true); return; } - if (ss->randomize) { - size_t next = ss->cur_item; - if (ss->files.num > 1) { - while (next == ss->cur_item) - next = random_file(ss); - } - ss->cur_item = next; - - } else if (++ss->cur_item >= ss->files.num) { - ss->cur_item = 0; - } - - if (ss->files.num) - do_transition(ss, false); + ss_next_slide(ss); } } @@ -861,11 +957,14 @@ static obs_properties_t *ss_properties(void *data) if (ss) { pthread_mutex_lock(&ss->mutex); - if (ss->files.num) { - struct image_file_data *last = da_end(ss->files); + if (ss->paths.num) { + char **p_last_path = da_end(ss->paths); + const char *last_path; const char *slash; - dstr_copy(&path, last->path); + last_path = p_last_path ? *p_last_path : ""; + + dstr_copy(&path, last_path); dstr_replace(&path, "\\", "/"); slash = strrchr(path.array, '/'); if (slash) diff --git a/plugins/linux-alsa/data/locale/fil-PH.ini b/plugins/linux-alsa/data/locale/fil-PH.ini new file mode 100644 index 000000000..fdc27af8b --- /dev/null +++ b/plugins/linux-alsa/data/locale/fil-PH.ini @@ -0,0 +1,3 @@ +AlsaInput="Audio Capture Device (ALSA)" +Device="Device" + diff --git a/plugins/linux-alsa/data/locale/gd-GB.ini b/plugins/linux-alsa/data/locale/gd-GB.ini new file mode 100644 index 000000000..4b3c24c05 --- /dev/null +++ b/plugins/linux-alsa/data/locale/gd-GB.ini @@ -0,0 +1,2 @@ +Device="Uidheam" + diff --git a/plugins/linux-alsa/data/locale/ka-GE.ini b/plugins/linux-alsa/data/locale/ka-GE.ini new file mode 100644 index 000000000..ca9bfeabc --- /dev/null +++ b/plugins/linux-alsa/data/locale/ka-GE.ini @@ -0,0 +1,3 @@ +AlsaInput="ხმის ჩამწერი მოწყობილობა (ALSA)" +Device="მოწყობილობა" + diff --git a/plugins/linux-alsa/data/locale/tl-PH.ini b/plugins/linux-alsa/data/locale/tl-PH.ini new file mode 100644 index 000000000..cce11aa12 --- /dev/null +++ b/plugins/linux-alsa/data/locale/tl-PH.ini @@ -0,0 +1,3 @@ +AlsaInput="Aparatong Pangkuha ng Audio (APA)" +Device="Device" + diff --git a/plugins/linux-alsa/linux-alsa.c b/plugins/linux-alsa/linux-alsa.c index b0cad0cf1..73635b126 100644 --- a/plugins/linux-alsa/linux-alsa.c +++ b/plugins/linux-alsa/linux-alsa.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-alsa", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Linux ALSA audio input capture"; +} extern struct obs_source_info alsa_input_capture; diff --git a/plugins/linux-capture/data/locale/fil-PH.ini b/plugins/linux-capture/data/locale/fil-PH.ini new file mode 100644 index 000000000..230e5da94 --- /dev/null +++ b/plugins/linux-capture/data/locale/fil-PH.ini @@ -0,0 +1,16 @@ +X11SharedMemoryScreenInput="Screen Capture (XSHM)" +Screen="Screen" +CaptureCursor="Kunin ang Cursor" +AdvancedSettings="Mga Advanced na Setting" +XServer="X Server" +XCCapture="Pagkuha ng Window (Xcomposite)" +Window="Window" +CropTop="I-crop ang Tuktok (mga pixel)" +CropLeft="I-crop ang Kaliwa (mga pixel)" +CropRight="I-crop ang Kanan (mga pixel)" +CropBottom="I-crop ang Ika (pixels)" +SwapRedBlue="Magpalit ng pula at asul" +LockX="Lock X server kapag nakukuha" +IncludeXBorder="Isama ang X Border" +ExcludeAlpha="Gumamit ng alpha-less texture na format (Mesa workaround)" + diff --git a/plugins/linux-capture/data/locale/gd-GB.ini b/plugins/linux-capture/data/locale/gd-GB.ini new file mode 100644 index 000000000..67871f384 --- /dev/null +++ b/plugins/linux-capture/data/locale/gd-GB.ini @@ -0,0 +1,16 @@ +X11SharedMemoryScreenInput="Glacadh-sgrìn (XSHM)" +Screen="Sgrìn" +CaptureCursor="Glac an cùrsair" +AdvancedSettings="Roghainnean adhartach" +XServer="Frithealaiche X" +XCCapture="Glacadh uinneige (Xcomposite)" +Window="Uinneag" +CropTop="Bearr aig a’ bharr (piogsail)" +CropLeft="Bearr aig an taobh chlì (piogsail)" +CropRight="Bearr aig an taobh deas (piogsail)" +CropBottom="Bearr aig a’ bhonn (piogsail)" +SwapRedBlue="Suaip dearg is gorm" +LockX="Glais am frithealaiche X rè a’ ghlacaidh" +IncludeXBorder="Gabh a-steach an t-iomall X" +ExcludeAlpha="Cleachd fòrmat innich gun alpha (seachnadh buga Mesa)" + diff --git a/plugins/linux-capture/data/locale/ka-GE.ini b/plugins/linux-capture/data/locale/ka-GE.ini new file mode 100644 index 000000000..7739d3548 --- /dev/null +++ b/plugins/linux-capture/data/locale/ka-GE.ini @@ -0,0 +1,16 @@ +X11SharedMemoryScreenInput="ეკრანის გადაღება (XSHM)" +Screen="ეკრანი" +CaptureCursor="მაჩვენებლის ასახვა" +AdvancedSettings="დამატებითი პარამეტრები" +XServer="X სერვერი" +XCCapture="ფანჯრის გადაღება (Xcomposite)" +Window="ფანჯარა" +CropTop="ზემოდან მოჭრა (პიქსელი)" +CropLeft="მარცხნიდან მოჭრა (პიქსელი)" +CropRight="მარჯვნიდან მოჭრა (პიქსელი)" +CropBottom="ქვემოდან მოჭრა (პიქსელი)" +SwapRedBlue="წითლისა ლურჯის შენაცვლება" +LockX="X სერვერის შეზღუდვა გადაღებისას" +IncludeXBorder="X ჩარჩოიანად" +ExcludeAlpha="გაუმჭვირვალე ზედაპირის გამოყენება (Mesa-ს ხარვეზის მოსაგვარებლად)" + diff --git a/plugins/linux-capture/data/locale/tl-PH.ini b/plugins/linux-capture/data/locale/tl-PH.ini new file mode 100644 index 000000000..bae7f5f6e --- /dev/null +++ b/plugins/linux-capture/data/locale/tl-PH.ini @@ -0,0 +1,16 @@ +X11SharedMemoryScreenInput="Pamagat ng Screen (XSHM)" +Screen="Ang Screen" +CaptureCursor="Pamagat ng kursor" +AdvancedSettings="Ang mga nauunang setting" +XServer="X tagapagsilbi" +XCCapture="Pamagat ng Bintana (Xpinaghalo)" +Window="Bintana" +CropTop="I-crop ang Top (mga pixel)" +CropLeft="I-crop ang kaliwa (mga pixel)" +CropRight="I-crop ang kanan (mga pixel)" +CropBottom="I-crop ang pinakailalim (mga pixel)" +SwapRedBlue="Pagpalitin ang pula at ang asul" +LockX="Kandado sa X server kapag kukuha" +IncludeXBorder="Kasama sa X na hangganan" +ExcludeAlpha="Gumamit ng alpha-less texture na format (Mesa workaround)" + diff --git a/plugins/linux-capture/linux-capture.c b/plugins/linux-capture/linux-capture.c index ad95463ce..ce49ee72e 100644 --- a/plugins/linux-capture/linux-capture.c +++ b/plugins/linux-capture/linux-capture.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-xshm", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "xcomposite/xshm based window/screen capture for X11"; +} extern struct obs_source_info xshm_input; diff --git a/plugins/linux-capture/xcompcap-main.cpp b/plugins/linux-capture/xcompcap-main.cpp index 0306b68b1..df78e3dcc 100644 --- a/plugins/linux-capture/xcompcap-main.cpp +++ b/plugins/linux-capture/xcompcap-main.cpp @@ -148,6 +148,7 @@ struct XCompcapMain_private bool lockX; bool include_border; bool exclude_alpha; + bool draw_opaque; double window_check_time = 0.0; @@ -301,6 +302,7 @@ void XCompcapMain::updateSettings(obs_data_t *settings) p->show_cursor = obs_data_get_bool(settings, "show_cursor"); p->include_border = obs_data_get_bool(settings, "include_border"); p->exclude_alpha = obs_data_get_bool(settings, "exclude_alpha"); + p->draw_opaque = false; } else { p->win = prevWin; } @@ -347,6 +349,20 @@ void XCompcapMain::updateSettings(obs_data_t *settings) cf = GS_BGRX; } + bool has_alpha = true; + + if (attr.depth < 32) { + cf = GS_BGRX; + has_alpha = false; + } + + if (cf == GS_BGRX) { + if (settings) { + p->swapRedBlue = !p->swapRedBlue; + } + p->draw_opaque = true; + } + p->border = attr.border_width; if (p->include_border) { @@ -395,15 +411,25 @@ void XCompcapMain::updateSettings(obs_data_t *settings) glBindTexture(GL_TEXTURE_2D, 0); } - const int attrs[] = + const int attrs_alpha[] = { GLX_BIND_TO_TEXTURE_RGBA_EXT, GL_TRUE, GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, - GLX_DOUBLEBUFFER, GL_FALSE, + GLX_ALPHA_SIZE, 8, + None + }; + + const int attrs_no_alpha[] = + { + GLX_BIND_TO_TEXTURE_RGB_EXT, GL_TRUE, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, None }; + const int *attrs = has_alpha ? attrs_alpha : attrs_no_alpha; + int nelem = 0; GLXFBConfig* configs = glXChooseFBConfig(xdisp, XCompcap::getRootWindowScreen(attr.root), @@ -417,8 +443,27 @@ void XCompcapMain::updateSettings(obs_data_t *settings) return; } - glXGetFBConfigAttrib(xdisp, configs[0], GLX_Y_INVERTED_EXT, &nelem); - p->inverted = nelem != 0; + GLXFBConfig config; + bool found = false; + for (int i = 0; i < nelem; i++) { + config = configs[i]; + XVisualInfo *visual = glXGetVisualFromFBConfig(xdisp, config); + if (attr.visual->visualid == visual->visualid) { + found = true; + XFree(visual); + break; + } + XFree(visual); + } + + if (!found) { + config = configs[0]; + p->draw_opaque = true; + } + + int inverted; + glXGetFBConfigAttrib(xdisp, config, GLX_Y_INVERTED_EXT, &inverted); + p->inverted = inverted != 0; xlock.resetError(); @@ -432,14 +477,23 @@ void XCompcapMain::updateSettings(obs_data_t *settings) return; } - const int attribs[] = + const int attribs_alpha[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, None }; - p->glxpixmap = glXCreatePixmap(xdisp, configs[0], p->pixmap, attribs); + const int attribs_no_alpha[] = + { + GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT, + None + }; + + const int *attribs = cf == GS_RGBA ? attribs_alpha : attribs_no_alpha; + + p->glxpixmap = glXCreatePixmap(xdisp, config, p->pixmap, attribs); if (xlock.gotError()) { blog(LOG_ERROR, "glXCreatePixmap failed: %s", @@ -465,10 +519,14 @@ void XCompcapMain::updateSettings(obs_data_t *settings) if (!p->windowName.empty()) { blog(LOG_INFO, "[window-capture: '%s'] update settings:\n" "\ttitle: %s\n" - "\tclass: %s", + "\tclass: %s\n" + "\tHas alpha: %s\n" + "\tFound exact GLXFBConfig: %s", obs_source_get_name(p->source), XCompcap::getWindowName(p->win).c_str(), - XCompcap::getWindowClass(p->win).c_str()); + XCompcap::getWindowClass(p->win).c_str(), + has_alpha ? "yes" : "no", + found ? "yes" : "no"); blog(LOG_DEBUG, "\n" "\tid: %s", std::to_string((long long)p->win).c_str()); @@ -562,7 +620,10 @@ void XCompcapMain::render(gs_effect_t *effect) PLock lock(&p->lock, true); - effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); + if (p->draw_opaque) + effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); + else + effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); if (!lock.isLocked() || !p->tex) return; diff --git a/plugins/linux-jack/data/locale/fil-PH.ini b/plugins/linux-jack/data/locale/fil-PH.ini new file mode 100644 index 000000000..ed2309a8d --- /dev/null +++ b/plugins/linux-jack/data/locale/fil-PH.ini @@ -0,0 +1,4 @@ +StartJACKServer="Simulan ang JACK Server" +Channels="Bilang ng Mga Channel" +JACKInput="JACK Input Client" + diff --git a/plugins/linux-jack/data/locale/ka-GE.ini b/plugins/linux-jack/data/locale/ka-GE.ini new file mode 100644 index 000000000..247377506 --- /dev/null +++ b/plugins/linux-jack/data/locale/ka-GE.ini @@ -0,0 +1,4 @@ +StartJACKServer="JACK სერვერის გაშვება" +Channels="არხების რაოდენობა" +JACKInput="JACK შეტანის კლიენტი" + diff --git a/plugins/linux-jack/jack-wrapper.c b/plugins/linux-jack/jack-wrapper.c index 63309cc9b..98fa132d8 100644 --- a/plugins/linux-jack/jack-wrapper.c +++ b/plugins/linux-jack/jack-wrapper.c @@ -103,7 +103,7 @@ int_fast32_t jack_init(struct jack_data* data) sizeof(jack_port_t*) * data->channels); for (unsigned int i = 0; i < data->channels; ++i) { char port_name[10] = {'\0'}; - snprintf(port_name, sizeof(port_name), "in_%d", i+1); + snprintf(port_name, sizeof(port_name), "in_%u", i+1); data->jack_ports[i] = jack_port_register(data->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); diff --git a/plugins/linux-jack/linux-jack.c b/plugins/linux-jack/linux-jack.c index 349a20584..be6721da4 100644 --- a/plugins/linux-jack/linux-jack.c +++ b/plugins/linux-jack/linux-jack.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-jack", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "JACK Audio Connection Kit output capture"; +} extern struct obs_source_info jack_output_capture; diff --git a/plugins/linux-pulseaudio/data/locale/fil-PH.ini b/plugins/linux-pulseaudio/data/locale/fil-PH.ini new file mode 100644 index 000000000..029c9911a --- /dev/null +++ b/plugins/linux-pulseaudio/data/locale/fil-PH.ini @@ -0,0 +1,4 @@ +PulseInput="Audio Input Capture (PulseAudio)" +PulseOutput="Audio Output Capture (PulseAudio)" +Device="Device" + diff --git a/plugins/linux-pulseaudio/data/locale/gd-GB.ini b/plugins/linux-pulseaudio/data/locale/gd-GB.ini new file mode 100644 index 000000000..3a2f3e954 --- /dev/null +++ b/plugins/linux-pulseaudio/data/locale/gd-GB.ini @@ -0,0 +1,4 @@ +PulseInput="Glacadh ion-chur fuaime (PulseAudio)" +PulseOutput="Glacadh às-chur fuaime (PulseAudio)" +Device="Uidheam" + diff --git a/plugins/linux-pulseaudio/data/locale/ka-GE.ini b/plugins/linux-pulseaudio/data/locale/ka-GE.ini new file mode 100644 index 000000000..78a045394 --- /dev/null +++ b/plugins/linux-pulseaudio/data/locale/ka-GE.ini @@ -0,0 +1,4 @@ +PulseInput="შემავალი ხმოვანი სიგნალის ჩაწერა (PulseAudio)" +PulseOutput="გამომავალი ხმოვანი სიგნალის ჩაწერა (PulseAudio)" +Device="მოწყობილობა" + diff --git a/plugins/linux-pulseaudio/data/locale/pt-BR.ini b/plugins/linux-pulseaudio/data/locale/pt-BR.ini index be20b6078..b67b8f02a 100644 --- a/plugins/linux-pulseaudio/data/locale/pt-BR.ini +++ b/plugins/linux-pulseaudio/data/locale/pt-BR.ini @@ -1,4 +1,4 @@ PulseInput="Dispositivo de Entrada de Áudio (PulseAudio)" -PulseOutput="Dispotitivo de Saída de Áudio (PulseAudio)" -Device="Dispotivo" +PulseOutput="Dispositivo de Saída de Áudio (PulseAudio)" +Device="Dispositivo" diff --git a/plugins/linux-pulseaudio/linux-pulseaudio.c b/plugins/linux-pulseaudio/linux-pulseaudio.c index acfd10ea0..78f066e0e 100644 --- a/plugins/linux-pulseaudio/linux-pulseaudio.c +++ b/plugins/linux-pulseaudio/linux-pulseaudio.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-pulseaudio", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Linux PulseAudio input/output capture"; +} extern struct obs_source_info pulse_input_capture; extern struct obs_source_info pulse_output_capture; diff --git a/plugins/linux-v4l2/data/locale/fil-PH.ini b/plugins/linux-v4l2/data/locale/fil-PH.ini new file mode 100644 index 000000000..c5d046620 --- /dev/null +++ b/plugins/linux-v4l2/data/locale/fil-PH.ini @@ -0,0 +1,11 @@ +V4L2Input="Video Capture Device (V4L2)" +Device="Device" +Input="Input" +VideoFormat="Format ng Video" +VideoStandard="Standard Video" +DVTiming="DV Timing" +Resolution="Resolution" +FrameRate="Frame rate" +LeaveUnchanged="Mag-iwan ng hindi nabago" +UseBuffering="Gamitin ang Buffering" + diff --git a/plugins/linux-v4l2/data/locale/gd-GB.ini b/plugins/linux-v4l2/data/locale/gd-GB.ini new file mode 100644 index 000000000..78a1c7023 --- /dev/null +++ b/plugins/linux-v4l2/data/locale/gd-GB.ini @@ -0,0 +1,11 @@ +V4L2Input="Uidheam glacadh video (V4L2)" +Device="Uidheam" +Input="Ion-chur" +VideoFormat="Fòrmat a’ video" +VideoStandard="Stannard a’ video" +DVTiming="Co-thìmeachadh DV" +Resolution="Dùmhlachd-bhreacaidh" +FrameRate="Reat fhrèamaichean" +LeaveUnchanged="Cum gun atharrachadh" +UseBuffering="Cleachd bufaireadh" + diff --git a/plugins/linux-v4l2/data/locale/ka-GE.ini b/plugins/linux-v4l2/data/locale/ka-GE.ini new file mode 100644 index 000000000..4ede749c5 --- /dev/null +++ b/plugins/linux-v4l2/data/locale/ka-GE.ini @@ -0,0 +1,11 @@ +V4L2Input="ვიდეოს ჩამწერი მოწყობილობა (V4L2)" +Device="მოწყობილობა" +Input="შემავალი" +VideoFormat="ვიდეოს ფორმატი" +VideoStandard="ვიდეოს სტანდარტი" +DVTiming="ციფრული ვიდეოს სინქრონიზაცია" +Resolution="გარჩევადობა" +FrameRate="კადრის სიხშირე" +LeaveUnchanged="უცვლელად დატოვება" +UseBuffering="ბუფერიზაციის გამოყენება" + diff --git a/plugins/linux-v4l2/data/locale/tl-PH.ini b/plugins/linux-v4l2/data/locale/tl-PH.ini new file mode 100644 index 000000000..27e63d622 --- /dev/null +++ b/plugins/linux-v4l2/data/locale/tl-PH.ini @@ -0,0 +1,11 @@ +V4L2Input="Pagkuha ng Video sa Aparato (V4L2)" +Device="Aparato" +Input="Pampasok" +VideoFormat="Ang Format ng Video" +VideoStandard="Ang Pamantayan ng Video" +DVTiming="Ang Tiyempong DV" +Resolution="Resulusyon" +FrameRate="Pag-Rate ng Frame" +LeaveUnchanged="Iniwa ng hindi nabago" +UseBuffering="Gamitin ang Buffering" + diff --git a/plugins/linux-v4l2/linux-v4l2.c b/plugins/linux-v4l2/linux-v4l2.c index 287e10ccf..b57deaa34 100644 --- a/plugins/linux-v4l2/linux-v4l2.c +++ b/plugins/linux-v4l2/linux-v4l2.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-v4l2", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Video4Linux2(V4L2) sources"; +} extern struct obs_source_info v4l2_input; diff --git a/plugins/mac-avcapture/av-capture.mm b/plugins/mac-avcapture/av-capture.mm index 2e2a59148..446ba910d 100644 --- a/plugins/mac-avcapture/av-capture.mm +++ b/plugins/mac-avcapture/av-capture.mm @@ -2175,6 +2175,10 @@ static void av_capture_update(void *data, obs_data_t *settings) OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("mac-avcapture", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "MacOS AVFoundation Capture source"; +} bool obs_module_load(void) { diff --git a/plugins/mac-avcapture/data/locale/fil-PH.ini b/plugins/mac-avcapture/data/locale/fil-PH.ini new file mode 100644 index 000000000..c9e6bb54a --- /dev/null +++ b/plugins/mac-avcapture/data/locale/fil-PH.ini @@ -0,0 +1,14 @@ +AVCapture="Video Capture aparato" +Device="Aparato" +UsePreset="Gamitin ang Preset" +Preset="Ang Preset" +Buffering="Gamitin ang Buffering" +FrameRate="Rate ng frame" +InputFormat="Format ng input" +ColorSpace="Kulay ng puwang" +VideoRange="Saklaw ng video" +VideoRange.Partial="Bahagyang" +VideoRange.Full="Buong" +Auto="I-Auto" +Unknown="Hindi kilalang ($1)" + diff --git a/plugins/mac-avcapture/data/locale/gd-GB.ini b/plugins/mac-avcapture/data/locale/gd-GB.ini new file mode 100644 index 000000000..ae9918b61 --- /dev/null +++ b/plugins/mac-avcapture/data/locale/gd-GB.ini @@ -0,0 +1,14 @@ +AVCapture="Uidheam glacadh video" +Device="Uidheam" +UsePreset="Cleachd ro-sheata" +Preset="Ro-sheata" +Buffering="Cleachd bufaireadh" +FrameRate="Reat fhrèamaichean" +InputFormat="Fòrmat an ion-chuir" +ColorSpace="Spàs dhathan" +VideoRange="Rainse a’ video" +VideoRange.Partial="Leth-phàirteach" +VideoRange.Full="Làn" +Auto="Fèin-obrachail" +Unknown="Chan eil fhios ($1)" + diff --git a/plugins/mac-avcapture/data/locale/ka-GE.ini b/plugins/mac-avcapture/data/locale/ka-GE.ini new file mode 100644 index 000000000..d00fdbd79 --- /dev/null +++ b/plugins/mac-avcapture/data/locale/ka-GE.ini @@ -0,0 +1,14 @@ +AVCapture="ვიდეოს ჩამწერი მოწყობილობა" +Device="მოწყობილობა" +UsePreset="მზა პარამეტრებით სარგებლობა" +Preset="მზა პარამეტრები" +Buffering="ბუფერიზაციის გამოყენება" +FrameRate="კადრის სიხშირე" +InputFormat="შეტანის ფორმატი" +ColorSpace="ფერთა სისტემა" +VideoRange="ვიდეოების არე" +VideoRange.Partial="ნაწილობრივი" +VideoRange.Full="სრული" +Auto="ავტომატური" +Unknown="უცნობი ($1)" + diff --git a/plugins/mac-avcapture/data/locale/tl-PH.ini b/plugins/mac-avcapture/data/locale/tl-PH.ini new file mode 100644 index 000000000..730004469 --- /dev/null +++ b/plugins/mac-avcapture/data/locale/tl-PH.ini @@ -0,0 +1,14 @@ +AVCapture="Pagkuha ng Aparato ng Video" +Device="Aparato" +UsePreset="Gamitin ang Preset" +Preset="Ang Preset" +Buffering="Gamitin ang Buffering" +FrameRate="Pag-rate ng Frame" +InputFormat="Pampasok na format" +ColorSpace="Pagitan na Kulay" +VideoRange="Saklaw na Video" +VideoRange.Partial="Ang Partial" +VideoRange.Full="Puno" +Auto="Awto" +Unknown="Hindi kilalang ($1)" + diff --git a/plugins/mac-capture/CMakeLists.txt b/plugins/mac-capture/CMakeLists.txt index 6e7b2ebd1..50e34f69c 100644 --- a/plugins/mac-capture/CMakeLists.txt +++ b/plugins/mac-capture/CMakeLists.txt @@ -14,7 +14,6 @@ include_directories(${COREAUDIO} set(mac-capture_HEADERS audio-device-enum.h - mac-helpers.h window-utils.h) set(mac-capture_SOURCES diff --git a/plugins/mac-capture/audio-device-enum.c b/plugins/mac-capture/audio-device-enum.c index bf7de8d1b..5a1d7b738 100644 --- a/plugins/mac-capture/audio-device-enum.c +++ b/plugins/mac-capture/audio-device-enum.c @@ -1,7 +1,8 @@ #include #include -#include "mac-helpers.h" +#include + #include "audio-device-enum.h" /* ugh, because mac has no means of capturing output, we have to basically @@ -114,9 +115,9 @@ static bool coreaudio_enum_add_device(void *param, CFStringRef cf_name, memset(&item, 0, sizeof(item)); - if (!cf_to_dstr(cf_name, &item.name)) + if (!cfstr_copy_dstr(cf_name, kCFStringEncodingUTF8, &item.name)) goto fail; - if (!cf_to_dstr(cf_uid, &item.value)) + if (!cfstr_copy_dstr(cf_uid, kCFStringEncodingUTF8, &item.value)) goto fail; if (data->input || !device_is_input(item.value.array)) diff --git a/plugins/mac-capture/data/locale/fil-PH.ini b/plugins/mac-capture/data/locale/fil-PH.ini new file mode 100644 index 000000000..95a732a32 --- /dev/null +++ b/plugins/mac-capture/data/locale/fil-PH.ini @@ -0,0 +1,21 @@ +CoreAudio.InputCapture="Awdyo Input KaptyuraPag-capture ng Audio Input" +CoreAudio.OutputCapture="Awdyo Awtput Kaptyura" +CoreAudio.Device="Aparato" +CoreAudio.Device.Default="Pumalya" +DisplayCapture="Ipakita paghuli" +DisplayCapture.Display="Ihayag" +DisplayCapture.ShowCursor="Ipakita ang Cursor" +WindowCapture="Pagkuha ng Window" +WindowCapture.ShowShadow="Ipakita ang anino ng Window" +WindowUtils.Window="Window" +WindowUtils.ShowEmptyNames="Ipakita ang Windows gamit ang mga walang laman na pangalan" +CropMode="I-crop" +CropMode.None="Wala" +CropMode.Manual="Mano-manong" +CropMode.ToWindow="Sa Window" +CropMode.ToWindowAndManual="Upang Window at Manu-manong" +Crop.origin.x="I-crop ang kaliwa" +Crop.origin.y="I-crop ang tuktok" +Crop.size.width="I-crop ang kanan" +Crop.size.height="I-crop ang ibaba" + diff --git a/plugins/mac-capture/data/locale/gd-GB.ini b/plugins/mac-capture/data/locale/gd-GB.ini new file mode 100644 index 000000000..b4cf88f8d --- /dev/null +++ b/plugins/mac-capture/data/locale/gd-GB.ini @@ -0,0 +1,21 @@ +CoreAudio.InputCapture="Glacadh ion-chur fuaime" +CoreAudio.OutputCapture="Glacadh às-chur fuaime" +CoreAudio.Device="Uidheam" +CoreAudio.Device.Default="Bun-roghainn" +DisplayCapture="Glacadh uidheim-taisbeanaidh" +DisplayCapture.Display="Uidheam-taisbeanaidh" +DisplayCapture.ShowCursor="Seall an cùrsair" +WindowCapture="Glacadh uinneige" +WindowCapture.ShowShadow="Seall sgàil na h-uinneige" +WindowUtils.Window="Uinneag" +WindowUtils.ShowEmptyNames="Seall na h-uinneagan le ainmean falamh" +CropMode="Bearr" +CropMode.None="Chan eil gin" +CropMode.Manual="A làimh" +CropMode.ToWindow="Dhan uinneag" +CropMode.ToWindowAndManual="Dhan uinneag is a làimh" +Crop.origin.x="Bearr aig an taobh chlì" +Crop.origin.y="Bearr aig a’ bharr" +Crop.size.width="Bearr aig an taobh deas" +Crop.size.height="Bearr aig a’ bhonn" + diff --git a/plugins/mac-capture/data/locale/ka-GE.ini b/plugins/mac-capture/data/locale/ka-GE.ini new file mode 100644 index 000000000..0f8b3eb27 --- /dev/null +++ b/plugins/mac-capture/data/locale/ka-GE.ini @@ -0,0 +1,21 @@ +CoreAudio.InputCapture="შემავალი ხმოვანი სიგნალის ჩაწერა" +CoreAudio.OutputCapture="გამომავალი ხმოვანი სიგნალის ჩაწერა" +CoreAudio.Device="მოწყობილობა" +CoreAudio.Device.Default="ნაგულისხმევი" +DisplayCapture="ეკრანის გადაღება" +DisplayCapture.Display="ეკრანი" +DisplayCapture.ShowCursor="მაჩვენებლის გამოჩენა" +WindowCapture="ფანჯრის გადაღება" +WindowCapture.ShowShadow="ფანჯრის ჩრდილის ჩვენება" +WindowUtils.Window="ფანჯარა" +WindowUtils.ShowEmptyNames="ფანჯრის ჩვენება ცარიელი სახელებით" +CropMode="შემოჭრა" +CropMode.None="არცერთი" +CropMode.Manual="ხელით" +CropMode.ToWindow="ფანჯარაზე" +CropMode.ToWindowAndManual="ფანჯარაზე ხელით" +Crop.origin.x="მარცხნივ შემოჭრა" +Crop.origin.y="ზემოთ შემოჭრა" +Crop.size.width="მარჯვნივ შემოჭრა" +Crop.size.height="ქვემოთ შემოჭრა" + diff --git a/plugins/mac-capture/data/locale/tl-PH.ini b/plugins/mac-capture/data/locale/tl-PH.ini new file mode 100644 index 000000000..11aa78ea0 --- /dev/null +++ b/plugins/mac-capture/data/locale/tl-PH.ini @@ -0,0 +1,21 @@ +CoreAudio.InputCapture="Pampasok na Pagkuha ng Audio" +CoreAudio.OutputCapture="Panglabas na pagkuha ng Audio" +CoreAudio.Device="Aparato" +CoreAudio.Device.Default="I-default" +DisplayCapture="Ipakita ang nakuha" +DisplayCapture.Display="Ipakita" +DisplayCapture.ShowCursor="Ipakita ang Kursor" +WindowCapture="Nakuhang bintana" +WindowCapture.ShowShadow="Ipakita ang Anino ng Bintana" +WindowUtils.Window="Bintana" +WindowUtils.ShowEmptyNames="Ipakita ang bintana na may mga walang laman na pangalan" +CropMode="I-crop" +CropMode.None="Wala" +CropMode.Manual="Mano-mano" +CropMode.ToWindow="Sa Bintana" +CropMode.ToWindowAndManual="Sa Bintana at mano-mano" +Crop.origin.x="I-crop sa kaliwa" +Crop.origin.y="I-crop sa taas" +Crop.size.width="I-crop sa kanan" +Crop.size.height="I-crop sa pinakailalim" + diff --git a/plugins/mac-capture/mac-audio.c b/plugins/mac-capture/mac-audio.c index 368480841..7d94c64f1 100644 --- a/plugins/mac-capture/mac-audio.c +++ b/plugins/mac-capture/mac-audio.c @@ -7,8 +7,8 @@ #include #include #include +#include -#include "mac-helpers.h" #include "audio-device-enum.h" #define PROPERTY_DEFAULT_DEVICE kAudioHardwarePropertyDefaultInputDevice @@ -89,6 +89,9 @@ static bool find_device_id_by_uid(struct coreaudio_data *ca) if (!ca->device_uid) ca->device_uid = bstrdup("default"); + ca->default_device = false; + ca->no_devices = false; + /* have to do this because mac output devices don't actually exist */ if (astrcmpi(ca->device_uid, "default") == 0) { if (ca->input) { @@ -493,7 +496,7 @@ static bool coreaudio_get_device_name(struct coreaudio_data *ca) { CFStringRef cf_name = NULL; UInt32 size = sizeof(CFStringRef); - char name[1024]; + char *name = NULL; const AudioObjectPropertyAddress addr = { kAudioDevicePropertyDeviceNameCFString, @@ -509,14 +512,15 @@ static bool coreaudio_get_device_name(struct coreaudio_data *ca) return false; } - if (!cf_to_cstr(cf_name, name, 1024)) { + name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8); + if (!name) { blog(LOG_WARNING, "[coreaudio_get_device_name] failed to " "convert name to cstr for some reason"); return false; } bfree(ca->device_name); - ca->device_name = bstrdup(name); + ca->device_name = name; if (cf_name) CFRelease(cf_name); diff --git a/plugins/mac-capture/mac-helpers.h b/plugins/mac-capture/mac-helpers.h deleted file mode 100644 index e77b45050..000000000 --- a/plugins/mac-capture/mac-helpers.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -static inline bool mac_success(OSStatus stat, const char *action) -{ - if (stat != noErr) { - blog(LOG_WARNING, "%s failed: %d", action, (int)stat); - return false; - } - - return true; -} - -static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size) -{ - if (!ref) return false; - return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8); -} - -static inline bool cf_to_dstr(CFStringRef ref, struct dstr *str) -{ - size_t size; - if (!ref) return false; - - size = (size_t)CFStringGetLength(ref); - if (!size) - return false; - - dstr_resize(str, size); - - return (bool)CFStringGetCString(ref, str->array, size+1, - kCFStringEncodingUTF8); -} diff --git a/plugins/mac-capture/plugin-main.c b/plugins/mac-capture/plugin-main.c index 4f6698dcb..2efa3a074 100644 --- a/plugins/mac-capture/plugin-main.c +++ b/plugins/mac-capture/plugin-main.c @@ -2,6 +2,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("mac-capture", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "macOS audio input/output and window/display capture"; +} extern struct obs_source_info coreaudio_input_capture_info; extern struct obs_source_info coreaudio_output_capture_info; diff --git a/plugins/mac-syphon/data/locale/fil-PH.ini b/plugins/mac-syphon/data/locale/fil-PH.ini new file mode 100644 index 000000000..18c86327f --- /dev/null +++ b/plugins/mac-syphon/data/locale/fil-PH.ini @@ -0,0 +1,13 @@ +Syphon="Pagkuha ng Laro (Siphon)" +Source="Pinagkukunan" +LaunchSyphonInject="Simulan ang SayponIngekt" +Inject="Turukan" +Application="Aplikasyon" +SyphonLicense="Ipahigod ang Lisensya" +Crop="I-crop" +Crop.origin.x="Gupitin ng maigsi sa kaliwa" +Crop.origin.y="Gupitin ng maigsi sa taas" +Crop.size.width="Gupitin ng maigsi sa kanan" +Crop.size.height="Gupitin ng maigsi sa ibaba" +AllowTransparency="Pahintulutan ang aninaw" + diff --git a/plugins/mac-syphon/data/locale/gd-GB.ini b/plugins/mac-syphon/data/locale/gd-GB.ini new file mode 100644 index 000000000..cae49db63 --- /dev/null +++ b/plugins/mac-syphon/data/locale/gd-GB.ini @@ -0,0 +1,9 @@ +Source="Tùs" +Application="Aplacaid" +Crop="Bearr" +Crop.origin.x="Bearr aig an taobh chlì" +Crop.origin.y="Bearr aig a’ bharr" +Crop.size.width="Bearr aig an taobh deas" +Crop.size.height="Bearr aig a’ bhonn" +AllowTransparency="Ceadaich trìd-shoillearachd" + diff --git a/plugins/mac-syphon/data/locale/ka-GE.ini b/plugins/mac-syphon/data/locale/ka-GE.ini new file mode 100644 index 000000000..f35959387 --- /dev/null +++ b/plugins/mac-syphon/data/locale/ka-GE.ini @@ -0,0 +1,13 @@ +Syphon="თამაშის გადაღება (Syphon)" +Source="წყარო" +LaunchSyphonInject="SyphonInject გაშვება" +Inject="ჩადგმა" +Application="პროგრამა" +SyphonLicense="Syphon ლიცენზია" +Crop="შემოჭრა" +Crop.origin.x="მარცხნივ შემოჭრა" +Crop.origin.y="ზემოთ შემოჭრა" +Crop.size.width="მარჯვნივ შემოჭრა" +Crop.size.height="ქვემოთ შემოჭრა" +AllowTransparency="გამჭვირვალობის დაშვება" + diff --git a/plugins/mac-syphon/plugin-main.c b/plugins/mac-syphon/plugin-main.c index 011e711f8..a8cde9995 100644 --- a/plugins/mac-syphon/plugin-main.c +++ b/plugins/mac-syphon/plugin-main.c @@ -2,6 +2,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("syphon", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Syphon based game capture for macOS"; +} extern struct obs_source_info syphon_info; diff --git a/plugins/mac-vth264/data/locale/de-DE.ini b/plugins/mac-vth264/data/locale/de-DE.ini index b391d6201..b095bd316 100644 --- a/plugins/mac-vth264/data/locale/de-DE.ini +++ b/plugins/mac-vth264/data/locale/de-DE.ini @@ -4,7 +4,7 @@ VTEncoder="VideoToolbox Codierer" Bitrate="Bitrate" UseMaxBitrate="Limitiere Bitrate" MaxBitrate="Maximale Bitrate" -MaxBitrateWindow="Maximale Bitrate Fenster (Sekunden)" +MaxBitrateWindow="Maximales Bitrate Fenster (Sekunden)" KeyframeIntervalSec="Keyframeintervall (Sekunden, 0=auto)" Profile="Profil" None="(Nichts)" diff --git a/plugins/mac-vth264/data/locale/fil-PH.ini b/plugins/mac-vth264/data/locale/fil-PH.ini new file mode 100644 index 000000000..234a7ed88 --- /dev/null +++ b/plugins/mac-vth264/data/locale/fil-PH.ini @@ -0,0 +1,14 @@ +VTH264EncHW="Apol VT H264 Softwer Enkoder" +VTH264EncSW="Apol VT H264 Softwer Enkoder" +VTEncoder="Bidyo ng Kahon ng kasangkapan Enkoder" +Bitrate="Bitreyt" +UseMaxBitrate="Limitasyon ng bitreyt" +MaxBitrate="Pinakamataas na bitreyt" +MaxBitrateWindow="Pinakamataas na bitreyt window (segundo)" +KeyframeIntervalSec="Ang Agwat ng Keypreym (segundo, 0=awto)" +Profile="Propayl" +None="(Wala)" +DefaultEncoder="(Depolt Enkoder)" +UseBFrames="Gamitin ang B-Preyms" + + diff --git a/plugins/mac-vth264/data/locale/gd-GB.ini b/plugins/mac-vth264/data/locale/gd-GB.ini new file mode 100644 index 000000000..503292ae0 --- /dev/null +++ b/plugins/mac-vth264/data/locale/gd-GB.ini @@ -0,0 +1,6 @@ +Bitrate="Reat bhiotaichean" +KeyframeIntervalSec="Eadaramh nam frèamaichean-iuchrach (diog, fèin-obrachail)" +Profile="Pròifil" +None="(Chan eil gin)" + + diff --git a/plugins/mac-vth264/data/locale/ka-GE.ini b/plugins/mac-vth264/data/locale/ka-GE.ini new file mode 100644 index 000000000..c6855e858 --- /dev/null +++ b/plugins/mac-vth264/data/locale/ka-GE.ini @@ -0,0 +1,14 @@ +VTH264EncHW="Apple VT H264 აპარატურული დამშიფრავი" +VTH264EncSW="Apple VT H264 პროგრამული დამშიფრავი" +VTEncoder="VideoToolbox დამშიფრავი" +Bitrate="ბიტური სიხშირე" +UseMaxBitrate="ბიტური სიხშირის შეზღუდვა" +MaxBitrate="უმაღლესი დაშვებული ბიტური სიხშირის" +MaxBitrateWindow="უმაღლესი ბიტური სიხშირის ფანჯარა (წამები)" +KeyframeIntervalSec="საკვანძო კადრებს შორის შუალედი (წამი, 0=თვითშერჩევა)" +Profile="პროფილი" +None="(არცერთი)" +DefaultEncoder="(ნაგულისხმევი დამშიფრავი)" +UseBFrames="B-კადრების გამოყენება" + + diff --git a/plugins/mac-vth264/data/locale/tl-PH.ini b/plugins/mac-vth264/data/locale/tl-PH.ini new file mode 100644 index 000000000..3918b8184 --- /dev/null +++ b/plugins/mac-vth264/data/locale/tl-PH.ini @@ -0,0 +1,14 @@ +VTH264EncHW="Ang Hardware Encoder ng Apple VT H264" +VTH264EncSW="Ang Software Encoder ng Apple VT H264" +VTEncoder="Ang VideoToolbox ng Encoder" +Bitrate="Bitreyt" +UseMaxBitrate="Limitasyon sa Bitreyt" +MaxBitrate="Pinakamalakas na bitrate" +MaxBitrateWindow="Pinakamalakas na bitrate window (segundo)" +KeyframeIntervalSec="Ang Pagitan sa Keyframe (segundo, 0=awto)" +Profile="Bista sa Tagiliran" +None="(Wala)" +DefaultEncoder="(I-default ang Encoder)" +UseBFrames="Gumamit ng mga B-Frame" + + diff --git a/plugins/mac-vth264/encoder.c b/plugins/mac-vth264/encoder.c index f5bb5e454..2d4d63d4a 100644 --- a/plugins/mac-vth264/encoder.c +++ b/plugins/mac-vth264/encoder.c @@ -6,6 +6,8 @@ #include #include +#include + #include #define VT_LOG(level, format, ...) \ @@ -74,23 +76,20 @@ struct vt_h264_encoder static void log_osstatus(int log_level, struct vt_h264_encoder *enc, const char *context, OSStatus code) { + char *c_str = NULL; CFErrorRef err = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, code, NULL); CFStringRef str = CFErrorCopyDescription(err); - CFIndex length = CFStringGetLength(str); - CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, - kCFStringEncodingUTF8); - - char *c_str = malloc(max_size); - if (CFStringGetCString(str, c_str, max_size, kCFStringEncodingUTF8)) { + c_str = cfstr_copy_cstr(str, kCFStringEncodingUTF8); + if (c_str) { if (enc) VT_BLOG(log_level, "Error in %s: %s", context, c_str); else VT_LOG(log_level, "Error in %s: %s", context, c_str); } - free(c_str); + bfree(c_str); CFRelease(str); CFRelease(err); } @@ -424,11 +423,12 @@ static void vt_h264_video_info(void *data, struct video_scale_info *info) enc->vt_pix_fmt = enc->fullrange ? kCVPixelFormatType_420YpCbCr8PlanarFullRange : kCVPixelFormatType_420YpCbCr8Planar; - } else if (info->format == VIDEO_FORMAT_I444) { - enc->obs_pix_fmt = info->format; - enc->vt_pix_fmt = kCVPixelFormatType_444YpCbCr10; + return; } + if (info->format == VIDEO_FORMAT_I444) + VT_BLOG(LOG_WARNING, "I444 color format not supported"); + // Anything else, return default enc->obs_pix_fmt = VIDEO_FORMAT_NV12; enc->vt_pix_fmt = enc->fullrange ? @@ -445,11 +445,12 @@ static void update_params(struct vt_h264_encoder *enc, obs_data_t *settings) struct video_scale_info info = { .format = voi->format }; + enc->fullrange = voi->range == VIDEO_RANGE_FULL; + // also sets the enc->vt_pix_fmt vt_h264_video_info(enc, &info); enc->colorspace = voi->colorspace; - enc->fullrange = voi->range == VIDEO_RANGE_FULL; enc->width = obs_encoder_get_width(enc->encoder); enc->height = obs_encoder_get_height(enc->encoder); diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 4c04ab1ed..1fe927e1e 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -13,6 +13,7 @@ set(obs-ffmpeg_HEADERS obs-ffmpeg-formats.h obs-ffmpeg-compat.h closest-pixel-format.h) + set(obs-ffmpeg_SOURCES obs-ffmpeg.c obs-ffmpeg-audio-encoders.c @@ -21,6 +22,13 @@ set(obs-ffmpeg_SOURCES obs-ffmpeg-mux.c obs-ffmpeg-source.c) +if(UNIX AND NOT APPLE) + list(APPEND obs-ffmpeg_SOURCES + obs-ffmpeg-vaapi.c) + LIST(APPEND obs-ffmpeg_PLATFORM_DEPS + ${LIBVA_LBRARIES}) +endif() + add_library(obs-ffmpeg MODULE ${obs-ffmpeg_HEADERS} ${obs-ffmpeg_SOURCES}) diff --git a/plugins/obs-ffmpeg/data/locale/ca-ES.ini b/plugins/obs-ffmpeg/data/locale/ca-ES.ini index a8fed0ddb..427a74c89 100644 --- a/plugins/obs-ffmpeg/data/locale/ca-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/ca-ES.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automàtic" ColorRange.Partial="Parcial" ColorRange.Full="Màxim" RestartMedia="Reinicia els mitjans" +SpeedPercentage="Velocitat (percentatge)" Seekable="Cercable" MediaFileFilter.AllMediaFiles="Tots els arxius multimèdia" diff --git a/plugins/obs-ffmpeg/data/locale/cs-CZ.ini b/plugins/obs-ffmpeg/data/locale/cs-CZ.ini index 3561fddd8..5f688a385 100644 --- a/plugins/obs-ffmpeg/data/locale/cs-CZ.ini +++ b/plugins/obs-ffmpeg/data/locale/cs-CZ.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automatický" ColorRange.Partial="Částečný" ColorRange.Full="Celkový" RestartMedia="Restartovat mediální zdroj" +SpeedPercentage="Rychlost (procenta)" Seekable="Posouvatelné" MediaFileFilter.AllMediaFiles="Všechny mediální soubory" diff --git a/plugins/obs-ffmpeg/data/locale/da-DK.ini b/plugins/obs-ffmpeg/data/locale/da-DK.ini index 892fd4ae5..25277c1bf 100644 --- a/plugins/obs-ffmpeg/data/locale/da-DK.ini +++ b/plugins/obs-ffmpeg/data/locale/da-DK.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Auto" ColorRange.Partial="Delvis" ColorRange.Full="Fuld" RestartMedia="Genstart Media" +SpeedPercentage="Hastighed (procent)" Seekable="Seekable" MediaFileFilter.AllMediaFiles="Alle mediefiler" diff --git a/plugins/obs-ffmpeg/data/locale/de-DE.ini b/plugins/obs-ffmpeg/data/locale/de-DE.ini index 3caf3e595..2025bfbb7 100644 --- a/plugins/obs-ffmpeg/data/locale/de-DE.ini +++ b/plugins/obs-ffmpeg/data/locale/de-DE.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automatisch" ColorRange.Partial="Teilweise" ColorRange.Full="Voll" RestartMedia="Medium neu starten" +SpeedPercentage="Geschwindigkeit (Prozent)" Seekable="Durch­such­bar" MediaFileFilter.AllMediaFiles="Alle Mediendateien" diff --git a/plugins/obs-ffmpeg/data/locale/el-GR.ini b/plugins/obs-ffmpeg/data/locale/el-GR.ini index 7a224d77a..77562dc5f 100644 --- a/plugins/obs-ffmpeg/data/locale/el-GR.ini +++ b/plugins/obs-ffmpeg/data/locale/el-GR.ini @@ -1,5 +1,6 @@ FFmpegOutput="Έξοδος FFmpeg" FFmpegAAC="FFmpeg προεπιλεγμένος κωδικοποιητής AAC" +FFmpegOpus="FFmpeg Opus κωδικοποιητής" Bitrate="Ρυθμός μετάδοσης bit" Preset="Προκαθορισμένο στυλ" RateControl="Έλεγχος ρυθμού" @@ -35,6 +36,8 @@ ColorRange.Auto="Αυτόματο" ColorRange.Partial="Μερικός" ColorRange.Full="Πλήρης" RestartMedia="Επανεκκίνηση Πολυμέσων" +SpeedPercentage="Ταχύτητα (τοις εκατό)" +Seekable="Παρεχόμενη" MediaFileFilter.AllMediaFiles="Όλα τα αρχεία πολυμέσων" MediaFileFilter.VideoFiles="Αρχεία Βίντεο" @@ -44,4 +47,6 @@ MediaFileFilter.AllFiles="Όλα τα αρχεία" ReplayBuffer="Διάρκεια μνήμης Replay" ReplayBuffer.Save="Αποθήκευση Replay" +HelperProcessFailed="Αδυναμία έναρξης τού βοηθού της διαδικασίας εγγραφής. Ελέγξτε ότι τα αρχεία OBS δεν έχουν αποκλειστεί η καταργηθεί από ένα τρίτο antivirus / λογισμικό ασφαλείας." +UnableToWritePath="Αδυναμία εγγραφής %1. Βεβαιωθείτε ότι χρησιμοποιείτε μια διαδρομή εγγραφής που επιτρέπεται για εγγραφή τού λογαριασμού χρήστη και ότι υπάρχει επαρκής χώρος στον σκληρό δίσκο." diff --git a/plugins/obs-ffmpeg/data/locale/es-ES.ini b/plugins/obs-ffmpeg/data/locale/es-ES.ini index f7999d497..9dcb1cfb1 100644 --- a/plugins/obs-ffmpeg/data/locale/es-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/es-ES.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automatico" ColorRange.Partial="Parcial" ColorRange.Full="Completo" RestartMedia="Reiniciar Medio" +SpeedPercentage="Velocidad (porcentaje)" Seekable="Buscable" MediaFileFilter.AllMediaFiles="Todos los archivos multimedia" diff --git a/plugins/obs-ffmpeg/data/locale/eu-ES.ini b/plugins/obs-ffmpeg/data/locale/eu-ES.ini index 2b100b1db..13d38a1c1 100644 --- a/plugins/obs-ffmpeg/data/locale/eu-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/eu-ES.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Auto" ColorRange.Partial="Partziala" ColorRange.Full="Osoa" RestartMedia="Berrabiarazi euskarria" +SpeedPercentage="Abiadura (ehunekoa)" Seekable="Bilagai" MediaFileFilter.AllMediaFiles="Multimedia-fitxategi guztiak" diff --git a/plugins/obs-ffmpeg/data/locale/fi-FI.ini b/plugins/obs-ffmpeg/data/locale/fi-FI.ini index 46ba85d43..1f24578ce 100644 --- a/plugins/obs-ffmpeg/data/locale/fi-FI.ini +++ b/plugins/obs-ffmpeg/data/locale/fi-FI.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automaattinen" ColorRange.Partial="Osittainen" ColorRange.Full="Täysi" RestartMedia="Uudelleenkäynnistä media" +SpeedPercentage="Nopeus (prosentti)" Seekable="Haettava" MediaFileFilter.AllMediaFiles="Kaikki mediatiedostot" diff --git a/plugins/obs-ffmpeg/data/locale/fil-PH.ini b/plugins/obs-ffmpeg/data/locale/fil-PH.ini new file mode 100644 index 000000000..7da7422e6 --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/fil-PH.ini @@ -0,0 +1,51 @@ +FFmpegOutput="Ang Awput ng FFmpeg" +FFmpegAAC="Ang Default FFmpeg AAC Encoder" +FFmpegOpus="Ang FFmpeg Opus Encoder" +Bitrate="Bitrate" +Preset="Ang Preset" +RateControl="Kontrolin ang Antas" +KeyframeIntervalSec="Ang pagitan ng Keyframe (segundos, 0=auto)" +Lossless="Lossless" + +BFrames="B-frames" + +NVENC.Use2Pass="Gamitin ang Two-Pass Encoding" +NVENC.Preset.default="I-Default" +NVENC.Preset.hq="Itaas ang Kalidad" +NVENC.Preset.hp="Itaas ang Pagganap" +NVENC.Preset.bd="Bluray" +NVENC.Preset.ll="Mababa na Latency" +NVENC.Preset.llhq="Mababa na Latency Mataas na kalidad" +NVENC.Preset.llhp="Mababa na Latency Mataas na pagganap" +NVENC.Level="Lebel" + +FFmpegSource="Pagkunan ng Media" +LocalFile="Ang File na lokal" +Looping="I-Loop" +Input="Ang Input" +InputFormat="Ang Format ng Input" +BufferingMB="Nag buffering ang Network (MB)" +HardwareDecode="Gamitin ang hardware decoding kapag magagamit" +ClearOnMediaEnd="Itago ang pinagmulan kung tapos ang playback" +Advanced="I-Advanced" +RestartWhenActivated="Simulan mulang ang playback kapag aktibo ang pinagmulan" +CloseFileWhenInactive="Isarado ang file pag di aktibo" +CloseFileWhenInactive.ToolTip="Isarado ang file kapag ang pinagmulan ay di ipinapakita ang stream or\nrecording. Ito nagpapahintulot na baguhin ang pinagmulang pag hindi aktibo,'\n pero dapat mayroong startup delay kapang ang pinagmulan muliang aktibo." +ColorRange="Antas ng kulay ng YUV" +ColorRange.Auto="I-Auto" +ColorRange.Partial="I-Partial" +ColorRange.Full="Buo" +RestartMedia="Simulan muli ang Media" +Seekable="I-Seekable" + +MediaFileFilter.AllMediaFiles="Lahat ng Media Files" +MediaFileFilter.VideoFiles="Bidyo Files" +MediaFileFilter.AudioFiles="Audio Files" +MediaFileFilter.AllFiles="Lahat ng Files" + +ReplayBuffer="Ang Replay Buffer" +ReplayBuffer.Save="I-Save ang Replay" + +HelperProcessFailed="Hindi pwedeng simulan ang pag proseso ng recording helper. Tingan kung OBS file ay hindi hinarangan o tinanggal ng anumang 3rd part antivirus / security software." +UnableToWritePath="Hindi pwedeng sumulat sa %1. Siguraduhin na gamit ang recording path kung saan ang user account mo ay tanggap ang pagsulat at mayroon sapat na espasyo sa disk." + diff --git a/plugins/obs-ffmpeg/data/locale/fr-FR.ini b/plugins/obs-ffmpeg/data/locale/fr-FR.ini index 7a8431449..9b15413f0 100644 --- a/plugins/obs-ffmpeg/data/locale/fr-FR.ini +++ b/plugins/obs-ffmpeg/data/locale/fr-FR.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Auto" ColorRange.Partial="Partielle" ColorRange.Full="Complète" RestartMedia="Redémarrez Media" +SpeedPercentage="Vitesse (pourcent)" Seekable="Navigable" MediaFileFilter.AllMediaFiles="Tous les fichiers multimédias" diff --git a/plugins/obs-ffmpeg/data/locale/gd-GB.ini b/plugins/obs-ffmpeg/data/locale/gd-GB.ini new file mode 100644 index 000000000..8c485623d --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/gd-GB.ini @@ -0,0 +1,49 @@ +FFmpegOutput="Às-chur FFmpeg" +FFmpegAAC="Inneal-còdachaidh AAC tùsail airson FFmpeg" +FFmpegOpus="Inneal-còdachaidh Opus airson FFmpeg" +Bitrate="Reat bhiotaichean" +Preset="Ro-sheata" +RateControl="Smachd air an reat" +KeyframeIntervalSec="Eadaramh nam frèamaichean-iuchrach (diog, fèin-obrachail)" +Lossless="Gun chall càileachd" + +BFrames="Frèamaichean-B" + +NVENC.Use2Pass="Cleachd còdachadh dà phas" +NVENC.Preset.default="Bun-roghainn" +NVENC.Preset.hq="Càileachd àrd" +NVENC.Preset.hp="Dèanadas àrd" +NVENC.Preset.bd="Bluray" +NVENC.Preset.ll="Foillidheachd ìosal" +NVENC.Preset.llhq="Foillidheachd ìosal ⁊ càileachd àrd" +NVENC.Preset.llhp="Foillidheachd ìosal ⁊ dèanadas àrd" +NVENC.Level="Leibheil" + +FFmpegSource="Tùs a’ mheadhain" +LocalFile="Faidhle ionadail" +Looping="Lùb" +Input="Ion-chur" +InputFormat="Fòrmat an ion-chuir" +BufferingMB="Bufair an lìonraidh (MB)" +HardwareDecode="Cleachd dì-chòdachadh bathair-chruaidh ma bhios e ri fhaighinn" +ClearOnMediaEnd="Falaich an tùs nuair a bhios a’ chluiche deiseil" +Advanced="Adhartach" +RestartWhenActivated="Ath-thòisich a’ chluiche nuair a thig gnìomh on tùs" +CloseFileWhenInactive="Dùin am faidhle mur eil gnìomh ann" +ColorRange="Rainse dhathan YUV" +ColorRange.Auto="Fèin-obrachail" +ColorRange.Partial="Leth-phàirteach" +ColorRange.Full="Làn" +RestartMedia="Ath-thòisich am meadhan" +SpeedPercentage="Luaths (sa cheud)" +Seekable="Gabhaidh sireadh ann" + +MediaFileFilter.AllMediaFiles="A h-uile faidhle meadhain" +MediaFileFilter.VideoFiles="Faidhlichean video" +MediaFileFilter.AudioFiles="Faidhlichean fuaime" +MediaFileFilter.AllFiles="A h-uile faidhle" + +ReplayBuffer="Bufair na h-ath-chluiche" +ReplayBuffer.Save="Sàbhail an ath-chluiche" + + diff --git a/plugins/obs-ffmpeg/data/locale/hu-HU.ini b/plugins/obs-ffmpeg/data/locale/hu-HU.ini index 7b1b598ab..d1339a7ad 100644 --- a/plugins/obs-ffmpeg/data/locale/hu-HU.ini +++ b/plugins/obs-ffmpeg/data/locale/hu-HU.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Auto" ColorRange.Partial="Részleges" ColorRange.Full="Teljes" RestartMedia="Media újraindítása" +SpeedPercentage="Sebesség (százalékos)" Seekable="Kereshető" MediaFileFilter.AllMediaFiles="Minden médiafájl" diff --git a/plugins/obs-ffmpeg/data/locale/it-IT.ini b/plugins/obs-ffmpeg/data/locale/it-IT.ini index 623ba5ac4..2d69185d1 100644 --- a/plugins/obs-ffmpeg/data/locale/it-IT.ini +++ b/plugins/obs-ffmpeg/data/locale/it-IT.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Autom." ColorRange.Partial="Parziale" ColorRange.Full="Intero" RestartMedia="Riavvia Media" +SpeedPercentage="Velocità (percentuale)" Seekable="Ricercabile" MediaFileFilter.AllMediaFiles="Tutti i file media" diff --git a/plugins/obs-ffmpeg/data/locale/ja-JP.ini b/plugins/obs-ffmpeg/data/locale/ja-JP.ini index c78930992..c291993c7 100644 --- a/plugins/obs-ffmpeg/data/locale/ja-JP.ini +++ b/plugins/obs-ffmpeg/data/locale/ja-JP.ini @@ -36,6 +36,7 @@ ColorRange.Auto="自動" ColorRange.Partial="一部" ColorRange.Full="全部" RestartMedia="メディアを再開する" +SpeedPercentage="速度 (パーセント)" Seekable="シーク可能" MediaFileFilter.AllMediaFiles="すべてのメディアファイル" diff --git a/plugins/obs-ffmpeg/data/locale/ka-GE.ini b/plugins/obs-ffmpeg/data/locale/ka-GE.ini new file mode 100644 index 000000000..f1d9220f0 --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/ka-GE.ini @@ -0,0 +1,52 @@ +FFmpegOutput="FFmpeg გამომავალი სიგნალი" +FFmpegAAC="FFmpeg ნაგულისხმევი AAC დამშიფრავი" +FFmpegOpus="FFmpeg Opus დამშიფრავი" +Bitrate="ბიტური სიხშირე" +Preset="მზა პარამეტრები" +RateControl="სიხშირის მართვა" +KeyframeIntervalSec="საკვანძო კადრებს შორის შუალედი (წამი, 0=თვითშერჩევა)" +Lossless="უდანაკარგო" + +BFrames="B-კადრები" + +NVENC.Use2Pass="ორმაგი დაშიფვრის გამოყენება" +NVENC.Preset.default="ნაგულისხმევი" +NVENC.Preset.hq="მაღალი ხარისხი" +NVENC.Preset.hp="მაღალი წარმადობა" +NVENC.Preset.bd="Bluray" +NVENC.Preset.ll="მცირე დაყოვნება" +NVENC.Preset.llhq="მცირე დაყოვნება, მაღალი ხარისხი" +NVENC.Preset.llhp="მცირე დაყოვნება, მაღალი წარმადობა" +NVENC.Level="საფეხური" + +FFmpegSource="მასალის წყარო" +LocalFile="ადგილობრივი ფაილი" +Looping="დაუსრულებლად გამეორება" +Input="შეტანა" +InputFormat="შეტანის ფორმატი" +BufferingMB="ქსელის ბუფერიზაცია (მბაიტი)" +HardwareDecode="აპარატურული დაშიფვრის გამოყენება, ხელმისაწვდომობის შემთხვევაში" +ClearOnMediaEnd="წყაროს დამალვა, გაშვების დამთავრებისას" +Advanced="გაფართოებული" +RestartWhenActivated="ხელახლა გაშვება წყაროს ამოქმედებისას" +CloseFileWhenInactive="ფაილის დახურვა უმოქმედობისას" +CloseFileWhenInactive.ToolTip="ფაილი დაიხურება, თუ წყარო არ იქნება ეთერში ან\nჩაწერაზე გაშვებული. ეს საშუალებას იძლევა შეიცვალოს ფაილი, როცა წყარო არაა მოქმედი,\nთუმცა ხელახლა ამოქმედებისას, შესაძლოა გარკვეული დროით დაყოვნებას ჰქონდეს ადგილი." +ColorRange="YUV ფერთა გამა" +ColorRange.Auto="ავტომატური" +ColorRange.Partial="ნაწილობრივი" +ColorRange.Full="სრული" +RestartMedia="მასალის ხელახლა გაშვება" +SpeedPercentage="სიჩქარე (პროცენტი)" +Seekable="გადახვევით" + +MediaFileFilter.AllMediaFiles="ყველა მასალა" +MediaFileFilter.VideoFiles="ვიდეოფაილები" +MediaFileFilter.AudioFiles="ხმოვანი ფაილები" +MediaFileFilter.AllFiles="ყველა ფაილი" + +ReplayBuffer="გადახვევა" +ReplayBuffer.Save="გადახვევის შენახვა" + +HelperProcessFailed="ჩაწერის პროცესის გაშვება ვერ ხერხდება. გადაამოწმეთ, ხომ არ არის OBS ფაილები შეზღუდული ან წაშლილი ანტივირუსის / უსაფრთხოების სხვა პროგრამების მიერ." +UnableToWritePath="%1-ში ჩაწერა ვერ ხერხდება. დარწმუნდით, რომ ჩაწერისთვის ისეთი მისამართი გაქვთ მითითებული, სადაც ჩაწერის ნებართვაც გააჩნია თქვენს ანგარიშს და ამასთან, არის საკმარისი ადგილი დისკზე." + diff --git a/plugins/obs-ffmpeg/data/locale/ko-KR.ini b/plugins/obs-ffmpeg/data/locale/ko-KR.ini index ddf504132..535fdb832 100644 --- a/plugins/obs-ffmpeg/data/locale/ko-KR.ini +++ b/plugins/obs-ffmpeg/data/locale/ko-KR.ini @@ -36,6 +36,7 @@ ColorRange.Auto="자동" ColorRange.Partial="부분" ColorRange.Full="전체" RestartMedia="미디어 다시재생" +SpeedPercentage="속도 (백분율)" Seekable="탐색 가능" MediaFileFilter.AllMediaFiles="모든 미디어 파일" diff --git a/plugins/obs-ffmpeg/data/locale/nb-NO.ini b/plugins/obs-ffmpeg/data/locale/nb-NO.ini index 38ad66c86..7439c6e91 100644 --- a/plugins/obs-ffmpeg/data/locale/nb-NO.ini +++ b/plugins/obs-ffmpeg/data/locale/nb-NO.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automatisk" ColorRange.Partial="Delvis" ColorRange.Full="Hel" RestartMedia="Start media på nytt" +SpeedPercentage="Fart (prosent)" Seekable="Søkbar" MediaFileFilter.AllMediaFiles="Alle mediefiler" diff --git a/plugins/obs-ffmpeg/data/locale/nl-NL.ini b/plugins/obs-ffmpeg/data/locale/nl-NL.ini index d45cd8a5d..62c916395 100644 --- a/plugins/obs-ffmpeg/data/locale/nl-NL.ini +++ b/plugins/obs-ffmpeg/data/locale/nl-NL.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automatisch" ColorRange.Partial="Gedeeltelijk" ColorRange.Full="Volledig" RestartMedia="Media herstarten" +SpeedPercentage="Snelheid (percentage)" Seekable="Zoekbaar" MediaFileFilter.AllMediaFiles="Alle mediabestanden" diff --git a/plugins/obs-ffmpeg/data/locale/pl-PL.ini b/plugins/obs-ffmpeg/data/locale/pl-PL.ini index 9215a4465..09f1f5d5d 100644 --- a/plugins/obs-ffmpeg/data/locale/pl-PL.ini +++ b/plugins/obs-ffmpeg/data/locale/pl-PL.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automatycznie" ColorRange.Partial="Częściowy" ColorRange.Full="Pełny" RestartMedia="Zrestartuj plik audio-wideo" +SpeedPercentage="Szybkość (procent)" Seekable="Przeszukiwalny" MediaFileFilter.AllMediaFiles="Wszystkie pliki multimedialne" diff --git a/plugins/obs-ffmpeg/data/locale/pt-BR.ini b/plugins/obs-ffmpeg/data/locale/pt-BR.ini index fe084c43c..2004e1a5d 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-BR.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-BR.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Auto" ColorRange.Partial="Parcial" ColorRange.Full="Completo" RestartMedia="Reiniciar Mídia" +SpeedPercentage="Velocidade (percentagem)" Seekable="Procurável" MediaFileFilter.AllMediaFiles="Todos Arquivos de Mídia" diff --git a/plugins/obs-ffmpeg/data/locale/ru-RU.ini b/plugins/obs-ffmpeg/data/locale/ru-RU.ini index ba5a81a1d..c0aeeefe5 100644 --- a/plugins/obs-ffmpeg/data/locale/ru-RU.ini +++ b/plugins/obs-ffmpeg/data/locale/ru-RU.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Автоматически" ColorRange.Partial="Частичный" ColorRange.Full="Полный" RestartMedia="Перезапустить медиа" +SpeedPercentage="Скорость (проценты)" Seekable="Перематываемый" MediaFileFilter.AllMediaFiles="Все медиа-файлы" diff --git a/plugins/obs-ffmpeg/data/locale/sv-SE.ini b/plugins/obs-ffmpeg/data/locale/sv-SE.ini index 82fbef2b8..e098af91a 100644 --- a/plugins/obs-ffmpeg/data/locale/sv-SE.ini +++ b/plugins/obs-ffmpeg/data/locale/sv-SE.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Automatisk" ColorRange.Partial="Delvis" ColorRange.Full="Full" RestartMedia="Starta om media" +SpeedPercentage="Hastighet (procent)" Seekable="Sökbar" MediaFileFilter.AllMediaFiles="Alla mediafiler" diff --git a/plugins/obs-ffmpeg/data/locale/tl-PH.ini b/plugins/obs-ffmpeg/data/locale/tl-PH.ini new file mode 100644 index 000000000..b3b3189ea --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/tl-PH.ini @@ -0,0 +1,51 @@ +FFmpegOutput="FFmpeg Panglabas" +FFmpegAAC="Ang hindi pagharap ng FFmpeg AAC Encoder" +FFmpegOpus="Ang FFmpeg Opus Encoder" +Bitrate="Bitreyt" +Preset="I-preset" +RateControl="Kontrolin ang Rate" +KeyframeIntervalSec="Ang Pagitan ng Keyframe (segundo, 0=awto)" +Lossless="Walang Pagkawala" + +BFrames="Ang mga B-frame" + +NVENC.Use2Pass="Gamitin ang Dalawang Pass ng Encoding" +NVENC.Preset.default="I-default" +NVENC.Preset.hq="Mataas na Kalidad" +NVENC.Preset.hp="Mataas na Pagganap" +NVENC.Preset.bd="Ang Bluray" +NVENC.Preset.ll="Mababang Pagkawalang kilos" +NVENC.Preset.llhq="Mababang-Pagkawalang kilos na Mataas ang Kalidad" +NVENC.Preset.llhp="Mababang-Pagkawalang kilos na Mataan ang Pagganap" +NVENC.Level="Antas" + +FFmpegSource="Pinagmulan ng Media" +LocalFile="Ang Lokal na File" +Looping="Silo" +Input="Pampasok" +InputFormat="Pampasok na Format" +BufferingMB="Ang Network Buffering (MB)" +HardwareDecode="Gamitin ang hardware sa pag-decode kapag itong magagamit na" +ClearOnMediaEnd="Itago ang pinagmulan kapag ang playback ay natapos" +Advanced="Nauuna" +RestartWhenActivated="I-restart ang playback kapag ang pinagmulan ay naging aktibo na" +CloseFileWhenInactive="Isarado ang file kapag hindi ito aktibo" +CloseFileWhenInactive.ToolTip="Tinatanggal ang file kapag ang pinagmulan ay hindi ipinapakita sa stream o\nnatala. Pinapayagan nito na mabago ang file kapag ang pinagmulan ay hindi aktibo,\nngunit maaaring may ilang pagkaantala sa startup kapag muling pinagana ang pinagmulan." +ColorRange="Ang Saklaw ng Kulay na YUV" +ColorRange.Auto="Awto" +ColorRange.Partial="Bahagya" +ColorRange.Full="Puno" +RestartMedia="I-restart ang Media" +Seekable="Maayos" + +MediaFileFilter.AllMediaFiles="Lahat ng mga Media File" +MediaFileFilter.VideoFiles="Ang mga Video File" +MediaFileFilter.AudioFiles="Ang mga Audio File" +MediaFileFilter.AllFiles="Lahat ng mga File" + +ReplayBuffer="I-replay ang Buffer" +ReplayBuffer.Save="I-save ang Replay" + +HelperProcessFailed="Hindi magawang simulan ang pagtatala ng proseso ng katulong. Suriin na ang mga file ng OBS ay hindi na-block o inalis ng anumang 3rd party antivirus / seguridad ng software." +UnableToWritePath="Hindi makapagsulat sa %1. Tiyaking gumagamit ka ng isang landas sa pagtatala kung saan pinahihintulutan ang iyong user account na magsulat at mayroong sapat na puwang sa disk." + diff --git a/plugins/obs-ffmpeg/data/locale/tr-TR.ini b/plugins/obs-ffmpeg/data/locale/tr-TR.ini index 22393a7c7..c4c3b771c 100644 --- a/plugins/obs-ffmpeg/data/locale/tr-TR.ini +++ b/plugins/obs-ffmpeg/data/locale/tr-TR.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Otomatik" ColorRange.Partial="Kısmi" ColorRange.Full="Tam" RestartMedia="Ortamı Yeniden Başlat" +SpeedPercentage="Hız (yüzde)" Seekable="Aranabilir" MediaFileFilter.AllMediaFiles="Tüm Medya Dosyaları" diff --git a/plugins/obs-ffmpeg/data/locale/uk-UA.ini b/plugins/obs-ffmpeg/data/locale/uk-UA.ini index 4ab367d95..88c2d4432 100644 --- a/plugins/obs-ffmpeg/data/locale/uk-UA.ini +++ b/plugins/obs-ffmpeg/data/locale/uk-UA.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Автовизначення" ColorRange.Partial="Частковий" ColorRange.Full="Повний" RestartMedia="Перезапустити медіа" +SpeedPercentage="Швидкість (відсотків)" Seekable="HTTP з перемотуванням" MediaFileFilter.AllMediaFiles="Файли мультимедіа" diff --git a/plugins/obs-ffmpeg/data/locale/zh-CN.ini b/plugins/obs-ffmpeg/data/locale/zh-CN.ini index da0beb45e..f630e620f 100644 --- a/plugins/obs-ffmpeg/data/locale/zh-CN.ini +++ b/plugins/obs-ffmpeg/data/locale/zh-CN.ini @@ -30,12 +30,13 @@ ClearOnMediaEnd="当播放结束时隐藏源" Advanced="高级" RestartWhenActivated="当源变为活动状态时重新启动播放" CloseFileWhenInactive="非活跃状态时关闭文件" -CloseFileWhenInactive.ToolTip="当源没有显示在推流或者\n录像时关闭文件. 这使得在源不活跃状态时可以更改文件,\n但是当当源重新激活时, 可能有一些启动延迟." +CloseFileWhenInactive.ToolTip="当源没有显示在推流或者\n录像时关闭文件。这使得在源不活跃状态时可以更改文件,\n但是当当源重新激活时, 可能有一些启动延迟。" ColorRange="YUV 颜色范围" ColorRange.Auto="自动" ColorRange.Partial="局部" ColorRange.Full="全部" RestartMedia="重新启动媒体" +SpeedPercentage="速度(百分比)" Seekable="可搜索" MediaFileFilter.AllMediaFiles="所有媒体文件" diff --git a/plugins/obs-ffmpeg/data/locale/zh-TW.ini b/plugins/obs-ffmpeg/data/locale/zh-TW.ini index 80e012161..88ee83972 100644 --- a/plugins/obs-ffmpeg/data/locale/zh-TW.ini +++ b/plugins/obs-ffmpeg/data/locale/zh-TW.ini @@ -36,6 +36,7 @@ ColorRange.Auto="自動" ColorRange.Partial="部分" ColorRange.Full="全部" RestartMedia="重新播放媒體" +SpeedPercentage="速度 (百分比)" Seekable="可查找" MediaFileFilter.AllMediaFiles="所有媒體檔案" diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c index 755b67c2f..b3c5c8857 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c @@ -316,6 +316,7 @@ static void create_video_stream(struct ffmpeg_mux *ffm) (AVRational){ffm->params.fps_den, ffm->params.fps_num}; ffm->video_stream->time_base = context->time_base; + ffm->video_stream->avg_frame_rate = av_inv_q(context->time_base); if (ffm->output->oformat->flags & AVFMT_GLOBALHEADER) context->flags |= CODEC_FLAG_GLOBAL_H; diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c index 31b0e9b33..7d6c5ae68 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c @@ -132,6 +132,10 @@ static bool initialize_codec(struct enc_encoder *enc) warn("Failed to open AAC codec: %s", av_err2str(ret)); return false; } + enc->aframe->format = enc->context->sample_fmt; + enc->aframe->channels = enc->context->channels; + enc->aframe->channel_layout = enc->context->channel_layout; + enc->aframe->sample_rate = enc->context->sample_rate; enc->frame_size = enc->context->frame_size; if (!enc->frame_size) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index facc17141..9acdef24b 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -290,6 +290,11 @@ static bool open_audio_codec(struct ffmpeg_data *data) return false; } + data->aframe->format = context->sample_fmt; + data->aframe->channels = context->channels; + data->aframe->channel_layout = context->channel_layout; + data->aframe->sample_rate = context->sample_rate; + context->strict_std_compliance = -2; ret = avcodec_open2(context, data->acodec, NULL); diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c new file mode 100644 index 000000000..6f6e9b699 --- /dev/null +++ b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c @@ -0,0 +1,536 @@ +/****************************************************************************** + Copyright (C) 2016 by Hugh Bailey + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 27, 100) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "obs-ffmpeg-formats.h" + +#define do_log(level, format, ...) \ + blog(level, "[FFMPEG VAAPI encoder: '%s'] " format, \ + obs_encoder_get_name(enc->encoder), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) + +struct vaapi_encoder { + obs_encoder_t *encoder; + + AVBufferRef *vadevice_ref; + AVBufferRef *vaframes_ref; + + AVCodec * vaapi; + AVCodecContext *context; + + AVFrame *vframe; + + DARRAY(uint8_t) buffer; + + uint8_t *header; + size_t header_size; + + uint8_t *sei; + size_t sei_size; + + int height; + bool first_packet; + bool initialized; +}; + +static const char *vaapi_getname(void *unused) +{ + UNUSED_PARAMETER(unused); + return "FFMPEG VAAPI"; +} + +static inline bool valid_format(enum video_format format) +{ + return format == VIDEO_FORMAT_NV12; +} + +static void vaapi_video_info(void *data, struct video_scale_info *info) +{ + struct vaapi_encoder *enc = data; + enum video_format pref_format; + + pref_format = obs_encoder_get_preferred_video_format(enc->encoder); + + if (!valid_format(pref_format)) { + pref_format = valid_format(info->format) ? info->format + : VIDEO_FORMAT_NV12; + } + + info->format = pref_format; +} + +static bool vaapi_init_codec(struct vaapi_encoder *enc, const char *path) +{ + int ret; + + ret = av_hwdevice_ctx_create(&enc->vadevice_ref, AV_HWDEVICE_TYPE_VAAPI, + path, NULL, 0); + if (ret < 0) { + warn("Failed to create VAAPI device context: %s", + av_err2str(ret)); + return false; + } + + enc->vaframes_ref = av_hwframe_ctx_alloc(enc->vadevice_ref); + if (!enc->vaframes_ref) { + warn("Failed to alloc HW frames context"); + return false; + } + + AVHWFramesContext *frames_ctx = + (AVHWFramesContext *)enc->vaframes_ref->data; + frames_ctx->format = AV_PIX_FMT_VAAPI; + frames_ctx->sw_format = AV_PIX_FMT_NV12; + frames_ctx->width = enc->context->width; + frames_ctx->height = enc->context->height; + frames_ctx->initial_pool_size = 20; + + ret = av_hwframe_ctx_init(enc->vaframes_ref); + if (ret < 0) { + warn("Failed to init HW frames context: %s", av_err2str(ret)); + return false; + } + + /* 2. Create software frame and picture */ + enc->vframe = av_frame_alloc(); + if (!enc->vframe) { + warn("Failed to allocate video frame"); + return false; + } + + enc->vframe->format = enc->context->pix_fmt; + enc->vframe->width = enc->context->width; + enc->vframe->height = enc->context->height; + enc->vframe->colorspace = enc->context->colorspace; + enc->vframe->color_range = enc->context->color_range; + + ret = av_frame_get_buffer(enc->vframe, base_get_alignment()); + if (ret < 0) { + warn("Failed to allocate vframe: %s", av_err2str(ret)); + return false; + } + + /* 3. set up codec */ + enc->context->pix_fmt = AV_PIX_FMT_VAAPI; + enc->context->hw_frames_ctx = av_buffer_ref(enc->vaframes_ref); + + ret = avcodec_open2(enc->context, enc->vaapi, NULL); + if (ret < 0) { + warn("Failed to open VAAPI codec: %s", av_err2str(ret)); + return false; + } + + enc->initialized = true; + return true; +} + +static bool vaapi_update(void *data, obs_data_t *settings) +{ + struct vaapi_encoder *enc = data; + + const char *device = obs_data_get_string(settings, "vaapi_device"); + + int profile = (int)obs_data_get_int(settings, "profile"); + int bf = (int)obs_data_get_int(settings, "bf"); + + int level = (int)obs_data_get_int(settings, "level"); + int bitrate = (int)obs_data_get_int(settings, "bitrate"); + int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); + + int qp = (int)obs_data_get_int(settings, "qp"); + int quality = (int)obs_data_get_int(settings, "quality"); + + av_opt_set_int(enc->context->priv_data, "qp", qp, 0); + av_opt_set_int(enc->context->priv_data, "quality", quality, 0); + + video_t * video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + struct video_scale_info info; + + info.format = voi->format; + info.colorspace = voi->colorspace; + info.range = voi->range; + + vaapi_video_info(enc, &info); + + enc->context->profile = profile; + enc->context->max_b_frames = bf; + enc->context->level = level; + enc->context->bit_rate = bitrate * 1000; + + enc->context->width = obs_encoder_get_width(enc->encoder); + enc->context->height = obs_encoder_get_height(enc->encoder); + + enc->context->time_base = (AVRational){voi->fps_den, voi->fps_num}; + enc->context->pix_fmt = obs_to_ffmpeg_video_format(info.format); + enc->context->colorspace = info.colorspace == VIDEO_CS_709 + ? AVCOL_SPC_BT709 + : AVCOL_SPC_BT470BG; + enc->context->color_range = info.range == VIDEO_RANGE_FULL + ? AVCOL_RANGE_JPEG + : AVCOL_RANGE_MPEG; + + if (keyint_sec > 0) { + enc->context->gop_size = + keyint_sec * voi->fps_num / voi->fps_den; + } else { + enc->context->gop_size = 120; + } + + enc->height = enc->context->height; + + info("settings:\n" + "\tdevice: %s\n" + "\tqp: %d\n" + "\tquality: %d\n" + "\tprofile: %d\n" + "\tlevel: %d\n" + "\tbitrate: %d\n" + "\tkeyint: %d\n" + "\twidth: %d\n" + "\theight: %d\n" + "\tb-frames: %d\n", + device, qp, quality, profile, level, bitrate, + enc->context->gop_size, enc->context->width, + enc->context->height, enc->context->max_b_frames); + + return vaapi_init_codec(enc, device); +} + +static void vaapi_destroy(void *data) +{ + struct vaapi_encoder *enc = data; + + if (enc->initialized) { + AVPacket pkt = {0}; + int r_pkt = 1; + + while (r_pkt) { +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + if (avcodec_receive_packet(enc->context, &pkt) < 0) + break; +#else + if (avcodec_encode_video2(enc->context, &pkt, NULL, + &r_pkt) < 0) + break; +#endif + + if (r_pkt) + av_packet_unref(&pkt); + } + } + + avcodec_close(enc->context); + av_frame_unref(enc->vframe); + av_frame_free(&enc->vframe); + av_buffer_unref(&enc->vaframes_ref); + av_buffer_unref(&enc->vadevice_ref); + da_free(enc->buffer); + bfree(enc->header); + bfree(enc->sei); + + bfree(enc); +} + +static void *vaapi_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + struct vaapi_encoder *enc; + avcodec_register_all(); + + enc = bzalloc(sizeof(*enc)); + enc->encoder = encoder; + + int vaapi_codec = (int)obs_data_get_int(settings, "vaapi_codec"); + + if (vaapi_codec == AV_CODEC_ID_H264) { + enc->vaapi = avcodec_find_encoder_by_name("h264_vaapi"); + } + + enc->first_packet = true; + + blog(LOG_INFO, "---------------------------------"); + + if (!enc->vaapi) { + warn("Couldn't find encoder"); + goto fail; + } + + enc->context = avcodec_alloc_context3(enc->vaapi); + if (!enc->context) { + warn("Failed to create codec context"); + goto fail; + } + + if (!vaapi_update(enc, settings)) + goto fail; + + return enc; + +fail: + vaapi_destroy(enc); + return NULL; +} + +static inline void copy_data(AVFrame *pic, const struct encoder_frame *frame, + int height, enum AVPixelFormat format) +{ + int h_chroma_shift, v_chroma_shift; + av_pix_fmt_get_chroma_sub_sample( + format, &h_chroma_shift, &v_chroma_shift); + for (int plane = 0; plane < MAX_AV_PLANES; plane++) { + if (!frame->data[plane]) + continue; + + int frame_rowsize = (int)frame->linesize[plane]; + int pic_rowsize = pic->linesize[plane]; + int bytes = frame_rowsize < pic_rowsize ? frame_rowsize + : pic_rowsize; + int plane_height = height >> (plane ? v_chroma_shift : 0); + + for (int y = 0; y < plane_height; y++) { + int pos_frame = y * frame_rowsize; + int pos_pic = y * pic_rowsize; + + memcpy(pic->data[plane] + pos_pic, + frame->data[plane] + pos_frame, bytes); + } + } +} + +static bool vaapi_encode(void *data, struct encoder_frame *frame, + struct encoder_packet *packet, bool *received_packet) +{ + struct vaapi_encoder *enc = data; + AVFrame * hwframe = NULL; + AVPacket av_pkt; + int got_packet; + int ret; + + hwframe = av_frame_alloc(); + if (!hwframe) { + warn("vaapi_encode: failed to allocate hw frame"); + return false; + } + + ret = av_hwframe_get_buffer(enc->vaframes_ref, hwframe, 0); + if (ret < 0) { + warn("vaapi_encode: failed to get buffer for hw frame: %s", + av_err2str(ret)); + goto fail; + } + + copy_data(enc->vframe, frame, enc->height, enc->context->pix_fmt); + + enc->vframe->pts = frame->pts; + hwframe->pts = frame->pts; + hwframe->width = enc->vframe->width; + hwframe->height = enc->vframe->height; + + ret = av_hwframe_transfer_data(hwframe, enc->vframe, 0); + if (ret < 0) { + warn("vaapi_encode: failed to upload hw frame: %s", + av_err2str(ret)); + goto fail; + } + + ret = av_frame_copy_props(hwframe, enc->vframe); + if (ret < 0) { + warn("vaapi_encode: failed to copy props to hw frame: %s", + av_err2str(ret)); + goto fail; + } + + av_init_packet(&av_pkt); + +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + ret = avcodec_send_frame(enc->context, hwframe); + if (ret == 0) + ret = avcodec_receive_packet(enc->context, &av_pkt); + + got_packet = (ret == 0); + + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + ret = 0; +#else + ret = avcodec_encode_video2( + enc->context, &av_pkt, hwframe, &got_packet); +#endif + if (ret < 0) { + warn("vaapi_encode: Error encoding: %s", av_err2str(ret)); + goto fail; + } + + if (got_packet && av_pkt.size) { + if (enc->first_packet) { + uint8_t *new_packet; + size_t size; + + enc->first_packet = false; + obs_extract_avc_headers(av_pkt.data, av_pkt.size, + &new_packet, &size, &enc->header, + &enc->header_size, &enc->sei, + &enc->sei_size); + + da_copy_array(enc->buffer, new_packet, size); + bfree(new_packet); + } else { + da_copy_array(enc->buffer, av_pkt.data, av_pkt.size); + } + + packet->pts = av_pkt.pts; + packet->dts = av_pkt.dts; + packet->data = enc->buffer.array; + packet->size = enc->buffer.num; + packet->type = OBS_ENCODER_VIDEO; + packet->keyframe = obs_avc_keyframe(packet->data, packet->size); + *received_packet = true; + } else { + *received_packet = false; + } + + av_packet_unref(&av_pkt); + av_frame_free(&hwframe); + return true; + +fail: + av_frame_free(&hwframe); + return false; +} + +static void set_visible(obs_properties_t *ppts, const char *name, bool visible) +{ + obs_property_t *p = obs_properties_get(ppts, name); + obs_property_set_visible(p, visible); +} + +static void vaapi_defaults(obs_data_t *settings) +{ + obs_data_set_default_string( + settings, "vaapi_device", "/dev/dri/renderD128"); + obs_data_set_default_int(settings, "vaapi_codec", AV_CODEC_ID_H264); + obs_data_set_default_int(settings, "profile", + FF_PROFILE_H264_CONSTRAINED_BASELINE); + obs_data_set_default_int(settings, "level", 40); + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "keyint_sec", 0); + obs_data_set_default_int(settings, "bf", 0); + obs_data_set_default_int(settings, "qp", 20); + obs_data_set_default_int(settings, "quality", 0); + obs_data_set_default_int(settings, "rendermode", 0); +} + +static obs_properties_t *vaapi_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + + obs_properties_t *props = obs_properties_create(); + obs_property_t * list; + + list = obs_properties_add_list(props, "vaapi_device", "VAAPI Device", + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + char path[128] = "/dev/dri/renderD1"; + for (int i = 28;; i++) { + sprintf(path, "/dev/dri/renderD1%d", i); + if (access(path, F_OK) == 0) { + char card[128] = "Card: "; + sprintf(card, "Card%d: %s", i - 28, path); + obs_property_list_add_string(list, card, path); + } else { + break; + } + } + + list = obs_properties_add_list(props, "vaapi_codec", "VAAPI Codec", + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int(list, "H.264 (default)", AV_CODEC_ID_H264); + + list = obs_properties_add_list(props, "level", "Level", + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, "480p30 (3.0)", 30); + obs_property_list_add_int(list, "720p30/480p60 (3.1)", 31); + obs_property_list_add_int( + list, "Compatibility mode (4.0 default)", 40); + obs_property_list_add_int(list, "720p60/1080p30 (4.1)", 41); + obs_property_list_add_int(list, "1080p60 (4.2)", 42); + + obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), 0, + 300000, 50); + + obs_properties_add_int(props, "keyint_sec", + obs_module_text("Keyframe Interval (seconds)"), 0, 20, + 1); + + return props; +} + +static bool vaapi_extra_data(void *data, uint8_t **extra_data, size_t *size) +{ + struct vaapi_encoder *enc = data; + + *extra_data = enc->header; + *size = enc->header_size; + return true; +} + +static bool vaapi_sei_data(void *data, uint8_t **extra_data, size_t *size) +{ + struct vaapi_encoder *enc = data; + + *extra_data = enc->sei; + *size = enc->sei_size; + return true; +} + +struct obs_encoder_info vaapi_encoder_info = { + .id = "ffmpeg_vaapi", + .type = OBS_ENCODER_VIDEO, + .codec = "h264", + .get_name = vaapi_getname, + .create = vaapi_create, + .destroy = vaapi_destroy, + .encode = vaapi_encode, + .get_defaults = vaapi_defaults, + .get_properties = vaapi_properties, + .get_extra_data = vaapi_extra_data, + .get_sei_data = vaapi_sei_data, + .get_video_info = vaapi_video_info +}; + +#endif diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c index 3ccef9baa..fb04c4cf0 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg.c @@ -2,12 +2,17 @@ #include #include #include +#include #include #include #include OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-ffmpeg", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "FFmpeg based sources/outputs/encoders"; +} extern struct obs_source_info ffmpeg_source; extern struct obs_output_info ffmpeg_output; @@ -17,6 +22,14 @@ extern struct obs_encoder_info aac_encoder_info; extern struct obs_encoder_info opus_encoder_info; extern struct obs_encoder_info nvenc_encoder_info; +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 27, 100) +#define LIBAVUTIL_VAAPI_AVAILABLE +#endif + +#ifdef LIBAVUTIL_VAAPI_AVAILABLE +extern struct obs_encoder_info vaapi_encoder_info; +#endif + static DARRAY(struct log_context { void *context; char str[4096]; @@ -158,6 +171,14 @@ static bool nvenc_supported(void) #endif +#ifdef LIBAVUTIL_VAAPI_AVAILABLE +static bool vaapi_supported(void) +{ + AVCodec *vaenc = avcodec_find_encoder_by_name("h264_vaapi"); + return !!vaenc; +} +#endif + bool obs_module_load(void) { da_init(active_log_contexts); @@ -176,6 +197,12 @@ bool obs_module_load(void) blog(LOG_INFO, "NVENC supported"); obs_register_encoder(&nvenc_encoder_info); } +#if !defined(_WIN32) && defined(LIBAVUTIL_VAAPI_AVAILABLE) + if (vaapi_supported()) { + blog(LOG_INFO, "FFMPEG VAAPI supported"); + obs_register_encoder(&vaapi_encoder_info); + } +#endif #endif return true; } diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index ec4289cc7..23a985425 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -1,13 +1,23 @@ project(obs-filters) -find_package(Libspeexdsp QUIET) -if(LIBSPEEXDSP_FOUND) - set(obs-filters_LIBSPEEXDSP_SOURCES - noise-suppress-filter.c) - set(obs-filters_LIBSPEEXDSP_LIBRARIES - ${LIBSPEEXDSP_LIBRARIES}) +option(DISABLE_SPEEXDSP "Disable building of the SpeexDSP-based Noise Suppression filter" OFF) + +if(DISABLE_SPEEXDSP) + message(STATUS "SpeexDSP support disabled") + set(LIBSPEEXDSP_FOUND FALSE) else() - message(STATUS "Speexdsp library not found, speexdsp filters disabled") + find_package(Libspeexdsp QUIET) + + if(NOT LIBSPEEXDSP_FOUND) + message(STATUS "SpeexDSP support not found") + set(LIBSPEEXDSP_FOUND FALSE) + else() + message(STATUS "SpeexDSP supported") + set(obs-filters_LIBSPEEXDSP_SOURCES + noise-suppress-filter.c) + set(obs-filters_LIBSPEEXDSP_LIBRARIES + ${LIBSPEEXDSP_LIBRARIES}) + endif() endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/obs-filters-config.h.in" @@ -38,6 +48,7 @@ set(obs-filters_SOURCES gain-filter.c noise-gate-filter.c mask-filter.c + invert-audio-polarity.c compressor-filter.c) add_library(obs-filters MODULE diff --git a/plugins/obs-filters/color-correction-filter.c b/plugins/obs-filters/color-correction-filter.c index e418d231e..fad36a681 100644 --- a/plugins/obs-filters/color-correction-filter.c +++ b/plugins/obs-filters/color-correction-filter.c @@ -351,16 +351,16 @@ static obs_properties_t *color_correction_filter_properties(void *data) obs_properties_t *props = obs_properties_create(); obs_properties_add_float_slider(props, SETTING_GAMMA, - TEXT_GAMMA, -3.0f, 3.0f, 0.01f); + TEXT_GAMMA, -3.0, 3.0, 0.01); obs_properties_add_float_slider(props, SETTING_CONTRAST, - TEXT_CONTRAST, -2.0f, 2.0f, 0.01f); + TEXT_CONTRAST, -2.0, 2.0, 0.01); obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, - TEXT_BRIGHTNESS, -1.0f, 1.0f, 0.01f); + TEXT_BRIGHTNESS, -1.0, 1.0, 0.01); obs_properties_add_float_slider(props, SETTING_SATURATION, - TEXT_SATURATION, -1.0f, 5.0f, 0.01f); + TEXT_SATURATION, -1.0, 5.0, 0.01); obs_properties_add_float_slider(props, SETTING_HUESHIFT, - TEXT_HUESHIFT, -180.0f, 180.0f, 0.01f); + TEXT_HUESHIFT, -180.0, 180.0, 0.01); obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, 0, 100, 1); diff --git a/plugins/obs-filters/compressor-filter.c b/plugins/obs-filters/compressor-filter.c index 3c3789f4b..244a9bcd9 100644 --- a/plugins/obs-filters/compressor-filter.c +++ b/plugins/obs-filters/compressor-filter.c @@ -40,12 +40,12 @@ #define TEXT_OUTPUT_GAIN MT_("Compressor.OutputGain") #define TEXT_SIDECHAIN_SOURCE MT_("Compressor.SidechainSource") -#define MIN_RATIO 1.0f -#define MAX_RATIO 32.0f -#define MIN_THRESHOLD_DB -60.0f +#define MIN_RATIO 1.0 +#define MAX_RATIO 32.0 +#define MIN_THRESHOLD_DB -60.0 #define MAX_THRESHOLD_DB 0.0f -#define MIN_OUTPUT_GAIN_DB -32.0f -#define MAX_OUTPUT_GAIN_DB 32.0f +#define MIN_OUTPUT_GAIN_DB -32.0 +#define MAX_OUTPUT_GAIN_DB 32.0 #define MIN_ATK_RLS_MS 1 #define MAX_RLS_MS 1000 #define MAX_ATK_MS 500 @@ -442,6 +442,9 @@ static struct obs_audio_data *compressor_filter_audio(void *data, struct compressor_data *cd = data; const uint32_t num_samples = audio->frames; + if (num_samples == 0) + return audio; + float **samples = (float**)audio->data; pthread_mutex_lock(&cd->sidechain_update_mutex); @@ -497,15 +500,15 @@ static obs_properties_t *compressor_properties(void *data) parent = obs_filter_get_parent(cd->context); obs_properties_add_float_slider(props, S_RATIO, - TEXT_RATIO, MIN_RATIO, MAX_RATIO, 0.5f); + TEXT_RATIO, MIN_RATIO, MAX_RATIO, 0.5); obs_properties_add_float_slider(props, S_THRESHOLD, - TEXT_THRESHOLD, MIN_THRESHOLD_DB, MAX_THRESHOLD_DB, 0.1f); + TEXT_THRESHOLD, MIN_THRESHOLD_DB, MAX_THRESHOLD_DB, 0.1); obs_properties_add_int_slider(props, S_ATTACK_TIME, TEXT_ATTACK_TIME, MIN_ATK_RLS_MS, MAX_ATK_MS, 1); obs_properties_add_int_slider(props, S_RELEASE_TIME, TEXT_RELEASE_TIME, MIN_ATK_RLS_MS, MAX_RLS_MS, 1); obs_properties_add_float_slider(props, S_OUTPUT_GAIN, - TEXT_OUTPUT_GAIN, MIN_OUTPUT_GAIN_DB, MAX_OUTPUT_GAIN_DB, 0.1f); + TEXT_OUTPUT_GAIN, MIN_OUTPUT_GAIN_DB, MAX_OUTPUT_GAIN_DB, 0.1); obs_property_t *sources = obs_properties_add_list(props, S_SIDECHAIN_SOURCE, TEXT_SIDECHAIN_SOURCE, diff --git a/plugins/obs-filters/data/chroma_key_filter.effect b/plugins/obs-filters/data/chroma_key_filter.effect index 8a75968d7..86280702d 100644 --- a/plugins/obs-filters/data/chroma_key_filter.effect +++ b/plugins/obs-filters/data/chroma_key_filter.effect @@ -55,17 +55,15 @@ float4 SampleTexture(float2 uv) float GetBoxFilteredChromaDist(float3 rgb, float2 texCoord) { - float distVal = GetChromaDist(rgb); - distVal += GetChromaDist(SampleTexture(texCoord-pixel_size).rgb); - distVal += GetChromaDist(SampleTexture(texCoord-float2(pixel_size.x, 0.0)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord-float2(pixel_size.x, -pixel_size.y)).rgb); - - distVal += GetChromaDist(SampleTexture(texCoord-float2(0.0, pixel_size.y)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord+float2(0.0, pixel_size.y)).rgb); - - distVal += GetChromaDist(SampleTexture(texCoord+float2(pixel_size.x, -pixel_size.y)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord+float2(pixel_size.x, 0.0)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord+pixel_size).rgb); + float2 h_pixel_size = pixel_size / 2.0; + float2 point_0 = float2(pixel_size.x, h_pixel_size.y); + float2 point_1 = float2(h_pixel_size.x, -pixel_size.y); + float distVal = GetChromaDist(SampleTexture(texCoord-point_0).rgb); + distVal += GetChromaDist(SampleTexture(texCoord+point_0).rgb); + distVal += GetChromaDist(SampleTexture(texCoord-point_1).rgb); + distVal += GetChromaDist(SampleTexture(texCoord+point_1).rgb); + distVal *= 2.0; + distVal += GetChromaDist(rgb); return distVal / 9.0; } @@ -76,6 +74,7 @@ float4 ProcessChromaKey(float4 rgba, VertData v_in) float fullMask = pow(saturate(baseMask / smoothness), 1.5); float spillVal = pow(saturate(baseMask / spill), 1.5); + rgba.rgba *= color; rgba.a *= fullMask; float desat = (rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722); @@ -86,7 +85,7 @@ float4 ProcessChromaKey(float4 rgba, VertData v_in) float4 PSChromaKeyRGBA(VertData v_in) : TARGET { - float4 rgba = image.Sample(textureSampler, v_in.uv) * color; + float4 rgba = image.Sample(textureSampler, v_in.uv); return ProcessChromaKey(rgba, v_in); } diff --git a/plugins/obs-filters/data/locale/el-GR.ini b/plugins/obs-filters/data/locale/el-GR.ini index 54d2059e5..dd23c14e0 100644 --- a/plugins/obs-filters/data/locale/el-GR.ini +++ b/plugins/obs-filters/data/locale/el-GR.ini @@ -1,15 +1,25 @@ ColorFilter="Διόρθωση Χρώματος" ColorGradeFilter="Εφαρμογή LUT" +MaskFilter="Μάσκα εικόνα/μείγμα" AsyncDelayFilter="Καθυστέρηση Βίντεο (Ασύγχρονη)" +CropFilter="Αποκοπή/Pad" ScrollFilter="Κύλιση" ChromaKeyFilter="Κλειδί Chroma" ColorKeyFilter="Κλειδί Χρώματος" SharpnessFilter="Όξυνση" +ScaleFilter="Κλιμάκωση/αναλογίες" +GPUDelayFilter="Καταστήσει καθυστέρηση" +UndistortCenter="Undistort κέντρο της εικόνας κατά την κλιμάκωση από ultrawide" +NoiseGate="Πύλη θορύβου" +NoiseSuppress="Καταστολή θορύβου" Gain="Απολαβή" DelayMs="Καθυστέρηση (χιλιοστά του δευτερολέπτου)" Type="Τύπος" MaskBlendType.MaskColor="Μάσκα Άλφα (Κανάλι Χρώματος)" MaskBlendType.MaskAlpha="Μάσκα Άλφα (Κανάλι Άλφα)" +MaskBlendType.BlendMultiply="Ανάμιξη (πολλαπλασιάζονται)" +MaskBlendType.BlendAddition="Μείγμα (Προσθήκη)" +MaskBlendType.BlendSubtraction="Μείγμα (αφαίρεση)" Path="Διαδρομή" Color="Χρώμα" Opacity="Αδιαφάνεια" @@ -18,8 +28,11 @@ Brightness="Φωτεινότητα" Gamma="Γάμμα" BrowsePath.Images="Όλα τα αρχεία εικόνας" BrowsePath.AllFiles="Όλα τα αρχεία" +KeyColorType="Βασικό χρώμα τύπου" +KeyColor="Βασικό χρώμα" Similarity="Ομοιότητα (1-1000)" Smoothness="Ομαλότητα (1-1000)" +ColorSpillReduction="Βασικό χρώμα πετρελαιοκηλίδα μείωση (1-1000)" Crop.Left="Αριστερά" Crop.Right="Δεξιά" Crop.Top="Πάνω" @@ -36,15 +49,29 @@ Red="Κόκκινο" Green="Πράσινο" Blue="Μπλε" Magenta="Ματζέντα" +NoiseGate.OpenThreshold="Ανοικτό όριο (dB)" +NoiseGate.CloseThreshold="Κλείστό όριο (dB)" NoiseGate.AttackTime="Χρόνος προσβολής (msec)" NoiseGate.HoldTime="Χρόνος αναμονής (msec)" NoiseGate.ReleaseTime="Χρόνος διάχυσης (msec)" Gain.GainDB="Απολαβή (dB)" +StretchImage="Τέντωση εικόνας (απόρριψη αναλογίας εικόνας)" Resolution="Ανάλυση" None="Καμία" +ScaleFiltering="Κλίμακα φιλτραρίσματος" +ScaleFiltering.Point="Σημείο" +ScaleFiltering.Bilinear="Διγραμμικές" +ScaleFiltering.Bicubic="Δικυβική" +ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="Καταστολή επιπέδου (dB)" Saturation="Κορεσμός" HueShift="Μετατόπιση Απόχρωσης" Amount="Ποσό" +Compressor="Συμπιεστής" Compressor.Ratio="Αναλογία (X:1)" Compressor.Threshold="Κατώφλι (dB)" +Compressor.AttackTime="Επίθεση (ms)" +Compressor.ReleaseTime="Απελευθέρωση (ms)" +Compressor.OutputGain="Εξόδου κέρδος (dB)" +Compressor.SidechainSource="Πηγή sidechain/βουτιά" diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index 241cbeef1..7ebaf97b7 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Render Delay" UndistortCenter="Undistort center of image when scaling from ultrawide" NoiseGate="Noise Gate" NoiseSuppress="Noise Suppression" +InvertPolarity="Invert Polarity" Gain="Gain" DelayMs="Delay (milliseconds)" Type="Type" @@ -57,6 +58,7 @@ NoiseGate.ReleaseTime="Release Time (milliseconds)" Gain.GainDB="Gain (dB)" StretchImage="Stretch Image (discard image aspect ratio)" Resolution="Resolution" +Base.Canvas="Base (Canvas) Resolution" None="None" ScaleFiltering="Scale Filtering" ScaleFiltering.Point="Point" diff --git a/plugins/obs-filters/data/locale/fil-PH.ini b/plugins/obs-filters/data/locale/fil-PH.ini new file mode 100644 index 000000000..6fe2d8875 --- /dev/null +++ b/plugins/obs-filters/data/locale/fil-PH.ini @@ -0,0 +1,77 @@ +ColorFilter="Pagtama ng Kulay" +ColorGradeFilter="Mag-aplay ng LUT" +MaskFilter="Imahe ng Maskara/Timpla" +AsyncDelayFilter="Naantalang bidyo (Async)" +CropFilter="I-Crop/Pad" +ScrollFilter="Mag-scroll" +ChromaKeyFilter="Susi ng Chroma" +ColorKeyFilter="Susi ng Kulay" +SharpnessFilter="I-Sharpen" +ScaleFilter="I-Scaling/Ratio ng Aspeto" +GPUDelayFilter="I-Render Delay" +UndistortCenter="Undistort center ng imahe kapang nag scaling mula sa ultrawide" +NoiseGate="Ang Noise Gate" +NoiseSuppress="Ang Noise Suppression" +Gain="Ang Gain" +DelayMs="I-Delay (millisegundos)" +Type="I-Type" +MaskBlendType.MaskColor="I-Alpha Mask (Kulang ng Channel)" +MaskBlendType.MaskAlpha="I-Alpha Mask (Alpha ng Channel)" +MaskBlendType.BlendMultiply="I-Blend (Paramihin)" +MaskBlendType.BlendAddition="I-Blend (Dagdagan)" +MaskBlendType.BlendSubtraction="I-Blend (Bawasan)" +Path="Ang Landas" +Color="Kulay" +Opacity="Kalabuan" +Contrast="Kaibahan" +Brightness="Kaliwanagan" +Gamma="Ang Gamma" +BrowsePath.Images="Lahat ng Image Files" +BrowsePath.AllFiles="Lahat ng Files" +KeyColorType="Susi ng Color Type" +KeyColor="Susi ng Kulay" +Similarity="Ang pagkapareho (1-1000)" +Smoothness="Ang pagkakinis (1-1000)" +ColorSpillReduction="Ang pagbawas ng Key Color Spill (1-1000)" +Crop.Left="Kaliwa" +Crop.Right="Kanan" +Crop.Top="Itaas" +Crop.Bottom="Ibaba" +Crop.Width="Ang Lapad" +Crop.Height="Ang Taas" +Crop.Relative="Ang Relative" +ScrollFilter.SpeedX="Ang Horizontal Speed" +ScrollFilter.SpeedY="Ang Vertical Speed" +ScrollFilter.LimitWidth="Limitasyon ng Lapad" +ScrollFilter.LimitHeight="Limitasyon ng Taas" +CustomColor="Pagsadya ng kulay" +Red="Pula" +Green="Berde" +Blue="Asul" +Magenta="Magenta" +NoiseGate.OpenThreshold="Buksan ang Threshold (dB)" +NoiseGate.CloseThreshold="Isarado ang Threshold (dB)" +NoiseGate.AttackTime="Oras ng Pag-atake (milsegudo)" +NoiseGate.HoldTime="Paghawak ng Oras (milsegudo)" +NoiseGate.ReleaseTime="Paglabas ng Oras (milsegudo)" +Gain.GainDB="Makakuha (dB)" +StretchImage="Mag-stretch ng Imahe (itapon ang imahe ng anyong panumbasan)" +Resolution="Resolusyon" +None="Wala" +ScaleFiltering="Iskala ng Pagsasala" +ScaleFiltering.Point="Punto" +ScaleFiltering.Bilinear="Bilinir" +ScaleFiltering.Bicubic="Bikyubik" +ScaleFiltering.Lanczos="Lankzos" +NoiseSuppress.SuppressLevel="Antas ng Pagpigil (dB)" +Saturation="Pagbababad" +HueShift="Kulay ng pagririlyebo" +Amount="Halaga" +Compressor="Kompresor" +Compressor.Ratio="Tumbasan (X:1)" +Compressor.Threshold="Threshold (dB)" +Compressor.AttackTime="Atake (ms)" +Compressor.ReleaseTime="Paglabas (ms)" +Compressor.OutputGain="Makakuha ng Awput (dB)" +Compressor.SidechainSource="Saydcheyn/Pinagmulan ng Daking" + diff --git a/plugins/obs-filters/data/locale/gd-GB.ini b/plugins/obs-filters/data/locale/gd-GB.ini new file mode 100644 index 000000000..2f94ad5d8 --- /dev/null +++ b/plugins/obs-filters/data/locale/gd-GB.ini @@ -0,0 +1,26 @@ +ScrollFilter="Sgrolaich" +SharpnessFilter="Geuraich" +Type="Seòrsa" +Path="Slighe" +Color="Dath" +Contrast="Iomsgaradh" +Brightness="Soilleireachd" +BrowsePath.AllFiles="A h-uile faidhle" +Crop.Left="Clì" +Crop.Right="Deas" +Crop.Top="Barr" +Crop.Bottom="Bonn" +Crop.Width="Leud" +Crop.Height="Àirde" +Red="Dearg" +Green="Uaine" +Blue="Gorm" +Resolution="Dùmhlachd-bhreacaidh" +None="Chan eil gin" +ScaleFiltering="Criathradh sgèilidh" +ScaleFiltering.Point="Puing" +ScaleFiltering.Bilinear="Dà-loidhneach" +ScaleFiltering.Bicubic="Dà-chiùbach" +ScaleFiltering.Lanczos="Lanczos" +Saturation="Sàthachd" + diff --git a/plugins/obs-filters/data/locale/ka-GE.ini b/plugins/obs-filters/data/locale/ka-GE.ini index 9b0122a1a..7547556c6 100644 --- a/plugins/obs-filters/data/locale/ka-GE.ini +++ b/plugins/obs-filters/data/locale/ka-GE.ini @@ -1,4 +1,77 @@ +ColorFilter="ფერთა გასწორება" +ColorGradeFilter="LUT-ის ასახვა" +MaskFilter="სურათის ნიღაბი/შერევა" +AsyncDelayFilter="ვიდეოს დაყოვნება (ასინქრონული)" +CropFilter="შემოჭრა/არეები" +ScrollFilter="გადაადგილება" +ChromaKeyFilter="ფონის ჩანაცვლება (Chroma Key)" +ColorKeyFilter="ფერის ჩანაცვლება (Color Key)" +SharpnessFilter="სიმკვეთრის მომატება" +ScaleFilter="ზომების ცვლილება/გვერდების თანაფარდობა" +GPUDelayFilter="დაყოვნება დამუშავებისას" +UndistortCenter="ზეფართო სურათის შუაგულის გამრუდების არიდება, ზომების შეცვლისას" +NoiseGate="ხმაურის შეზღუდვა" +NoiseSuppress="ხმაურის დახშობა" +Gain="სიგნალის გაძლიერება" +DelayMs="დაყოვნება (მილიწამი)" +Type="სახეობა" +MaskBlendType.MaskColor="ალფა-ნიღაბი (ფერის არხი)" +MaskBlendType.MaskAlpha="ალფა-ნიღაბი (გამჭვირვალობის არხი)" +MaskBlendType.BlendMultiply="შერევა (გამრავლება)" +MaskBlendType.BlendAddition="შერევა (დამატება)" +MaskBlendType.BlendSubtraction="შერევა (გამოკლება)" +Path="მისამართი" +Color="ფერი" +Opacity="გაუმჭვირვალობა" +Contrast="კონტრასტი" +Brightness="სიკაშკაშე" +Gamma="ფერთა გამა" +BrowsePath.Images="ყველანაირი სურათი" +BrowsePath.AllFiles="ყველა ფაილი" +KeyColorType="საკვანძო ფერის სახე" +KeyColor="საკვანძო ფერი" +Similarity="მსგავსება (1-1000)" +Smoothness="სიგლუვე (1-1000)" +ColorSpillReduction="საკვანძო ფერთა გაბნევის შემცირება (1-1000)" +Crop.Left="მარცხენა" +Crop.Right="მარჯვენა" +Crop.Top="ზედა" +Crop.Bottom="ქვედა" +Crop.Width="სიგანე" +Crop.Height="სიმაღლე" +Crop.Relative="თანაფარდობა" +ScrollFilter.SpeedX="თარაზული სიჩქარე" +ScrollFilter.SpeedY="შვეული სიჩქარე" +ScrollFilter.LimitWidth="სიგანის შეზღუდვა" +ScrollFilter.LimitHeight="სიმაღლის შეზღუდვა" +CustomColor="მითითებული ფერი" Red="წითელი" Green="მწვანე" Blue="ლურჯი" +Magenta="მეწამული" +NoiseGate.OpenThreshold="ქვედა ზღურბლი (dB)" +NoiseGate.CloseThreshold="ზედა ზღურბლი (dB)" +NoiseGate.AttackTime="მომატების (Attack) ხანგრძლივობა (მილიწამი)" +NoiseGate.HoldTime="დაყოვნების (Hold) ხანგრძლივობა (მილიწამი)" +NoiseGate.ReleaseTime="შემცირების (Release) ხანგრძლივობა (მილიწამი)" +Gain.GainDB="გაძლიერება (dB)" +StretchImage="სურათის გაწელვა (გვერდების თანაფარდობის უგულებელყოფა)" +Resolution="გაფართოება" +None="არცერთი" +ScaleFiltering="მასშტაბირების ფილტრი" +ScaleFiltering.Point="წერტილოვანი" +ScaleFiltering.Bilinear="ორხაზოვანი" +ScaleFiltering.Bicubic="ბიკუბური" +ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="დახშობის ხარისხი (dB)" +Saturation="გაჯერებულობა" +HueShift="შეფერილობის შეცვლა" +Amount="რაოდენობა" +Compressor="დამხშობი" +Compressor.Ratio="ფარდობა (X:1)" +Compressor.Threshold="ზღურბლი (dB)" +Compressor.AttackTime="მომატება (მწ)" +Compressor.ReleaseTime="შემცირება (მწ)" +Compressor.OutputGain="გამომავალი სიგნალის გაძლიერება (dB)" +Compressor.SidechainSource="Sidechain/ხმის დონის დადაბლების წყარო" diff --git a/plugins/obs-filters/data/locale/ru-RU.ini b/plugins/obs-filters/data/locale/ru-RU.ini index ddc0c3ec5..1fe92efbc 100644 --- a/plugins/obs-filters/data/locale/ru-RU.ini +++ b/plugins/obs-filters/data/locale/ru-RU.ini @@ -2,12 +2,12 @@ ColorFilter="Коррекция цвета" ColorGradeFilter="Применить LUT" MaskFilter="Маска изображения/Смешивание" AsyncDelayFilter="Задержка видео (асинхронность)" -CropFilter="Кадрировать" +CropFilter="Кадрирование" ScrollFilter="Прокрутка" ChromaKeyFilter="Хромакей" ColorKeyFilter="Цветовой ключ" SharpnessFilter="Увеличить резкость" -ScaleFilter="Коэффициент Масштабирования/Аспект" +ScaleFilter="Масштабирование/Соотношение сторон" GPUDelayFilter="Задержка отображения" UndistortCenter="Не искривлять центр изображения при масштабировании Ultrawide разрешения" NoiseGate="Пропускной уровень шума" @@ -58,10 +58,10 @@ Gain.GainDB="Усиление (дБ)" StretchImage="Растянуть изображение (игнорировать пропорции изображения)" Resolution="Разрешение" None="Нет" -ScaleFiltering="Масштаб Фильтрации" -ScaleFiltering.Point="Точечная" -ScaleFiltering.Bilinear="Билинейная" -ScaleFiltering.Bicubic="Бикубическая" +ScaleFiltering="Фильтр масштабирования" +ScaleFiltering.Point="Точечный" +ScaleFiltering.Bilinear="Билинейный" +ScaleFiltering.Bicubic="Бикубический" ScaleFiltering.Lanczos="Метод Ланцоша" NoiseSuppress.SuppressLevel="Уровень подавления (дБ)" Saturation="Насыщенность" diff --git a/plugins/obs-filters/data/locale/tl-PH.ini b/plugins/obs-filters/data/locale/tl-PH.ini new file mode 100644 index 000000000..be67b67cf --- /dev/null +++ b/plugins/obs-filters/data/locale/tl-PH.ini @@ -0,0 +1,77 @@ +ColorFilter="Koreksyon ng mga Kulay" +ColorGradeFilter="I-apply ang LUT" +MaskFilter="Imahe ng Mask/Blend" +AsyncDelayFilter="I-delay ang Video (Async)" +CropFilter="I-crop/Pad" +ScrollFilter="I-scroll" +ChromaKeyFilter="Ang Chroma Key" +ColorKeyFilter="Kulay ng mga Key" +SharpnessFilter="Patalasan pa" +ScaleFilter="Scaling/Aspect Ratio" +GPUDelayFilter="Mad-render ng Delay" +UndistortCenter="Ang sentro ay i-undistort kapag ang scaling ay mula sa ultrawide" +NoiseGate="Noise Gate" +NoiseSuppress="Pagtimpiin ang tunog" +Gain="Napunan" +DelayMs="I-delay (millisegundos)" +Type="Tipo" +MaskBlendType.MaskColor="Alpha Mask (Kulay ng Channel)" +MaskBlendType.MaskAlpha="Alpha Mask (Alpha Channel)" +MaskBlendType.BlendMultiply="I-blend (pagpaparami)" +MaskBlendType.BlendAddition="I-blend (pagsamahin)" +MaskBlendType.BlendSubtraction="I-blend (paghiwalayin)" +Path="Daanan" +Color="Kulay" +Opacity="Opacity" +Contrast="Kaibahan" +Brightness="Gaano kaliwanag" +Gamma="Gamma" +BrowsePath.Images="Lahat ng mga Imahen sa File" +BrowsePath.AllFiles="Lahat ng mga Files" +KeyColorType="Tipo ng Kulay sa Key" +KeyColor="Kulay ng Key" +Similarity="Kapareho (1-1000)" +Smoothness="Kakinisan (1-1000)" +ColorSpillReduction="Kulay ng Key sa Spill Reduction (1-1000)" +Crop.Left="Kaliwa" +Crop.Right="Kanan" +Crop.Top="Itaas" +Crop.Bottom="Ibaba" +Crop.Width="Lapad" +Crop.Height="Taas" +Crop.Relative="Kaugnayan" +ScrollFilter.SpeedX="Pahigang Bilis" +ScrollFilter.SpeedY="Patayong Bilis" +ScrollFilter.LimitWidth="Limitasyon ng Lapad" +ScrollFilter.LimitHeight="Limitasyon sa Haba" +CustomColor="Custom na Kulay" +Red="Pula" +Green="Kulay Luntian" +Blue="Bughaw" +Magenta="Magenta" +NoiseGate.OpenThreshold="I-open ang Threshold (dB)" +NoiseGate.CloseThreshold="Isarado ang Threshold (dB)" +NoiseGate.AttackTime="Atakihin ang Oras (millisegundo)" +NoiseGate.HoldTime="Pigilin ang Oras (millisegundo)" +NoiseGate.ReleaseTime="Pakawalan ang Oras (millisegundo)" +Gain.GainDB="Napunan (dB)" +StretchImage="I-stretch ang Imahe (baliwalain ang imahe mula sa aspect ratio)" +Resolution="Resulosyon" +None="Wala" +ScaleFiltering="I-filter ang Scale" +ScaleFiltering.Point="Tuldok" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="Lebel ng Suppression (dB)" +Saturation="Saturation" +HueShift="Hue Shift" +Amount="Ang Halaga" +Compressor="Ang Comppressor" +Compressor.Ratio="Proporsyon (X:1)" +Compressor.Threshold="Threshold (dB)" +Compressor.AttackTime="Atake (ms)" +Compressor.ReleaseTime="Pakawalan (ms)" +Compressor.OutputGain="Ang nadagdag sa panlabas (dB)" +Compressor.SidechainSource="Pinagmulan ng Sidechain/Ducking" + diff --git a/plugins/obs-filters/data/locale/vi-VN.ini b/plugins/obs-filters/data/locale/vi-VN.ini index b4beb14f8..dd770dcb8 100644 --- a/plugins/obs-filters/data/locale/vi-VN.ini +++ b/plugins/obs-filters/data/locale/vi-VN.ini @@ -23,6 +23,7 @@ Red="Đỏ" Green="Xanh" Blue="Xanh nước biển" Magenta="Đỏ tươi" +Resolution="Độ phân giải" None="Không có" Saturation="Độ bão hoà" diff --git a/plugins/obs-filters/invert-audio-polarity.c b/plugins/obs-filters/invert-audio-polarity.c new file mode 100644 index 000000000..8c635085d --- /dev/null +++ b/plugins/obs-filters/invert-audio-polarity.c @@ -0,0 +1,49 @@ +#include + +static const char *invert_polarity_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("InvertPolarity"); +} + +static void invert_polarity_destroy(void *data) +{ + UNUSED_PARAMETER(data); +} + +static void *invert_polarity_create(obs_data_t *settings, obs_source_t *filter) +{ + UNUSED_PARAMETER(settings); + return filter; +} + +static struct obs_audio_data *invert_polarity_filter_audio(void *unused, + struct obs_audio_data *audio) +{ + float **adata = (float**)audio->data; + + for (size_t c = 0; c < MAX_AV_PLANES; c++) { + register float *channel_data = adata[c]; + register float *channel_end = channel_data + audio->frames; + + if (!channel_data) + break; + + while (channel_data < channel_end) { + *(channel_data++) *= -1.0f; + } + } + + UNUSED_PARAMETER(unused); + return audio; +} + +struct obs_source_info invert_polarity_filter = { + .id = "invert_polarity_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_AUDIO, + .get_name = invert_polarity_name, + .create = invert_polarity_create, + .destroy = invert_polarity_destroy, + .filter_audio = invert_polarity_filter_audio, +}; diff --git a/plugins/obs-filters/noise-gate-filter.c b/plugins/obs-filters/noise-gate-filter.c index 22033388f..0993e3bd7 100644 --- a/plugins/obs-filters/noise-gate-filter.c +++ b/plugins/obs-filters/noise-gate-filter.c @@ -41,8 +41,8 @@ struct noise_gate_data { float held_time; }; -#define VOL_MIN -96.0f -#define VOL_MAX 0.0f +#define VOL_MIN -96.0 +#define VOL_MAX 0.0 static const char *noise_gate_name(void *unused) { @@ -155,8 +155,8 @@ static struct obs_audio_data *noise_gate_filter_audio(void *data, static void noise_gate_defaults(obs_data_t *s) { - obs_data_set_default_double(s, S_OPEN_THRESHOLD, -26.0f); - obs_data_set_default_double(s, S_CLOSE_THRESHOLD, -32.0f); + obs_data_set_default_double(s, S_OPEN_THRESHOLD, -26.0); + obs_data_set_default_double(s, S_CLOSE_THRESHOLD, -32.0); obs_data_set_default_int (s, S_ATTACK_TIME, 25); obs_data_set_default_int (s, S_HOLD_TIME, 200); obs_data_set_default_int (s, S_RELEASE_TIME, 150); @@ -167,9 +167,9 @@ static obs_properties_t *noise_gate_properties(void *data) obs_properties_t *ppts = obs_properties_create(); obs_properties_add_float_slider(ppts, S_CLOSE_THRESHOLD, - TEXT_CLOSE_THRESHOLD, VOL_MIN, VOL_MAX, 1.0f); + TEXT_CLOSE_THRESHOLD, VOL_MIN, VOL_MAX, 1.0); obs_properties_add_float_slider(ppts, S_OPEN_THRESHOLD, - TEXT_OPEN_THRESHOLD, VOL_MIN, VOL_MAX, 1.0f); + TEXT_OPEN_THRESHOLD, VOL_MIN, VOL_MAX, 1.0); obs_properties_add_int(ppts, S_ATTACK_TIME, TEXT_ATTACK_TIME, 0, 10000, 1); obs_properties_add_int(ppts, S_HOLD_TIME, TEXT_HOLD_TIME, diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 02ac63d63..53cf16ad6 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -2,8 +2,11 @@ #include "obs-filters-config.h" OBS_DECLARE_MODULE() - OBS_MODULE_USE_DEFAULT_LOCALE("obs-filters", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "OBS core filters"; +} extern struct obs_source_info mask_filter; extern struct obs_source_info crop_filter; @@ -20,6 +23,7 @@ extern struct obs_source_info async_delay_filter; #if SPEEXDSP_ENABLED extern struct obs_source_info noise_suppress_filter; #endif +extern struct obs_source_info invert_polarity_filter; extern struct obs_source_info noise_gate_filter; extern struct obs_source_info compressor_filter; @@ -40,6 +44,7 @@ bool obs_module_load(void) #if SPEEXDSP_ENABLED obs_register_source(&noise_suppress_filter); #endif + obs_register_source(&invert_polarity_filter); obs_register_source(&noise_gate_filter); obs_register_source(&compressor_filter); return true; diff --git a/plugins/obs-filters/scale-filter.c b/plugins/obs-filters/scale-filter.c index 3a1cfef7d..b0d66d4fc 100644 --- a/plugins/obs-filters/scale-filter.c +++ b/plugins/obs-filters/scale-filter.c @@ -18,6 +18,7 @@ #define T_SAMPLING_BICUBIC obs_module_text("ScaleFiltering.Bicubic") #define T_SAMPLING_LANCZOS obs_module_text("ScaleFiltering.Lanczos") #define T_UNDISTORT obs_module_text("UndistortCenter") +#define T_BASE obs_module_text("Base.Canvas") #define S_SAMPLING_POINT "point" #define S_SAMPLING_BILINEAR "bilinear" @@ -42,6 +43,7 @@ struct scale_filter_data { bool target_valid; bool valid; bool undistort; + bool base_canvas_resolution; }; static const char *scale_filter_name(void *unused) @@ -59,18 +61,29 @@ static void scale_filter_update(void *data, obs_data_t *settings) const char *sampling = obs_data_get_string(settings, S_SAMPLING); filter->valid = true; + filter->base_canvas_resolution = false; - ret = sscanf(res_str, "%dx%d", &filter->cx_in, &filter->cy_in); - if (ret == 2) { + if (strcmp(res_str, T_BASE) == 0) { + struct obs_video_info ovi; + obs_get_video_info(&ovi); filter->aspect_ratio_only = false; + filter->base_canvas_resolution = true; + filter->cx_in = ovi.base_width; + filter->cy_in = ovi.base_height; } else { - ret = sscanf(res_str, "%d:%d", &filter->cx_in, &filter->cy_in); - if (ret != 2) { - filter->valid = false; - return; - } + ret = sscanf(res_str, "%dx%d", &filter->cx_in, &filter->cy_in); + if (ret == 2) { + filter->aspect_ratio_only = false; + } else { + ret = sscanf(res_str, "%d:%d", &filter->cx_in, + &filter->cy_in); + if (ret != 2) { + filter->valid = false; + return; + } - filter->aspect_ratio_only = true; + filter->aspect_ratio_only = true; + } } if (astrcmpi(sampling, S_SAMPLING_POINT) == 0) { @@ -126,6 +139,13 @@ static void scale_filter_tick(void *data, float seconds) int cx; int cy; + if (filter->base_canvas_resolution) { + struct obs_video_info ovi; + obs_get_video_info(&ovi); + filter->cx_in = ovi.base_width; + filter->cy_in = ovi.base_height; + } + target = obs_filter_get_target(filter->context); filter->cx_out = 0; filter->cy_out = 0; @@ -347,6 +367,7 @@ static obs_properties_t *scale_filter_properties(void *data) OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_NONE, T_NONE); + obs_property_list_add_string(p, T_BASE, T_BASE); for (size_t i = 0; i < NUM_ASPECTS; i++) obs_property_list_add_string(p, aspects[i], aspects[i]); diff --git a/plugins/obs-filters/scroll-filter.c b/plugins/obs-filters/scroll-filter.c index 2ad879305..9ff361f9b 100644 --- a/plugins/obs-filters/scroll-filter.c +++ b/plugins/obs-filters/scroll-filter.c @@ -121,10 +121,10 @@ static obs_properties_t *scroll_filter_properties(void *data) obs_properties_add_float_slider(props, "speed_x", obs_module_text("ScrollFilter.SpeedX"), - -500.0f, 500.0f, 1.0f); + -500.0, 500.0, 1.0); obs_properties_add_float_slider(props, "speed_y", obs_module_text("ScrollFilter.SpeedY"), - -500.0f, 500.0f, 1.0f); + -500.0, 500.0, 1.0); p = obs_properties_add_bool(props, "limit_cx", obs_module_text("ScrollFilter.LimitWidth")); diff --git a/plugins/obs-filters/sharpness-filter.c b/plugins/obs-filters/sharpness-filter.c index 0e7a79568..526961468 100644 --- a/plugins/obs-filters/sharpness-filter.c +++ b/plugins/obs-filters/sharpness-filter.c @@ -101,7 +101,7 @@ static obs_properties_t *sharpness_properties(void *data) obs_properties_t *props = obs_properties_create(); obs_properties_add_float_slider(props, "sharpness", - "Sharpness", 0.0f, 1.0f, 0.01f); + "Sharpness", 0.0, 1.0, 0.01); UNUSED_PARAMETER(data); return props; diff --git a/plugins/obs-libfdk/data/locale/fil-PH.ini b/plugins/obs-libfdk/data/locale/fil-PH.ini new file mode 100644 index 000000000..d4c346ee4 --- /dev/null +++ b/plugins/obs-libfdk/data/locale/fil-PH.ini @@ -0,0 +1,4 @@ +LibFDK="libfdk AAC Enkoder" +Bitrate="Bitreyt" +Afterburner="Paganhin ang AAC Afterburner" + diff --git a/plugins/obs-libfdk/data/locale/gd-GB.ini b/plugins/obs-libfdk/data/locale/gd-GB.ini new file mode 100644 index 000000000..042ecedbc --- /dev/null +++ b/plugins/obs-libfdk/data/locale/gd-GB.ini @@ -0,0 +1,4 @@ +LibFDK="Inneal-còdachaidh libfdk AAC" +Bitrate="Reat bhiotaichean" +Afterburner="Cuir an comas Afterburner AAC" + diff --git a/plugins/obs-libfdk/data/locale/ka-GE.ini b/plugins/obs-libfdk/data/locale/ka-GE.ini new file mode 100644 index 000000000..fbff316a1 --- /dev/null +++ b/plugins/obs-libfdk/data/locale/ka-GE.ini @@ -0,0 +1,4 @@ +LibFDK="libfdk AAC დამშიფრავი" +Bitrate="ბიტური სიხშირე" +Afterburner="AAC Afterburner-ის ჩართვა" + diff --git a/plugins/obs-libfdk/data/locale/tl-PH.ini b/plugins/obs-libfdk/data/locale/tl-PH.ini new file mode 100644 index 000000000..fe95a215e --- /dev/null +++ b/plugins/obs-libfdk/data/locale/tl-PH.ini @@ -0,0 +1,4 @@ +LibFDK="libfdk na AAC Encoder" +Bitrate="Ang Bitrate" +Afterburner="Paganahin ang AAC Afterburner" + diff --git a/plugins/obs-libfdk/data/locale/ur-PK.ini b/plugins/obs-libfdk/data/locale/ur-PK.ini index eb205a309..1655d2bb6 100644 --- a/plugins/obs-libfdk/data/locale/ur-PK.ini +++ b/plugins/obs-libfdk/data/locale/ur-PK.ini @@ -1,2 +1,4 @@ LibFDK="لابفدک AAC انکوڈر" +Bitrate="بٹ شرح" +Afterburner="بٹرا قابل اطلاق AAC بعدبورنر" diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index c1f940b68..62a2c805a 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -1,178 +1,205 @@ project(obs-outputs) -option(USE_SSL "Enable rtmps support with OpenSSL" OFF) +set(WITH_RTMPS AUTO CACHE STRING "Enable RTMPS support with mbedTLS") +set_property(CACHE WITH_RTMPS PROPERTY STRINGS AUTO ON OFF) -if (USE_SSL) - find_package(SSL QUIET) - find_package(ZLIB QUIET) +option(STATIC_MBEDTLS "Statically link mbedTLS into binary" OFF) + +if (WITH_RTMPS OR (WITH_RTMPS STREQUAL "AUTO")) + find_package(MbedTLS QUIET) + find_package(ZLIB QUIET) endif() -if (SSL_FOUND AND ZLIB_FOUND) - add_definitions(-DCRYPTO -DUSE_OPENSSL) - include_directories(${SSL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) +if (LIBMBEDTLS_FOUND AND ZLIB_FOUND) + add_definitions(-DCRYPTO -DUSE_MBEDTLS) + include_directories(${LIBMBEDTLS_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) else() - if (USE_SSL) - message(WARNING "SSL enabled by user, but OpenSSL was not found") - endif() - unset(SSL_LIBRARIES) - unset(ZLIB_LIBRARIES) - add_definitions(-DNO_CRYPTO) + if(WITH_RTMPS STREQUAL "AUTO") + message(WARNING "mbedTLS was not found, RTMPS will be auto-disabled") + elseif (WITH_RTMPS) + message(FATAL_ERROR "RTMPS enabled by user, but mbedTLS was not found") + endif() + unset(LIBMBEDTLS_LIBRARIES) + unset(ZLIB_LIBRARIES) + add_definitions(-DNO_CRYPTO) endif() # --- Find libwebrtc or fail early if( WIN32 ) - # NOTE ALEX: David claims that it does not work. - # the problem WOULD come from this call not going through and not - # setting the include dirs properly on windows. - # One should never manually set that variable to zero, but in this - # specific case this is the right way to approach the problem. - set(WEBRTC_USE_FILE_INCLUDED 0) + # NOTE ALEX: David claims that it does not work. + # the problem WOULD come from this call not going through and not + # setting the include dirs properly on windows. + # One should never manually set that variable to zero, but in this + # specific case this is the right way to approach the problem. + set(WEBRTC_USE_FILE_INCLUDED 0) endif() find_package(libwebrtc REQUIRED) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/CMakeLists.txt") - find_package(Libcurl REQUIRED) - - add_definitions(-DFTL_STATIC_COMPILE) - - include_directories(${LIBCURL_INCLUDE_DIRS}) - - set(ftl_SOURCES - ftl-stream.c - ftl-sdk/libftl/hmac/hmac.c - ftl-sdk/libftl/hmac/sha2.c - ftl-sdk/libftl/ftl-sdk.c - ftl-sdk/libftl/handshake.c - ftl-sdk/libftl/ingest.c - ftl-sdk/libftl/ftl_helpers.c - ftl-sdk/libftl/media.c - ftl-sdk/libftl/gettimeofday/gettimeofday.c - ftl-sdk/libftl/logging.c) - set(ftl_HEADERS - ftl-sdk/libftl/hmac/hmac.h - ftl-sdk/libftl/hmac/sha2.h - ftl-sdk/libftl/ftl.h - ftl-sdk/libftl/ftl_private.h) - set(ftl_IMPORTS - ${OBS_JANSSON_IMPORT} - ${LIBCURL_LIBRARIES}) - - if (WIN32) - list(APPEND ftl_SOURCES - ftl-sdk/libftl/win32/socket.c - ftl-sdk/libftl/gettimeofday/gettimeofday.c - ftl-sdk/libftl/win32/threads.c) - list(APPEND ftl_HEADERS - ftl-sdk/libftl/gettimeofday/gettimeofday.h - ftl-sdk/libftl/win32/threads.h) - - include_directories(ftl-sdk/libftl/win32) - else() - list(APPEND ftl_SOURCES - ftl-sdk/libftl/posix/socket.c - ftl-sdk/libftl/posix/threads.c) - list(APPEND ftl_HEADERS - ftl-sdk/libftl/posix/threads.h) - - include_directories(ftl-sdk/libftl/posix) - endif() - - include_directories(ftl-sdk/libftl) - - set(COMPILE_FTL TRUE) + find_package(Libcurl REQUIRED) + + add_definitions(-DFTL_STATIC_COMPILE) + + include_directories(${LIBCURL_INCLUDE_DIRS}) + + set(ftl_SOURCES + ftl-stream.c + ftl-sdk/libftl/hmac/hmac.c + ftl-sdk/libftl/hmac/sha2.c + ftl-sdk/libftl/ftl-sdk.c + ftl-sdk/libftl/handshake.c + ftl-sdk/libftl/ingest.c + ftl-sdk/libftl/ftl_helpers.c + ftl-sdk/libftl/media.c + ftl-sdk/libftl/gettimeofday/gettimeofday.c + ftl-sdk/libftl/logging.c) + set(ftl_HEADERS + ftl-sdk/libftl/hmac/hmac.h + ftl-sdk/libftl/hmac/sha2.h + ftl-sdk/libftl/ftl.h + ftl-sdk/libftl/ftl_private.h) + set(ftl_IMPORTS + ${OBS_JANSSON_IMPORT} + ${LIBCURL_LIBRARIES}) + + if (WIN32) + list(APPEND ftl_SOURCES + ftl-sdk/libftl/win32/socket.c + ftl-sdk/libftl/gettimeofday/gettimeofday.c + ftl-sdk/libftl/win32/threads.c) + list(APPEND ftl_HEADERS + ftl-sdk/libftl/gettimeofday/gettimeofday.h + ftl-sdk/libftl/win32/threads.h) + + include_directories(ftl-sdk/libftl/win32) + else() + list(APPEND ftl_SOURCES + ftl-sdk/libftl/posix/socket.c + ftl-sdk/libftl/posix/threads.c) + list(APPEND ftl_HEADERS + ftl-sdk/libftl/posix/threads.h) + + include_directories(ftl-sdk/libftl/posix) + endif() + + include_directories(ftl-sdk/libftl) + + set(COMPILE_FTL TRUE) else() - set(COMPILE_FTL FALSE) + set(COMPILE_FTL FALSE) endif() configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/obs-outputs-config.h.in" - "${CMAKE_BINARY_DIR}/plugins/obs-outputs/config/obs-outputs-config.h") + "${CMAKE_CURRENT_SOURCE_DIR}/obs-outputs-config.h.in" + "${CMAKE_BINARY_DIR}/plugins/obs-outputs/config/obs-outputs-config.h") include_directories("${CMAKE_BINARY_DIR}/plugins/obs-outputs/config") if(WIN32) -set( obs-outputs_PLATFORM_DEPS - ${WEBRTC_LIBRARIES} - ws2_32 - winmm - Iphlpapi -) - if(MSVC) - set( obs-outputs_PLATFORM_DEPS ${obs-outputs_PLATFORM_DEPS} w32-pthreads ) - endif() - elseif( APPLE ) + set(obs-outputs_PLATFORM_DEPS + ${WEBRTC_LIBRARIES} + ws2_32 + winmm + Iphlpapi) + + if (WITH_RTMPS OR (WITH_RTMPS STREQUAL "AUTO")) + SET(obs-outputs_PLATFORM_DEPS + ${obs-outputs_PLATFORM_DEPS} + crypt32) + endif() +endif() + +if(MSVC) + set(obs-outputs_PLATFORM_DEPS + ${obs-outputs_PLATFORM_DEPS} + w32-pthreads) +endif() + + +if(APPLE AND (WITH_RTMPS OR (WITH_RTMPS STREQUAL "AUTO"))) + find_library(FOUNDATION_FRAMEWORK Foundation) + find_library(SECURITY_FRAMEWORK Security) + + set(obs-outputs_PLATFORM_DEPS + ${WEBRTC_LIBRARIES} + ${obs-outputs_PLATFORM_DEPS} + ${FOUNDATION_FRAMEWORK} + ${SECURITY_FRAMEWORK}) +elseif(APPLE) set( obs-outputs_PLATFORM_DEPS ${WEBRTC_LIBRARIES} ) endif() set(obs-outputs_librtmp_HEADERS - librtmp/amf.h - librtmp/bytes.h - librtmp/cencode.h - librtmp/dh.h - librtmp/dhgroups.h - librtmp/handshake.h - librtmp/http.h - librtmp/log.h - librtmp/md5.h - librtmp/rtmp.h - librtmp/rtmp_sys.h) - + librtmp/amf.h + librtmp/bytes.h + librtmp/cencode.h + librtmp/dh.h + librtmp/dhgroups.h + librtmp/handshake.h + librtmp/http.h + librtmp/log.h + librtmp/md5.h + librtmp/rtmp.h + librtmp/rtmp_sys.h) set(obs-outputs_librtmp_SOURCES - librtmp/amf.c - librtmp/cencode.c - librtmp/hashswf.c - librtmp/log.c - librtmp/md5.c - librtmp/parseurl.c -librtmp/rtmp.c) - -# --- header and source files -set( obs-outputs_HEADERS - obs-output-ver.h - flv-mux.h - rtmp-helpers.h - rtmp-stream.h - janus-stream.h - spankchain-stream.h - millicast-stream.h - net-if.h - AudioDeviceModuleWrapper.h - VideoCapture.h - VideoCapturer.h - WebRTCStream.h - WebsocketClient.h - ) - -set( obs-outputs_SOURCES - obs-outputs.c - rtmp-stream.c - janus-stream.cpp - spankchain-stream.cpp - millicast-stream.cpp - rtmp-windows.c - AudioDeviceModuleWrapper.cpp - VideoCapturer.cpp - WebRTCStream.cpp - net-if.c - null-output.c - flv-output.c - flv-mux.c - ) - -# --- library target -add_library( obs-outputs MODULE - ${obs-outputs_SOURCES} - ${obs-outputs_HEADER} - ${obs-outputs_librtmp_SOURCES} - ${obs-outputs_librtmp_HEADERS} -) - -target_link_libraries( obs-outputs - ${SSL_LIBRARIES} - ${ZLIB_LIBRARIES} - ${obs-outputs_PLATFORM_DEPS} - libobs - websocketclient -) + librtmp/amf.c + librtmp/cencode.c + librtmp/hashswf.c + librtmp/log.c + librtmp/md5.c + librtmp/parseurl.c + librtmp/rtmp.c) + +if(NOT WIN32) + set_source_files_properties(${obs-outputs_librtmp_SOURCES} PROPERTIES + COMPILE_FLAGS "-fvisibility=hidden") +endif() + +set(obs-outputs_HEADERS + "${CMAKE_BINARY_DIR}/plugins/obs-outputs/config/obs-outputs-config.h" + obs-output-ver.h + rtmp-helpers.h + rtmp-stream.h + janus-stream.h + spankchain-stream.h + millicast-stream.h + net-if.h + AudioDeviceModuleWrapper.h + VideoCapture.h + VideoCapturer.h + WebRTCStream.h + WebsocketClient.h + flv-mux.h) + +set(obs-outputs_SOURCES + obs-outputs.c + null-output.c + rtmp-stream.c + janus-stream.cpp + spankchain-stream.cpp + millicast-stream.cpp + rtmp-windows.c + AudioDeviceModuleWrapper.cpp + VideoCapturer.cpp + WebRTCStream.cpp + flv-output.c + flv-mux.c + net-if.c) + +add_library(obs-outputs MODULE + ${ftl_SOURCES} + ${ftl_HEADERS} + ${obs-outputs_SOURCES} + ${obs-outputs_HEADERS} + ${obs-outputs_librtmp_SOURCES} + ${obs-outputs_librtmp_HEADERS}) +target_link_libraries(obs-outputs + ${SSL_LIBRARIES} + libobs + ${LIBMBEDTLS_LIBRARIES} + ${ZLIB_LIBRARIES} + ${ftl_IMPORTS} + ${obs-outputs_PLATFORM_DEPS} + websocketclient) install_obs_plugin_with_data(obs-outputs data) diff --git a/plugins/obs-outputs/WebRTCStream.cpp b/plugins/obs-outputs/WebRTCStream.cpp index 0cccde218..c50581c24 100644 --- a/plugins/obs-outputs/WebRTCStream.cpp +++ b/plugins/obs-outputs/WebRTCStream.cpp @@ -119,17 +119,17 @@ bool WebRTCStream::start(Type type) //Get connection properties url = obs_service_get_url(service); - try { - room = std::stoll(obs_service_get_room(service)); - } - catch (const std::invalid_argument& ia) { - error("Invalid room name (must be a positive integer number)"); - return false; - } - catch (const std::out_of_range& oor) { - error("Room name out of range (number too big)"); - return false; - } +// try { + room = obs_service_get_room(service); +// } +// catch (const std::invalid_argument& ia) { +// error("Invalid room name (must be a positive integer number)"); +// return false; +// } +// catch (const std::out_of_range& oor) { +// error("Room name out of range (number too big)"); +// return false; +// } const char *tmpString = obs_service_get_username(service); username = (NULL == tmpString ? "" : tmpString); @@ -230,7 +230,7 @@ bool WebRTCStream::start(Type type) return false; } //Log them - info("-connecting to [url:%s,room:%ld,username:%s,password:%s]", url.c_str(), room, username.c_str(), password.c_str()); + info("-connecting to [url:%s,room:%s,username:%s,password:%s]", url.c_str(), room.c_str(), username.c_str(), password.c_str()); //Connect client if (!client->connect(url, room, username, password, this)){ //Error @@ -457,15 +457,16 @@ void WebRTCStream::onAudioFrame(audio_data *frame) //bitrate and dropped_frame uint64_t WebRTCStream::getBitrate() { - rtc::scoped_refptr observerVideo (new rtc::RefCountedObject ()); - rtc::scoped_refptr observerAudio (new rtc::RefCountedObject ()); - - pc->GetStats (observerVideo, video_track, webrtc::PeerConnectionInterface::kStatsOutputLevelStandard); - pc->GetStats (observerAudio, audio_track, webrtc::PeerConnectionInterface::kStatsOutputLevelStandard); - - std::this_thread::sleep_for(std::chrono::milliseconds(2)); - - bitrate = observerVideo->BytesSent() + observerAudio->BytesSent(); +// rtc::scoped_refptr observerVideo (new rtc::RefCountedObject ()); +// rtc::scoped_refptr observerAudio (new rtc::RefCountedObject ()); +// +// pc->GetStats (observerVideo, video_track, webrtc::PeerConnectionInterface::kStatsOutputLevelStandard); +// pc->GetStats (observerAudio, audio_track, webrtc::PeerConnectionInterface::kStatsOutputLevelStandard); +// +// std::this_thread::sleep_for(std::chrono::milliseconds(2)); +// +// bitrate = observerVideo->BytesSent() + observerAudio->BytesSent(); +// info("BITRATE?%ld",bitrate); - return bitrate; + return 0; } diff --git a/plugins/obs-outputs/WebRTCStream.h b/plugins/obs-outputs/WebRTCStream.h index 8305c98ff..83ea8c602 100644 --- a/plugins/obs-outputs/WebRTCStream.h +++ b/plugins/obs-outputs/WebRTCStream.h @@ -117,7 +117,7 @@ class WebRTCStream : public rtc::RefCountedObject private: //Connection properties std::string url; - long long room; + std::string room; std::string username; std::string password; std::string codec; diff --git a/plugins/obs-outputs/WebsocketClient.h b/plugins/obs-outputs/WebsocketClient.h index be33417c1..186d42855 100644 --- a/plugins/obs-outputs/WebsocketClient.h +++ b/plugins/obs-outputs/WebsocketClient.h @@ -31,7 +31,7 @@ class WEBSOCKETCLIENT_API WebsocketClient virtual void onDisconnected() = 0; }; public: - virtual bool connect(std::string url, long long room, std::string username, std::string token, Listener* listener) = 0; + virtual bool connect(std::string url, std::string room, std::string username, std::string token, Listener* listener) = 0; virtual bool open(const std::string &sdp, const std::string& codec = "") = 0; virtual bool trickle(const std::string &mid, int index, const std::string &candidate, bool last) = 0; virtual bool disconnect(bool wait) = 0; diff --git a/plugins/obs-outputs/data/locale/ar-SA.ini b/plugins/obs-outputs/data/locale/ar-SA.ini index fe2e41f64..29c197c2b 100644 --- a/plugins/obs-outputs/data/locale/ar-SA.ini +++ b/plugins/obs-outputs/data/locale/ar-SA.ini @@ -2,4 +2,8 @@ RTMPStream="تيار RTMP" RTMPStream.DropThreshold="انخفاض البداية (مللي ثانية)" FLVOutput="اخراج الملف بصيغة FLV" FLVOutput.FilePath="مسار الملف" +Default="Default" + +ConnectionTimedOut="انتهت مهلة الاتصال. تأكد من أن قمت بتكوين خدمة البث صالحة ولا جدار الحماية بحظر الاتصال." +ConnectionReset="The connection was reset by the peer. This usually indicates internet connection problems between you and the streaming service." diff --git a/plugins/obs-outputs/data/locale/ca-ES.ini b/plugins/obs-outputs/data/locale/ca-ES.ini index dbeb5fc0e..1471d151b 100644 --- a/plugins/obs-outputs/data/locale/ca-ES.ini +++ b/plugins/obs-outputs/data/locale/ca-ES.ini @@ -4,3 +4,12 @@ FLVOutput="Sortida del fitxer FLV" FLVOutput.FilePath="Camí del fitxer" Default="Per defecte" +ConnectionTimedOut="S'ha esgotat el temps de la connexió. Assegureu-vos que ha configurat un servei de transmissió vàlid i cap tallafocs està bloquejant la connexió." +PermissionDenied="La connexió ha estat bloquejada. Comproveu la configuració del tallafocs o antivirus per assegurar-se que OBS disposa de llibertat absoluta a Internet." +ConnectionAborted="La connexió ha estat avortada. Normalment això indica que hi ha problemes de connexió entre el vostre equip i el servei de transmissió." +ConnectionReset="La connexió s'ha acabat. Normalment això indica que hi ha problemes de connexió entre el vostre equip i el servei de transmissió." +HostNotFound="Nom d'amfitrió no trobat. Assegureu-vos que hi hagi configurat un servidor de transmissió vàlid i que la seva connexió a Internet / DNS estiguin funcionant correctament." +NoData="Nom d'amfitrió trobat, però no hi ha dades del tipus sol·licitat. Això pot passar si heu enllaçat a una adreça IPv6 i el seu servei de transmissió només té adreces IPv4 (veure configuració / avançada)." +AddressNotAvailable="Direcció no disponible. Potser heu intentat enllaçar amb una adreça IP no vàlida (veure configuració / avançada)." +SSLCertVerifyFailed="El servidor RTMP ha enviat un certificat SSL no vàlid." + diff --git a/plugins/obs-outputs/data/locale/cs-CZ.ini b/plugins/obs-outputs/data/locale/cs-CZ.ini index 8d0f9ced5..e48340d20 100644 --- a/plugins/obs-outputs/data/locale/cs-CZ.ini +++ b/plugins/obs-outputs/data/locale/cs-CZ.ini @@ -4,3 +4,12 @@ FLVOutput="Výstup do FLV souboru" FLVOutput.FilePath="Cesta k souboru" Default="Výchozí" +ConnectionTimedOut="Vypršel časový limit připojení. Přesvědčte se, zda jste správně nastavili vysílací službu a že žádná brána firewall neblokuje připojení." +PermissionDenied="Připojení bylo zablokováno. Zkontrolujte, zda má OBS povolen přístup k internetu v nastavení své brány firewall / antiviru." +ConnectionAborted="Připojení bylo přerušeno. Toto obvykle znamená, že nastaly problémy s připojením mezi vámi a vysílací službou." +ConnectionReset="Připojení bylo resetováno druhou stranou. Toto obvykle znamená, že nastaly problémy s připojením mezi vámi a vysílací službou." +HostNotFound="Hostitel nebyl nalezen. Zkontrolujte, zda jste zadali správný vysílací server a že vaše připojení k internetu / DNS funguje jak má." +NoData="Hostitel byl nalezen, ale žádná data požadovaného typu. Toto se může stát, pokud používáte IPv6 adresu, ale vaše vysílací služba podporuje pouze připojení přes svou IPv4 adresu (viz. Nastavení / Rozšířené)." +AddressNotAvailable="Adresa není k dispozici. Možná jste se snažili použít chybnou IP adresu (viz. Nastavení / Rozšířené)." +SSLCertVerifyFailed="RTMP server odeslal neplatný SSL certifikát." + diff --git a/plugins/obs-outputs/data/locale/da-DK.ini b/plugins/obs-outputs/data/locale/da-DK.ini index 292cbf0af..eb3f97307 100644 --- a/plugins/obs-outputs/data/locale/da-DK.ini +++ b/plugins/obs-outputs/data/locale/da-DK.ini @@ -1,6 +1,15 @@ RTMPStream="RTMP Strøm" -RTMPStream.DropThreshold="Drop Tærskel (millisekunder)" +RTMPStream.DropThreshold="Tabstærskel (millisekunder)" FLVOutput="FLV File Output" FLVOutput.FilePath="Filsti" Default="Standard" +ConnectionTimedOut="Forbindelsen fik timeout. Tjek venligst at du har opsat en gyldig streaming-tjeneste og at ingen firewall blokerer forbindelsen." +PermissionDenied="Forbindelsen blev blokeret. Tjek venligst indstillingerne for firewall/antivirus for at sikre, at OBS har fuld adgang til Internet." +ConnectionAborted="Forbindelsen blev afbrudt. Dette indikerer typisk et problem med Internetforbindelsen mellem dig og streaming-tjenesten." +ConnectionReset="Forbindelsen blev afbrudt. Dette indikerer typisk et problem med Internetforbindelsen mellem dig og streaming-tjenesten." +HostNotFound="Værtsnavn ikke fundet. Tjek at du har angivet en gyldig streaming-server, og at din Internetforbindelse/DNS fungerer korrekt." +NoData="Værtsnavn fundet, men ingen data af den ønskede type. Dette kan forekomme, hvis du har tildelt en IPv6-adresse, og din streaming-tjeneste kun benytter IPv4-adresser (se Indstillinger/Avanceret)." +AddressNotAvailable="Adresse utilgængelig. Du kan have forsøgt at tildele en ugyldig IP-adresse (se Indstillinger/Avanceret)." +SSLCertVerifyFailed="RTMP-serveren har sendt et ugyldig SSL-certifikat." + diff --git a/plugins/obs-outputs/data/locale/de-DE.ini b/plugins/obs-outputs/data/locale/de-DE.ini index 766321061..031a6c0b3 100644 --- a/plugins/obs-outputs/data/locale/de-DE.ini +++ b/plugins/obs-outputs/data/locale/de-DE.ini @@ -4,3 +4,12 @@ FLVOutput="FLV Dateiausgabe" FLVOutput.FilePath="Dateipfad" Default="Standard" +ConnectionTimedOut="Zeitüberschreitung bei der Verbindung. Stellen Sie sicher, dass Sie einen gültigen Streaming-Service konfiguriert haben und keine Firewall die Verbindung blockiert." +PermissionDenied="Die Verbindung wurde blockiert. Überprüfen Sie Ihre Firewall / Anti-Virus-Einstellungen, um sicherzustellen, dass OBS vollen Internetzugang hat." +ConnectionAborted="Die Verbindung wurde abgebrochen. Dies bedeutet in der Regel Probleme mit der Internetverbindung zwischen Ihnen und dem Streaming-Dienst." +ConnectionReset="Die Verbindung wurde durch Kommunikationspartner zurückgesetzt. Dies bedeutet in der Regel Probleme mit der Internetverbindung zwischen Ihnen und dem Streaming-Dienst." +HostNotFound="Hostname nicht gefunden. Stellen Sie sicher, dass Sie einen gültigen Streaming-Server eingegeben haben und Ihre Internetverbindung / DNS korrekt arbeiten." +NoData="Hostname gefunden, aber keine Daten des angeforderten Typs. Dies kann auftreten, wenn Sie eine IPv6-Adresse verwenden und Ihr Streaming-Dienst nur über IPv4-Adressen verfügt (siehe Einstellungen / Erweitert)." +AddressNotAvailable="Adresse nicht Verfügbar. Sie haben möglicherweise versucht, eine ungültige IP-Adresse zu verwenden (siehe Einstellungen / Erweitert)." +SSLCertVerifyFailed="Der RTMP-Server hat ein ungültiges SSL-Zertifikat gesendet." + diff --git a/plugins/obs-outputs/data/locale/el-GR.ini b/plugins/obs-outputs/data/locale/el-GR.ini index e31000f5c..3b8cb1252 100644 --- a/plugins/obs-outputs/data/locale/el-GR.ini +++ b/plugins/obs-outputs/data/locale/el-GR.ini @@ -4,3 +4,11 @@ FLVOutput="FLV Αρχείο Εξόδου" FLVOutput.FilePath="Διαδρομή Αρχείου" Default="Προεπιλογή" +ConnectionTimedOut="Εξαντλήθηκε το χρονικό όριο της σύνδεσης. Βεβαιωθείτε ότι έχετε ρυθμίσει μια έγκυρη υπηρεσία συνεχούς ροής και το τείχος προστασίας δεν εμποδίζει τη σύνδεση." +PermissionDenied="Αποκλείστηκε η σύνδεση. Ελέγξτε το τείχος προστασίας / ρυθμίσεις προστασίας από ιούς για να βεβαιωθείτε οτι στο OBS επιτρέπεται να έχει πλήρης πρόσβαση στο διαδίκτυο." +ConnectionAborted="Η σύνδεση ματαιώθηκε. Αυτό συνήθως υποδεικνύει προβλήματα σύνδεσης στο διαδίκτυο ανάμεσα σε εσάς και την υπηρεσία συνεχούς ροής." +ConnectionReset="Η σύνδεση ήταν επαναφέρθηκε στον ομότιμο υπολογιστή. Αυτό συνήθως υποδεικνύει προβλήματα σύνδεσης στο διαδίκτυο ανάμεσα σε εσάς και την υπηρεσία συνεχούς ροής." +HostNotFound="Το όνομα του κεντρικού υπολογιστή δεν βρέθηκε. Βεβαιωθείτε ότι πληκτρολογήσατε έναν έγκυρο διακομιστή συνεχούς ροής και η σύνδεση στο διαδίκτυο / DNS λειτουργεί σωστά." +NoData="Το όνομα του κεντρικού υπολογιστή βρέθηκε, αλλά χωρίς δεδομένα του ζητούμενου τύπου. Αυτό μπορεί να συμβεί αν έχετε δεσμεύσει μια διεύθυνση IPv6 και για την υπηρεσία συνεχούς ροής μόνο διευθύνσεις IPv4 (ανατρέξτε στην ενότητα ρυθμίσεις)." +AddressNotAvailable="Η διεύθυνση δεν είναι διαθέσιμη. Μπορεί να έχετε δοκιμάσει να συνδεθείτε σε μια άκυρη διεύθυνση IP (βλ. ρυθμίσεις)." + diff --git a/plugins/obs-outputs/data/locale/en-US.ini b/plugins/obs-outputs/data/locale/en-US.ini index c28e54eb0..3a915ce78 100644 --- a/plugins/obs-outputs/data/locale/en-US.ini +++ b/plugins/obs-outputs/data/locale/en-US.ini @@ -3,3 +3,12 @@ RTMPStream.DropThreshold="Drop Threshold (milliseconds)" FLVOutput="FLV File Output" FLVOutput.FilePath="File Path" Default="Default" + +ConnectionTimedOut="The connection timed out. Make sure you've configured a valid streaming service and no firewall is blocking the connection." +PermissionDenied="The connection was blocked. Check your firewall / anti-virus settings to make sure OBS is allowed full internet access." +ConnectionAborted="The connection was aborted. This usually indicates internet connection problems between you and the streaming service." +ConnectionReset="The connection was reset by the peer. This usually indicates internet connection problems between you and the streaming service." +HostNotFound="Hostname not found. Make sure you entered a valid streaming server and your internet connection / DNS are working correctly." +NoData="Hostname found, but no data of the requested type. This can occur if you have bound to an IPv6 address and your streaming service only has IPv4 addresses (see Settings / Advanced)." +AddressNotAvailable="Address not available. You may have tried to bind to an invalid IP address (see Settings / Advanced)." +SSLCertVerifyFailed="The RTMP server sent an invalid SSL certificate." diff --git a/plugins/obs-outputs/data/locale/es-ES.ini b/plugins/obs-outputs/data/locale/es-ES.ini index 8286872e7..80a91bb70 100644 --- a/plugins/obs-outputs/data/locale/es-ES.ini +++ b/plugins/obs-outputs/data/locale/es-ES.ini @@ -4,3 +4,12 @@ FLVOutput="Archivo de salida FLV" FLVOutput.FilePath="Ruta de archivo" Default="Por defecto" +ConnectionTimedOut="Agotado el tiempo de la conexión. Asegúrese de que ha configurado un servicio de streaming válido y ningún firewall está bloqueando la conexión." +PermissionDenied="La conexión ha sido bloqueada. Compruebe su configuración del cortafuegos o anti-virus para asegurarse de que OBS tiene acceso completo a Internet." +ConnectionAborted="La conexión ha sido abortada. Normalmente esto indica que hay problemas de conexión entre tu equipo y el servicio de transmisión." +ConnectionReset="La conexión se ha terminado. Normalmente esto indica que hay problemas de conexión entre tu equipo y el servicio de transmisión." +HostNotFound="Nombre de host no encontrado. Asegúrese que haya configurado un servidor de transmisión valido y que su conexión a Internet / DNS estén funcionando correctamente." +NoData="Nombre de host encontrado, pero no hay datos del tipo solicitado. Esto puede ocurrir si has enlazado a una dirección IPv6 y su servicio de streaming sólo tiene direcciones IPv4 (ver Configuración / Avanzada)." +AddressNotAvailable="Dirección no disponible. Puede que hayas intentado enlazar con una dirección IP no valida (vea Configuración / Avanzado)." +SSLCertVerifyFailed="El servidor RTMP envió un certificado SSL no válido." + diff --git a/plugins/obs-outputs/data/locale/et-EE.ini b/plugins/obs-outputs/data/locale/et-EE.ini index dfca04e57..9ac2c59e8 100644 --- a/plugins/obs-outputs/data/locale/et-EE.ini +++ b/plugins/obs-outputs/data/locale/et-EE.ini @@ -1,2 +1,6 @@ RTMPStream="RTMP voogedastus" +FLVOutput="FLV faili väljund" +FLVOutput.FilePath="Faili tee" +Default="Vaikeseade" + diff --git a/plugins/obs-outputs/data/locale/eu-ES.ini b/plugins/obs-outputs/data/locale/eu-ES.ini index a5eb35096..8e38430e7 100644 --- a/plugins/obs-outputs/data/locale/eu-ES.ini +++ b/plugins/obs-outputs/data/locale/eu-ES.ini @@ -4,3 +4,12 @@ FLVOutput="FLV irteera fitxategia" FLVOutput.FilePath="Fitxategiaren bidea" Default="Lehenetsia" +ConnectionTimedOut="Konexiorik ez dago. Begiratu baliozko transmisio zerbitzua ezarrita duzun eta inongo suebakirik ez duen konexioa galarazten ari." +PermissionDenied="Konexioa blokeatuta dago. Begiratu zure suebakiaren / antibirusaren ezarpenak egiaztatzeko OBSek Internetera konektatzeko baimena duela." +ConnectionAborted="Konexioa galarazita dago. Transmisioaren zerbitzurekin konektatzeko arazoak izaten dira ohiko arrazoiak." +ConnectionReset="Pareak berrezarri du konexioa. Transmisioaren zerbitzurekin konektatzeko arazoak izaten dira ohiko arrazoiak." +HostNotFound="Ez da ostalari-izena topatu. Egiaztatu baliozko transmisio zerbitzaria jarri duzula eta zure Internet-konexioa eta DNSa zuzen ari direla lanean." +NoData="Ostalari-izena topatu da baina eskatutako datu motatik batere ez. Gerta daiteke IPv6 helbide bat eskatu izana eta zure transmisio zerbitzuak bakarrik onartzea IPv4 helbideak (Ikus ezarpen aurreratuak)." +AddressNotAvailable="Helbidea ez dago eskuragarri. Agian saiatu zara baliozkoa ez den IP helbide batera konektatzen (ikus ezarpen aurreratuak)." +SSLCertVerifyFailed="RTMP zerbitzariak baliorik gabeko SSL ziurtagiria bidali du." + diff --git a/plugins/obs-outputs/data/locale/fi-FI.ini b/plugins/obs-outputs/data/locale/fi-FI.ini index e4e849197..bed1ff4de 100644 --- a/plugins/obs-outputs/data/locale/fi-FI.ini +++ b/plugins/obs-outputs/data/locale/fi-FI.ini @@ -4,3 +4,12 @@ FLVOutput="FLV-tiedosto ulostulo" FLVOutput.FilePath="Tiedostopolku" Default="Oletusarvo" +ConnectionTimedOut="Yhteys keskeytyi. Varmista että olet asettanut lähetyspalvelun oikein ja palomuuri ei estä yhteyttä." +PermissionDenied="Yhteys estettiin. Tarkista palomuurin / virusturvan asetukset ja varmista, että OBS:lle on sallittu täydet yhteydet." +ConnectionAborted="Yhteys katkaistiin. Tämä tarkoittaa yleensä yhteysongelmia sinun ja lähetyspalvelun välillä." +ConnectionReset="Yhteys katkaistiin. Tämä tarkoittaa yleensä yhteysongelmia sinun ja lähetyspalvelun välillä." +HostNotFound="Isäntänimeä ei löytynyt. Varmista että syötit voimassaolevan lähetyspalvelimen ja että internet-yhteytesi tai DNS-palvelimesi toimivat oikein." +NoData="Isäntänimi löytyi, mutta ei oikeanlaista pyydettyä dataa. Näin voi tapahtua jos olet rajannut yhteytesi IPv6 -osoitteeseen ja lähetyspalvelusi tukee vain IPv4-osoitteita (Katso Asetukset / Lisäasetukset)." +AddressNotAvailable="Osoite ei ole saatavilla. Voi olla että yritit kiinnittää väärän IP-osoitteen (Katso Asetukset / Lisäasetukset)." +SSLCertVerifyFailed="RTMP-palvelin lähetti virheellisen SSL-sertifikaatin." + diff --git a/plugins/obs-outputs/data/locale/fil-PH.ini b/plugins/obs-outputs/data/locale/fil-PH.ini new file mode 100644 index 000000000..dc224e946 --- /dev/null +++ b/plugins/obs-outputs/data/locale/fil-PH.ini @@ -0,0 +1,14 @@ +RTMPStream="Ang RTMP Stream" +RTMPStream.DropThreshold="Ang Drop Treshold (millisegundos)" +FLVOutput="Ang FLV File Awput" +FLVOutput.FilePath="Ang Landas ng File" +Default="I-Default" + +ConnectionTimedOut="Ang koneskyong ay nag time out. Siguraduhin na nakaayos at balido ang streaming service at walang firewall na nakaharang sa koneksyon." +PermissionDenied="Ang koneksyon ay hinarang. Suriin ang firewall / anti-virus settings upang matiyak na tiyak na may buong access sa internet ang OBS." +ConnectionAborted="Ang koneksyon ay naudlot. Ito ay karaniwang nagpapahiwatig na may problema sa iyong internet koneksyon at sa streaming service." +ConnectionReset="Ang koneksyon ay na i-reset ng peer. Ito ay karaniwang nagpapahiwatig na may problema sa iyong internet koneksyon at sa streaming service." +HostNotFound="Hindi makita ang Hostname. Siguraduhin na nilagay mo ay balidong streaming server at ang iyong internet koneksyon / DNS ay gumagana ng mabuti." +NoData="Ang hostname ay nakita, pero walang hinihinging tipo ng datus. Ito ay ay nangyayari kapag naka bound sa IPv6 address at ang iyong streaming service ay IPv4 lamang (tignan Settings / Advanced)." +AddressNotAvailable="Hindi magamit ang address. Ikaw ay gumamit at na i-bind ito sa di balidong IP address ( tingan ang Settings / Advanced)." + diff --git a/plugins/obs-outputs/data/locale/fr-FR.ini b/plugins/obs-outputs/data/locale/fr-FR.ini index ad4b9b83f..454150fa4 100644 --- a/plugins/obs-outputs/data/locale/fr-FR.ini +++ b/plugins/obs-outputs/data/locale/fr-FR.ini @@ -4,3 +4,12 @@ FLVOutput="Fichier FLV sortant" FLVOutput.FilePath="Chemin du fichier" Default="Interface par défaut" +ConnectionTimedOut="La connexion à expiré. Assurez-vous que vous avez configuré un service de streaming valide et qu'aucun pare-feu ne bloque la connexion." +PermissionDenied="La connexion a été bloquée. Vérifiez vos paramètres de pare-feu / antivirus pour vous assurer OBS est autorisé à avoir l'accès complet d'internet." +ConnectionAborted="La connexion à été interrompue. Cela indique généralement des problèmes de connexion internet entre vous et le service de streaming." +ConnectionReset="La connexion à été interrompue. Cela indique généralement des problèmes de connexion internet entre vous et le service de diffusion." +HostNotFound="Nom d’hôte non trouvé. Assurez-vous que vous avez spécifié un serveur de diffusion valide et que votre connexion internet / DNS fonctionnent correctement." +NoData="Nom d’hôte trouvé, mais aucune donnée du type requis. Cela peut se produire si vous avez lié à une adresse IPv6 et votre service de diffusion ne possède que des adresses IPv4 (voir Paramètres / Avancé)." +AddressNotAvailable="Adresse non disponible. Vous avez peut-être essayé de la lier à une adresse IP non valide (voir Paramètres / Avancé)." +SSLCertVerifyFailed="Le serveur RTMP a fourni un certificat SSL incorrect." + diff --git a/plugins/obs-outputs/data/locale/gd-GB.ini b/plugins/obs-outputs/data/locale/gd-GB.ini new file mode 100644 index 000000000..2c1f2f638 --- /dev/null +++ b/plugins/obs-outputs/data/locale/gd-GB.ini @@ -0,0 +1,7 @@ +RTMPStream="Sruthadh RTMP" +RTMPStream.DropThreshold="Stairsneach an tuiteim (mille-dhiog)" +FLVOutput="Às-chur faidhle FLV" +FLVOutput.FilePath="Slighe an fhaidhle" +Default="Bun-roghainn" + + diff --git a/plugins/obs-outputs/data/locale/gl-ES.ini b/plugins/obs-outputs/data/locale/gl-ES.ini index 69c31e576..bc89bf0e8 100644 --- a/plugins/obs-outputs/data/locale/gl-ES.ini +++ b/plugins/obs-outputs/data/locale/gl-ES.ini @@ -3,3 +3,4 @@ RTMPStream.DropThreshold="Limiar (milisegundos)" FLVOutput="Ficheiro de saída FLV" FLVOutput.FilePath="Camiño do ficheiro" + diff --git a/plugins/obs-outputs/data/locale/he-IL.ini b/plugins/obs-outputs/data/locale/he-IL.ini index 836a10629..77f798745 100644 --- a/plugins/obs-outputs/data/locale/he-IL.ini +++ b/plugins/obs-outputs/data/locale/he-IL.ini @@ -2,4 +2,6 @@ RTMPStream="זרם RTMP" RTMPStream.DropThreshold="הורד סף (אלפיות שניה)" FLVOutput="קובץ פלט FLV" FLVOutput.FilePath="נתיב קובץ" +Default="ברירת מחדל" + diff --git a/plugins/obs-outputs/data/locale/hr-HR.ini b/plugins/obs-outputs/data/locale/hr-HR.ini index 6c94bfeb4..2310a6eac 100644 --- a/plugins/obs-outputs/data/locale/hr-HR.ini +++ b/plugins/obs-outputs/data/locale/hr-HR.ini @@ -4,3 +4,4 @@ FLVOutput="Izlaz u FLV datoteku" FLVOutput.FilePath="Putanja datoteke" Default="Podrazumevani" + diff --git a/plugins/obs-outputs/data/locale/hu-HU.ini b/plugins/obs-outputs/data/locale/hu-HU.ini index bd3c51a7a..631f19f89 100644 --- a/plugins/obs-outputs/data/locale/hu-HU.ini +++ b/plugins/obs-outputs/data/locale/hu-HU.ini @@ -4,3 +4,12 @@ FLVOutput="FLV kimeneti fájl" FLVOutput.FilePath="Fájl elérési útja" Default="Alapértelmezett" +ConnectionTimedOut="A kapcsolat időtúllépés miatt megszakadt. Győződjön meg róla, hogy a beállított stream szolgáltatás érvényes és hogy, tűzfal nem blokkolja a kapcsolatot." +PermissionDenied="A kapcsolat blokkolásra került. Ellenőrizze a tűzfal / antivírus beállításait, hogy az OBS számára engedélyezve van e a hozzáférés." +ConnectionAborted="A kapcsolat megszakadt. Ez általában azt jelzi, hogy az internetkapcsolat a stream kiszolgáló és ön között problémákkal néz szembe." +ConnectionReset="A kapcsolat a peer által megszakítva. Ez általában azt jelzi, hogy az internetkapcsolat a stream kiszolgáló és ön között problémákkal néz szembe." +HostNotFound="A hostnév nem található. Győződjön meg róla, hogy érvényes stream szervert adott meg és az internetkapcsolata / DNS szerver megfelelően működik." +NoData="Hostnév megtalálva, viszont a kért típusú állomány nem elérhető. Ez akkor fordul elő, ha IPv6 címhez van rendelve és a stream kiszolgálójának csak IPv4 címei állnak rendelkezésre (lásd: Beállítások / Haladó)." +AddressNotAvailable="A cím nem elérhető. Valószínűleg egy érvénytelen IP címet adott meg (Lásd: Beállítások / Haladó)." +SSLCertVerifyFailed="Az RTMP kiszolgáló által küldött SSL tanúsítvány érvénytelen." + diff --git a/plugins/obs-outputs/data/locale/it-IT.ini b/plugins/obs-outputs/data/locale/it-IT.ini index 863a95433..b9cf420a2 100644 --- a/plugins/obs-outputs/data/locale/it-IT.ini +++ b/plugins/obs-outputs/data/locale/it-IT.ini @@ -4,3 +4,11 @@ FLVOutput="Uscita file FLV" FLVOutput.FilePath="Destinazione file" Default="Predefinito" +ConnectionTimedOut="Timeout della connessione. Assicurarsi di aver configurato un valido servizio di streaming e nessun firewall sta bloccando la connessione." +PermissionDenied="La connessione è stata bloccata. Controlla il tuo firewall / impostazioni di anti-virus per assicurarsi che per OBS sia consentito accesso completo a internet." +ConnectionAborted="La connessione è stata interrotta. In genere indica problemi di connessione tra l'utente e il servizio di streaming." +ConnectionReset="La connessione è stata ripristinata dal peer. In genere indica problemi di connessione tra l'utente e il servizio di streaming." +HostNotFound="Nome host non trovato. Assicurarsi di aver inserito un valido server per lo streaming e che la connessione a internet / DNS funzioni correttamente." +NoData="Nome host trovato, ma nessun dato del tipo richiesto. Ciò può verificarsi se è stato associato a un indirizzo IPv6 e il servizio di streaming ha solo indirizzi IPv4 (vedere Impostazioni / avanzate)." +AddressNotAvailable="Indirizzo non disponibile. Si è cercato di associare un indirizzo IP non valido (vedere Impostazioni / avanzate)." + diff --git a/plugins/obs-outputs/data/locale/ja-JP.ini b/plugins/obs-outputs/data/locale/ja-JP.ini index 659d21bde..0366cfbb4 100644 --- a/plugins/obs-outputs/data/locale/ja-JP.ini +++ b/plugins/obs-outputs/data/locale/ja-JP.ini @@ -4,3 +4,12 @@ FLVOutput="FLV ファイル出力" FLVOutput.FilePath="ファイルのパス" Default="既定" +ConnectionTimedOut="接続がタイムアウトしました。 有効なストリーミングサービスを設定し、ファイアウォールが接続をブロックしていないことを確認してください。" +PermissionDenied="接続がブロックされました。 ファイアウォール/アンチウィルスの設定をチェックして、OBSにインターネットへのアクセスがすべて許可されていることを確認してください。" +ConnectionAborted="接続は中止されました。 ストリーミングサービスとの間のインターネット接続に問題があることを示しています。" +ConnectionReset="接続はピアによってリセットされました。 ストリーミングサービスとの間のインターネット接続に問題があることを示しています。" +HostNotFound="ホスト名が見つかりません。 有効なストリーミングサーバーを入力していることとインターネット接続/DNSが正しく機能していることを確認してください。" +NoData="ホスト名が見つかりましたが、要求されたタイプのデータがありません。 これはIPv6アドレスにバインドしている状態でストリーミングサービスにIPv4アドレスしかない場合に発生します。 (設定 / 詳細設定 を参照)" +AddressNotAvailable="アドレスを利用できません。 無効なIPアドレスにバインドしようとした可能性があります。 (設定 / 詳細設定 を参照)" +SSLCertVerifyFailed="RTMPサーバーが無効なSSL証明書を送信しました。" + diff --git a/plugins/obs-outputs/data/locale/ka-GE.ini b/plugins/obs-outputs/data/locale/ka-GE.ini new file mode 100644 index 000000000..933c06b32 --- /dev/null +++ b/plugins/obs-outputs/data/locale/ka-GE.ini @@ -0,0 +1,15 @@ +RTMPStream="RTMP ნაკადი" +RTMPStream.DropThreshold="ქვედა ზღურბლი (მილიწამი)" +FLVOutput="გამომავალი FLV ფაილი" +FLVOutput.FilePath="ფაილის მისამართი" +Default="ნაგულისხმევი" + +ConnectionTimedOut="კავშირის ვადა ამოიწურა. გადაამოწმეთ, სწორად გაქვთ თუ არა გამართული ნაკადის გაშვების მომსახურება და ქსელის ფარი ხომ არ ზღუდავს კავშირს." +PermissionDenied="კავშირი შეიზღუდა. გადაამოწმეთ ქსელის ფარის ან ანტივირუსული პროგრამის პარამეტრები და დარწმუნდით, რომ OBS-ს აქვს სრული დაშვება ინტერნეტთან." +ConnectionAborted="კავშირი გაუქმდა. ძირითადად, ეს მიუთითებს ინტერნეტკავშირის ხარვეზების არსებობას, თქვენსა და ნაკადის გაშვების მომსახურების მომწოდებელს შორის." +ConnectionReset="კავშირი გაწყდა ერთ-ერთი მხარის მიერ. ძირითადად, ეს მიუთითებს ინტერნეტკავშირის ხარვეზების არსებობას, თქვენსა და ნაკადის გაშვების მომსახურების მომწოდებელს შორის." +HostNotFound="დაკავშირების წერტილი ვერ მოიძებნა. დარწმუნდით, რომ სწორად უთითებთ ნაკადის გაშვების მომსახურების მონაცემებს და თქვენი DNS / ინტერნეტკავშირის პარამეტრებიც სწორადაა გამართული." +NoData="დაკავშირების წერტილი მოიძებნა, მაგრამ მოთხოვნილი სახის მონაცემები არა. ეს შეიძლება გამოწვეული იყოს იმით, რომ თქვენ უკავშირდებით IPv6 მისამართზე, ხოლო თქვენს ნაკადის გაშვების მომსახურებას, მხოლოდ IPv4 მისამართები გააჩნია (იხილეთ პარამეტრები / დამატებითი)." +AddressNotAvailable="მისამართი მიუწვდომელია. შესაძლოა, თქვენ ცდილობთ მცდარ IP მისამართზე დაკავშირებას (იხილეთ პარამეტრები / დამატებითი)." +SSLCertVerifyFailed="RTMP სერვერმა გაგზავნა არამართებული SSL სერტიფიკატი." + diff --git a/plugins/obs-outputs/data/locale/ko-KR.ini b/plugins/obs-outputs/data/locale/ko-KR.ini index ec2669dff..f3c114fde 100644 --- a/plugins/obs-outputs/data/locale/ko-KR.ini +++ b/plugins/obs-outputs/data/locale/ko-KR.ini @@ -4,3 +4,12 @@ FLVOutput="FLV 파일 출력" FLVOutput.FilePath="파일 경로" Default="기본값" +ConnectionTimedOut="연결 시간이 초과하였습니다. 방송 정보가 정확히 입력되었는지 확인하고, 방화벽 때문에 연결에 문제가 있는지 점검하십시오." +PermissionDenied="연결이 차단되었습니다. 방화벽이나 백신 설정에서 OBS의 인터넷 연결을 가로막고 있는지 확인하십시오." +ConnectionAborted="연결이 취소되었습니다. 보통 사용자와 방송 서비스 간의 연결 상태에 문제가 있음을 의미합니다." +ConnectionReset="상호 연결 문제로 초기화되었습니다. 보통 사용자와 방송 서비스 간의 연결 상태에 문제가 있음을 의미합니다." +HostNotFound="호스트 이름을 찾을 수 없습니다. 방송 서버 정보가 제대로 입력되었는지 확인하고, 인터넷 접속 혹은 DNS가 제대로 작동하고 있는지 점검하십시오." +NoData="호스트 이름은 찾았지만 요청한 형식의 데이터가 없습니다. 이 문제는 보통 사용자가 IPv6 형식의 주소를 고정하여 사용하면서 IPv4 형식의 주소만 지원하는 방송 서비스에 접속을 시도한 경우 나타납니다 (설정 / 고급 창을 확인하십시오)." +AddressNotAvailable="주소를 사용할 수 없습니다. 잘못된 IP주소를 고정하고 있습니다 (설정 / 고급 창을 확인하십시오)." +SSLCertVerifyFailed="해당 RTMP 서버는 잘못된 SSL 인증서를 보냈습니다." + diff --git a/plugins/obs-outputs/data/locale/nb-NO.ini b/plugins/obs-outputs/data/locale/nb-NO.ini index 7ad019444..7be490c99 100644 --- a/plugins/obs-outputs/data/locale/nb-NO.ini +++ b/plugins/obs-outputs/data/locale/nb-NO.ini @@ -4,3 +4,12 @@ FLVOutput="FLV-filutgang" FLVOutput.FilePath="Filbane" Default="Standard" +ConnectionTimedOut="Forbindelsen ble tidsavbrutt. Sørg for at du har konfigurert en strømmingstjeneste og at den ikke blir blokkert av en brannmur." +PermissionDenied="Tilkoblingen ble blokkert. Sjekk at OBS har full internettilgang i brannmur/antivirusinnstillingene dine." +ConnectionAborted="Tilkoblingen ble avbrutt. Dette betyr vanligvis at det er problemer med nettverkskoblingen mellom deg og strømmetjenesten." +ConnectionReset="Tilkoblingen ble avbrutt. Dette betyr vanligvis at det er problemer med nettverkskoblingen mellom deg og strømmetjenesten." +HostNotFound="Tjeneren ble ikke funnet. Kontroller at du har angitt en gyldig streaming server og at tilkoblingen / DNS fungerer." +NoData="Tjeneren funnet, men ingen data for den forespurte typen. Dette kan skje hvis du har bundet til en IPv6-adresse og streaming tjeneste har bare IPv4-adresser (se Snnstillinger / Avansert)." +AddressNotAvailable="Adresse ikke tilgjengelig. Du prøvde å binde til en ugyldig IP-adresse (se Innstillinger / Avansert)." +SSLCertVerifyFailed="RTMP-tjeneren sendte et ugyldig SSL-sertifikat." + diff --git a/plugins/obs-outputs/data/locale/nl-NL.ini b/plugins/obs-outputs/data/locale/nl-NL.ini index 944c8094d..e72f7a1ce 100644 --- a/plugins/obs-outputs/data/locale/nl-NL.ini +++ b/plugins/obs-outputs/data/locale/nl-NL.ini @@ -4,3 +4,12 @@ FLVOutput="FLV Bestandsuitvoer" FLVOutput.FilePath="Bestandspad" Default="Standaard" +ConnectionTimedOut="Er is een time-out opgetreden in de verbinding. Controleer dat je een geldige streaming service hebt ingesteld en dat er geen firewall de verbinding blokkeert." +PermissionDenied="De verbinding was geblokkeerd. Controleer je firewall/anti-virus instellingen om te controleren dat OBS volledige internettoegang heeft." +ConnectionAborted="De verbinding was afgebroken. Dit duidt meestal op verbindingsproblemen tussen jou en de streaming service." +ConnectionReset="De verbinding was gereset door de andere partij. Dit duidt meestal op verbindingsproblemen tussen jou en de streaming service." +HostNotFound="Hostname niet gevonden. Controleer dat je een geldige streaming service hebt ingevuld en dat je internetverbinding / DNS correct werken." +NoData="Hostname gevonden, maar geen data van het verwachte type. Dit kan gebeuren als je aan een IPv6 adres hebt gebonden, en je streaming service alleen IPv4 adressen heeft (zie Instellingen / Geavanceerd)." +AddressNotAvailable="Adres niet beschikbaar. Je hebt misschien geprobeerd om aan een ongeldig IP adres te binden (zie Instellingen / Geavanceerd)." +SSLCertVerifyFailed="De RTMP-server heeft een ongeldig SSL-certificaat verzonden." + diff --git a/plugins/obs-outputs/data/locale/pl-PL.ini b/plugins/obs-outputs/data/locale/pl-PL.ini index c5676eefb..735c3c34c 100644 --- a/plugins/obs-outputs/data/locale/pl-PL.ini +++ b/plugins/obs-outputs/data/locale/pl-PL.ini @@ -4,3 +4,12 @@ FLVOutput="Wyjście do pliku FLV" FLVOutput.FilePath="Scieżka do pliku" Default="Domyślne" +ConnectionTimedOut="Upłynął limit czasu połączenia. Upewnij się, że usługa strumieniowania jest poprawnie skonfigurowana a zapora internetowa nie blokuje połączenia." +PermissionDenied="Połączenie zostało zablokowane. Sprawdź stan zapory internetowej / ustawienia antywirusowe, aby upewnić się, że OBS ma pełny dostęp do internetu." +ConnectionAborted="Połączenie zostało przerwane. Wskazuje to najczęściej na problemy w połączeniu między Tobą a usługą strumieniowania." +ConnectionReset="Połączenie zostało przerwane po stronie serwera. Wskazuje to najczęściej na problemy w połączeniu między Tobą a usługą strumieniowania." +HostNotFound="Nie znaleziono nazwy hosta. Upewnij się, że wprowadzono prawidłowe dane serwera przesyłania strumieniowego i połączenie z internetem / DNS są poprawne." +NoData="Nazwa serwera została znaleziona ale nie stwierdzono poprawności odbieranych danych. Dzieje się tak najczęściej po przypisaniu aplikacji do adresu IPv6, gdy usługa strumieniowania obsługuje jedynie adresy IPv4 (zobacz Ustawienia -> Zaawansowane)." +AddressNotAvailable="Adres IP niedostępny. Być może powiązano aplikację z nieprawidłowym adresem IP (zobacz Ustawienia -> Zaawansowane)." +SSLCertVerifyFailed="Serwer RTMP wysłał nieprawidłowy certyfikat SSL." + diff --git a/plugins/obs-outputs/data/locale/pt-BR.ini b/plugins/obs-outputs/data/locale/pt-BR.ini index f07053454..7cd9b3900 100644 --- a/plugins/obs-outputs/data/locale/pt-BR.ini +++ b/plugins/obs-outputs/data/locale/pt-BR.ini @@ -4,3 +4,12 @@ FLVOutput="Arquivo de Saída FLV" FLVOutput.FilePath="Caminho do Arquivo" Default="Padrão" +ConnectionTimedOut="A conexão expirou. Verifique se você configurou um serviço de transmissão válido e nenhum firewall está bloqueando a conexão." +PermissionDenied="A conexão foi bloqueada. Verifique seu firewall / configurações de antivírus para certificar-se do acesso completo do OBS à internet." +ConnectionAborted="A conexão foi abortada. Isso geralmente indica problemas de conexão entre você e o serviço de transmissão." +ConnectionReset="A conexão foi redefinida pelo usuário. Isso geralmente indica problemas de conexão entre você e o serviço de transmissão." +HostNotFound="Host não encontrado. Verifique se você inseriu um servidor válido de transmissão e se sua conexão de internet / DNS estão funcionando corretamente." +NoData="Host encontrado, mas não há dados do tipo solicitado. Isso pode ocorrer se você tiver vinculado a um endereço IPv6 e seu serviço de transmissão tem apenas endereços IPv4 (consulte Configurações / Avançado)." +AddressNotAvailable="Endereço não disponível. Você pode ter tentado se vincular a um endereço IP inválido (consulte Configurações / Avançado)." +SSLCertVerifyFailed="O servidor RTMP enviou um certificado SSL inválido." + diff --git a/plugins/obs-outputs/data/locale/pt-PT.ini b/plugins/obs-outputs/data/locale/pt-PT.ini index ebcd46508..c3442dd35 100644 --- a/plugins/obs-outputs/data/locale/pt-PT.ini +++ b/plugins/obs-outputs/data/locale/pt-PT.ini @@ -3,3 +3,4 @@ RTMPStream.DropThreshold="Limite de corte (milissegundos)" FLVOutput="Ficheiro de saída FLV" FLVOutput.FilePath="Caminho do ficheiro" + diff --git a/plugins/obs-outputs/data/locale/ro-RO.ini b/plugins/obs-outputs/data/locale/ro-RO.ini index 2333b471d..9d67c9219 100644 --- a/plugins/obs-outputs/data/locale/ro-RO.ini +++ b/plugins/obs-outputs/data/locale/ro-RO.ini @@ -3,3 +3,4 @@ RTMPStream.DropThreshold="Prag de pierderi (milisecunde)" FLVOutput="Ieșire fișier FLV" FLVOutput.FilePath="Calea fișierului" + diff --git a/plugins/obs-outputs/data/locale/ru-RU.ini b/plugins/obs-outputs/data/locale/ru-RU.ini index ec14e977f..3834a253d 100644 --- a/plugins/obs-outputs/data/locale/ru-RU.ini +++ b/plugins/obs-outputs/data/locale/ru-RU.ini @@ -4,3 +4,12 @@ FLVOutput="Выходной файл FLV" FLVOutput.FilePath="Путь к файлу" Default="По умолчанию" +ConnectionTimedOut="Истекло время ожидания соединения. Убедитесь, что вы настроили действительный сервис вещания и брандмауэр не блокирует подключение." +PermissionDenied="Соединение было заблокировано. Проверьте параметры вашего брандмауэра/антивируса, чтобы убедиться, что OBS разрешен полный доступ к интернету." +ConnectionAborted="Соединение было прервано. Обычно это указывает на проблемы с интернет-соединением между вами и службой вещания." +ConnectionReset="Соединение было сброшено одноранговым узлом. Обычно это указывает на проблемы с интернет-соединением между вами и службой вещания." +HostNotFound="Имя узла не найдено. Убедитесь, что вы ввели действительный сервер вещания и ваше подключение к интернету/DNS работают правильно." +NoData="Имя узла найдено, но нет данных запрошенного типа. Такое может случиться, если вы привязаны к IPv6-адресу, а ваш сервис вещания имеет только IPv4-адреса (смотрите Настройки - Расширенные)." +AddressNotAvailable="Адрес недоступен. Возможно вы пытались привязаться к недействительному IP-адресу (смотрите Настройки - Расширенные)." +SSLCertVerifyFailed="RTMP сервер отправил недействительный сертификат SSL." + diff --git a/plugins/obs-outputs/data/locale/sk-SK.ini b/plugins/obs-outputs/data/locale/sk-SK.ini index 4949ef42a..d2b5d1129 100644 --- a/plugins/obs-outputs/data/locale/sk-SK.ini +++ b/plugins/obs-outputs/data/locale/sk-SK.ini @@ -1,5 +1,9 @@ RTMPStream="RTMP stream" +RTMPStream.DropThreshold="Prah strát (milisekundy)" FLVOutput="Výstup do súboru FLV" FLVOutput.FilePath="Cesta k súboru" -Default="Základné" +Default="Predvolené" + +ConnectionReset="Pripojenie bolo resetované druhou stranou. Toto obvykle znamená, že nastali problémy s pripojením medzi vami a streaming službou." +AddressNotAvailable="Adresa nie je k dispozícii. Môžno ste sa snažili naviazať na neplatnú IP adresu (pozrite si Nastavenia / Rozšírené)." diff --git a/plugins/obs-outputs/data/locale/sl-SI.ini b/plugins/obs-outputs/data/locale/sl-SI.ini index c74af980c..2451f6d6f 100644 --- a/plugins/obs-outputs/data/locale/sl-SI.ini +++ b/plugins/obs-outputs/data/locale/sl-SI.ini @@ -3,3 +3,4 @@ RTMPStream.DropThreshold="Raven Padca (milisekunde)" FLVOutput="Izhod datoteke FLV" FLVOutput.FilePath="Pot datoteke" + diff --git a/plugins/obs-outputs/data/locale/sr-CS.ini b/plugins/obs-outputs/data/locale/sr-CS.ini index 6c94bfeb4..2310a6eac 100644 --- a/plugins/obs-outputs/data/locale/sr-CS.ini +++ b/plugins/obs-outputs/data/locale/sr-CS.ini @@ -4,3 +4,4 @@ FLVOutput="Izlaz u FLV datoteku" FLVOutput.FilePath="Putanja datoteke" Default="Podrazumevani" + diff --git a/plugins/obs-outputs/data/locale/sr-SP.ini b/plugins/obs-outputs/data/locale/sr-SP.ini index b509fa719..ceb247319 100644 --- a/plugins/obs-outputs/data/locale/sr-SP.ini +++ b/plugins/obs-outputs/data/locale/sr-SP.ini @@ -4,3 +4,4 @@ FLVOutput="Излаз у FLV датотеку" FLVOutput.FilePath="Путања датотеке" Default="Подразумевана" + diff --git a/plugins/obs-outputs/data/locale/sv-SE.ini b/plugins/obs-outputs/data/locale/sv-SE.ini index 3dbc1d49e..f124aceb6 100644 --- a/plugins/obs-outputs/data/locale/sv-SE.ini +++ b/plugins/obs-outputs/data/locale/sv-SE.ini @@ -4,3 +4,12 @@ FLVOutput="FLV-filutmatning" FLVOutput.FilePath="Sökväg" Default="Standard" +ConnectionTimedOut="Anslutningen förlorades. Se till att du har konfigurerat en giltig strömningstjänst och att inga brandväggar blockerar anslutningen." +PermissionDenied="Anslutningen blockerades. Kontrollera inställningarna för din brandvägg/antivirusprogram för att se till att OBS har fullständig åtkomst till Internet." +ConnectionAborted="Anslutningen avbröts. Detta kan indikera problem med Internetanslutningen mellan dig och strömningstjänsten." +ConnectionReset="Anslutningen återställdes av en peer. Detta kan indikera problem med Internetanslutningen mellan dig och strömningstjänsten." +HostNotFound="Värdnamnet hittades inte. Se till att du har angivit en giltigt strömningstjänst och att din Internetanslutning / DNS fungerar på rätt sätt." +NoData="Värdnamnet hittades, men ingen data av den begärda typen. Detta kan hända om du ansluter till en IPv6-adress och din strömningstjänst endast har IPv4-adresser (gå till Inställningar / Avancerat)." +AddressNotAvailable="Adressen är inte tillgänglig. Du kanske försökte ansluta till en ogiltig IP-adress (gå till Inställningar / Avancerat)." +SSLCertVerifyFailed="RTMP-servern skickade ett ogiltigt SSL-certifikat." + diff --git a/plugins/obs-outputs/data/locale/th-TH.ini b/plugins/obs-outputs/data/locale/th-TH.ini index d1d63e0a1..f136bd991 100644 --- a/plugins/obs-outputs/data/locale/th-TH.ini +++ b/plugins/obs-outputs/data/locale/th-TH.ini @@ -1,2 +1,3 @@ FLVOutput.FilePath="ตำแหน่งไฟล์" + diff --git a/plugins/obs-outputs/data/locale/tl-PH.ini b/plugins/obs-outputs/data/locale/tl-PH.ini new file mode 100644 index 000000000..8ae0ace3b --- /dev/null +++ b/plugins/obs-outputs/data/locale/tl-PH.ini @@ -0,0 +1,14 @@ +RTMPStream="RTMP Stream" +RTMPStream.DropThreshold="Treshold ng pagbaba (millisegundos)" +FLVOutput="Ang kinalabasan ng FLV File" +FLVOutput.FilePath="Ang Daanan ng File" +Default="Ang Default" + +ConnectionTimedOut="Ang koneksyon ay nawala. Siguraduhing ikaw ay nag-configured ng isang balidong serbisyo ng streaming at walang firewall na nagpipigil sa koneksyon." +PermissionDenied="Ang koneksyon ay na-block. I-check ang iyong firewall / anti-virus na settings para masiguradong ang OBS ay pwedeng makapasok sa internet." +ConnectionAborted="Ang koneksyon ay nabigo. Ito ay kadalasang indikasyon na ang internet nakoneksyon ay may problema sa'yo o kay sa serbisyo ng streaming." +ConnectionReset="Ang koneksyon ay ni-reset ng peer. Ito ay kadalasang indikasyon na ang internet nakoneksyon ay may problema sa'yo o kay sa serbisyo ng streaming." +HostNotFound="Ang hostname ay hindi matagpuan. Siguraduhing ang nilagay mo ay isang balidong stereaming server at ang iyong internet koneksyon / DNS ay gumagana ng sakto." +NoData="Ang hostname ay natagpuan, ngunit walang data sa ni-request na tipo. Ito ay pwedeng mangyari kung wala kang bound para sa IPv6 na address at ang iyong streaming service ay meron lang IPv4 na mga address (tingnan ang Setting / Advanced)." +AddressNotAvailable="Ang address ay hindi pwede. Pwede kang sumubok na i-bind sa isang hindi balidong IP address (tingnan ang Setting / Advanced)." + diff --git a/plugins/obs-outputs/data/locale/tr-TR.ini b/plugins/obs-outputs/data/locale/tr-TR.ini index 6da54200a..61aa6c6e9 100644 --- a/plugins/obs-outputs/data/locale/tr-TR.ini +++ b/plugins/obs-outputs/data/locale/tr-TR.ini @@ -4,3 +4,12 @@ FLVOutput="FLV Dosyası Çıkışı" FLVOutput.FilePath="Dosya Yolu" Default="Varsayılan" +ConnectionTimedOut="Bağlantı zaman aşımına uğradı. Geçerli bir yayın servisi yapılandırdığınızdan ve bağlantıyı engelleyen bir güvenlik duvarı olmadığından emin olun." +PermissionDenied="Bağlantı engellendi. Güvenlik duvarı / virüs koruma ayarlarınızı OBS'ye tam internet erişimi verildiğinden emin olmak için kontrol edin." +ConnectionAborted="Bağlantı iptal edildi. Bu genellikle sizin ve yayın servisinin arasındaki internet bağlantısı sorununa işaret eder." +ConnectionReset="Bağlantı karşı taraftan sıfırlandı. Bu genellikle sizin ve yayın servisinin arasındaki internet bağlantısı sorununa işaret eder." +HostNotFound="Ana bilgisayar adı bulunamadı. Geçerli bir yayın sunucusu girdiğinizden ve internet bağlantınızın / DNS'nizin düzgün çalıştığını emin olun." +NoData="Ana bilgisayar adı bulundu, ancak istenen türde veri bulunamadı. Bu bir IPv6 adresine bağlamış ve yayın servisinizin sadece IPv4 adresleri varsa oluşabilir (bkz: Ayarlar / Gelişmiş)." +AddressNotAvailable="Adres kullanılamaz. Geçersiz bir IP adresi bağlamayı denemiş olabilirsiniz (bakın: Ayarlar / Gelişmiş)." +SSLCertVerifyFailed="RTMP sunucusu geçersiz bir SSL sertifikası gönderdi." + diff --git a/plugins/obs-outputs/data/locale/uk-UA.ini b/plugins/obs-outputs/data/locale/uk-UA.ini index de0fe2e1b..6ab98ef5d 100644 --- a/plugins/obs-outputs/data/locale/uk-UA.ini +++ b/plugins/obs-outputs/data/locale/uk-UA.ini @@ -4,3 +4,12 @@ FLVOutput="Вивід FLV файлу" FLVOutput.FilePath="Шлях до файлу" Default="За замовчанням" +ConnectionTimedOut="Вичерпано час підключення. Переконайтеся, що ви вказали правильний сервіс трансляцій і брандмауер не блокує з'єднання." +PermissionDenied="З'єднання було заблоковано. Перевірте налаштування вашого брандмауеру / антивірусу, щоб переконатися, що OBS має повний доступ до Інтернету." +ConnectionAborted="З'єднання було перервано. Зазвичай свідчить про проблеми з Інтернет підключенням між вами і постачальником з сервісу трансляцій." +ConnectionReset="З'єднання було скинуте рівноправним вузлом (reset by peer). Зазвичай свідчить про проблеми з Інтернет підключенням між вами і постачальником з сервісу трансляцій." +HostNotFound="Ім'я хоста, не знайдено. Переконайтеся, що ви ввели дійсний сервер трансляцій і підключення до Інтернету / DNS працює правильно." +NoData="Ім'я хоста знайдено, але нема жодних даних вказаного типу. Це може статися, якщо ви вказали прив'язку до IPv6-адресу, але ваш сервіс трансляцій підтримує лише адреси IPv4 (див. Налаштування / Розширені)." +AddressNotAvailable="Адреса недоступна. Напевно ви спробували прив'язатись до адаптера з неіснуючую IP-адресою (див. Налаштування / Розширені)." +SSLCertVerifyFailed="RTMP сервер надіслав неприпустимий сертифікат SSL." + diff --git a/plugins/obs-outputs/data/locale/vi-VN.ini b/plugins/obs-outputs/data/locale/vi-VN.ini index 8dc44d6be..5b2e11fe8 100644 --- a/plugins/obs-outputs/data/locale/vi-VN.ini +++ b/plugins/obs-outputs/data/locale/vi-VN.ini @@ -1,3 +1,14 @@ +RTMPStream="RTMP Stream" +RTMPStream.DropThreshold="Drop Threshold (mili giây)" +FLVOutput="FLV tập tin đầu ra" FLVOutput.FilePath="Đường dẫn tệp" Default="Mặc định" +ConnectionTimedOut="Kết nối đã hết thời. Đảm bảo bạn đã định cấu hình dịch vụ phát trực tuyến hợp lệ và không có tường lửa nào đang chặn kết nối." +PermissionDenied="Kết nối đã bị chặn. Hãy kiểm tra tường lửa / cài đặt chống virus để đảm bảo rằng OBS được cho phép truy cập internet đầy đủ." +ConnectionAborted="Kết nối đã bị hủy bỏ. Điều này thường chỉ ra kết nối internet giữa bạn và dịch vụ trực tuyến có vấn đề." +ConnectionReset="Kết nối đã được đặt lại bởi peer. Điều này thường chỉ ra các sự cố kết nối Internet giữa bạn và dịch vụ truyền trực tuyến." +HostNotFound="Tên máy chủ không tìm thấy. Đảm bảo rằng bạn đã nhập vào một máy chủ stream hợp lệ và kết nối internet của bạn / DNS đang hoạt động tốt." +NoData="Tên máy chủ được tìm thấy nhưng không có dữ liệu được yêu cầu. Điều này có thể xảy ra nếu bạn sử dụng địa chỉ IPv6 và dịch vụ stream của bạn chỉ có địa chỉ IPv4 (xem Cài đặt / Nâng cao)." +AddressNotAvailable="Địa chỉ không có sẵn. Bạn có thể đã cố gắng liên kết với một địa chỉ IP không hợp lệ (xem Cài đặt / Nâng cao)." + diff --git a/plugins/obs-outputs/data/locale/zh-CN.ini b/plugins/obs-outputs/data/locale/zh-CN.ini index 2f0a123b4..e8a99af87 100644 --- a/plugins/obs-outputs/data/locale/zh-CN.ini +++ b/plugins/obs-outputs/data/locale/zh-CN.ini @@ -4,3 +4,12 @@ FLVOutput="FLV 文件输出" FLVOutput.FilePath="文件路径" Default="默认" +ConnectionTimedOut="连接超时. 请确保您已经配置了一个有效的流媒体服务并且没有防火墙阻止连接." +PermissionDenied="连接被阻止. 检查您的防火墙 / 防病毒设置以确保允许 OBS 自由访问互联网." +ConnectionAborted="连接被中止. 这通常表明你和流媒体服务之间的互联网连接问题." +ConnectionReset="对方重置连接. 这通常表明你和流媒体服务之间的互联网连接问题." +HostNotFound="找不到 Hostname. 请确保您输入一个有效的流媒体服务器并且您的互联网连接 / DNS 工作正常." +NoData="Hostname 发现, 但没有请求的类型的数据的主机名. 这有可能因为你绑定到 IPv6 地址并且你的流媒体服务仅有 IPv4 地址 (请参阅设置 / 高级)." +AddressNotAvailable="没有可用的地址. 你可能在试图绑定到一个无效的 IP 地址 (请参阅设置 / 高级)." +SSLCertVerifyFailed="RTMP 服务器发送了无效的 SSL 证书。" + diff --git a/plugins/obs-outputs/data/locale/zh-TW.ini b/plugins/obs-outputs/data/locale/zh-TW.ini index 679909a03..15d5fc6ee 100644 --- a/plugins/obs-outputs/data/locale/zh-TW.ini +++ b/plugins/obs-outputs/data/locale/zh-TW.ini @@ -4,3 +4,12 @@ FLVOutput="FLV 檔案輸出" FLVOutput.FilePath="檔案路徑" Default="預設" +ConnectionTimedOut="連線逾時。請確定已經設定了一個有效的串流服務並且沒有防火牆在阻擋連接。" +PermissionDenied="連線被阻擋。請檢查防火牆 / 防毒設定以確保 OBS 有完整的網際網路存取權限。" +ConnectionAborted="連線被中止。通常這代表您與串流服務之間有網際網路連線問題。" +ConnectionReset="連線被對方重置。通常這代表您與串流服務之間有網際網路連線問題。" +HostNotFound="找不到主機名稱。請確定輸入了一個有效的串流服務器且網路連線跟 DNS 工作正常。" +NoData="找到主機名稱,但沒有要求類型的資料。這可能發生在您綁定於 IPv6 位址但串流服務只有 IPv4 位址 (請看 設定/進階)。" +AddressNotAvailable="位址不可用。可能因為嘗試綁定到一個不正確 IP 位址(請確認 設定/進階 的設定)。" +SSLCertVerifyFailed="RTMP 伺服器發送了一則不合法的 SSL 憑證。" + diff --git a/plugins/obs-outputs/flv-mux.c b/plugins/obs-outputs/flv-mux.c index 671e7fbc7..8bd51e9c8 100644 --- a/plugins/obs-outputs/flv-mux.c +++ b/plugins/obs-outputs/flv-mux.c @@ -71,7 +71,7 @@ static bool build_flv_meta_data(obs_output_t *context, enc_str(&enc, end, "onMetaData"); *enc++ = AMF_ECMA_ARRAY; - enc = AMF_EncodeInt32(enc, end, a_idx == 0 ? 14 : 9); + enc = AMF_EncodeInt32(enc, end, a_idx == 0 ? 20 : 15); enc_num_val(&enc, end, "duration", 0.0); enc_num_val(&enc, end, "fileSize", 0.0); diff --git a/plugins/obs-outputs/flv-mux.h b/plugins/obs-outputs/flv-mux.h index 0277cc41f..50c37e22b 100644 --- a/plugins/obs-outputs/flv-mux.h +++ b/plugins/obs-outputs/flv-mux.h @@ -1,13 +1,16 @@ /****************************************************************************** Copyright (C) 2014 by Hugh Bailey + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ @@ -28,4 +31,4 @@ extern void write_file_info(FILE *file, int64_t duration_ms, int64_t size); extern bool flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size, bool write_header, size_t audio_idx); extern void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset, -uint8_t **output, size_t *size, bool is_header); \ No newline at end of file + uint8_t **output, size_t *size, bool is_header); diff --git a/plugins/obs-outputs/flv-output.c b/plugins/obs-outputs/flv-output.c index 6880c56e7..680a954a9 100644 --- a/plugins/obs-outputs/flv-output.c +++ b/plugins/obs-outputs/flv-output.c @@ -1,13 +1,16 @@ /****************************************************************************** Copyright (C) 2014 by Hugh Bailey + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ @@ -269,4 +272,4 @@ struct obs_output_info flv_output_info = { .stop = flv_output_stop, .encoded_packet = flv_output_data, .get_properties = flv_output_properties -}; \ No newline at end of file +}; diff --git a/plugins/obs-outputs/ftl-sdk/.gitignore b/plugins/obs-outputs/ftl-sdk/.gitignore deleted file mode 100644 index 414ed9d80..000000000 --- a/plugins/obs-outputs/ftl-sdk/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -build -.vscode/* -!.vscode/settings.json -*.orig -.vs/* diff --git a/plugins/obs-outputs/ftl-sdk/.gitmodules b/plugins/obs-outputs/ftl-sdk/.gitmodules deleted file mode 100644 index c20f430f4..000000000 --- a/plugins/obs-outputs/ftl-sdk/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "libcurl"] - path = libcurl - url = https://github.com/curl/curl -[submodule "libjansson"] - path = libjansson - url = https://github.com/akheron/jansson diff --git a/plugins/obs-outputs/ftl-sdk/CMakeLists.txt b/plugins/obs-outputs/ftl-sdk/CMakeLists.txt deleted file mode 100644 index efd82428d..000000000 --- a/plugins/obs-outputs/ftl-sdk/CMakeLists.txt +++ /dev/null @@ -1,117 +0,0 @@ -cmake_minimum_required (VERSION 2.8.0) -enable_language(C) -project(libftl) - -option(DISABLE_AUTO_INGEST "Set to TRUE to disable auto ingest feature which removes curl and jansson dependancies" FALSE) -MESSAGE( STATUS "FTL DISABLE_AUTO_INGEST: " ${DISABLE_AUTO_INGEST}) - -find_package(Threads REQUIRED) - -set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - -if (DISABLE_AUTO_INGEST) - add_definitions(-DDISABLE_AUTO_INGEST) -endif() - -# We will only try to include curl if we have auto ingest enabled. -if (NOT DISABLE_AUTO_INGEST) - FIND_PACKAGE(CURL) -endif() -if (NOT CURL_FOUND AND NOT DISABLE_AUTO_INGEST) - SET(CURL_DISABLE_NTLM ON CACHE BOOL "Disabling NTLM") - SET(CURL_DISABLE_TELNET ON CACHE BOOL "Disabling Telnet") - SET(CURL_DISABLE_LDAP ON CACHE BOOL "Disabling Ldap") - SET(CURL_DISABLE_LDAPS ON CACHE BOOL "Disabling secure ldap") - SET(BUILD_CURL_EXE OFF CACHE BOOL "Building libcurl") - SET(HTTP_ONLY ON CACHE BOOL "using compiling HTTP") - SET(BUILD_TESTING OFF CACHE BOOL "Not building Tests") - add_subdirectory(libcurl) - SET(CURL_INCLUDE_DIRS libcurl/include ${CMAKE_CURRENT_BINARY_DIR}/libcurl/include/curl) - SET(CURL_LIBRARIES libcurl) - include_directories(${CURL_INCLUDE_DIRS}) -endif() - -# We will only try to include lib jansson if auto ingest is enabled. -SET(JANSSON_LIBRARIES "") -if (NOT DISABLE_AUTO_INGEST) - SET(JANSSON_BUILD_DOCS OFF CACHE BOOL "Jansson docs off") - SET(JANSSON_WITHOUT_TESTS ON CACHE BOOL "Jansson build without tests") - SET(JANSSON_EXAMPLES OFF CACHE BOOL "Jansson disable examples") - SET(USE_WINDOWS_CRYPTOAPI off) - add_subdirectory(libjansson) - include_directories(${CMAKE_CURRENT_BINARY_DIR}/libjansson/include) - SET(JANSSON_LIBRARIES jansson) -endif() - -if (WIN32) - set(FTL_PLATFORM_FILES ftl_app/win32/xgetopt.c - ftl_app/win32/xgetopt.h - ftl_app/win32/ctrlc_handler.c) - #set(FTL_PLATFORM_LIBS kernel32 user32 gdi32 advapi32 ) - #set(FTL_PLATFORM_LIBS ws2_32 ) - set(FTLSDK_PLATFORM_FILES libftl/win32/socket.c - libftl/win32/socket.h - libftl/win32/threads.c - libftl/win32/threads.h) - include_directories(libftl/win32) -else() - set(FTL_PLATFORM_FILES ftl_app/posix/ctrlc_handler.c) - set(FTLSDK_PLATFORM_FILES libftl/posix/socket.c - libftl/posix/socket.h - libftl/posix/threads.c - libftl/posix/threads.h) - include_directories(libftl/posix) -endif() - -add_library(ftl SHARED libftl/hmac/hmac.c - libftl/hmac/hmac.h - libftl/hmac/sha2.c - libftl/hmac/sha2.h - libftl/gettimeofday/gettimeofday.c - libftl/gettimeofday/gettimeofday.h - libftl/ftl-sdk.c - libftl/handshake.c - libftl/ingest.c - libftl/ftl_helpers.c - libftl/media.c - libftl/logging.c - libftl/ftl.h - libftl/ftl_private.h - ${FTLSDK_PLATFORM_FILES}) -include_directories(libftl libftl/gettimeofday) - -set_target_properties(ftl PROPERTIES VERSION "0.5.0") -set_target_properties(ftl PROPERTIES SOVERSION 0) - -target_link_libraries(ftl ${CURL_LIBRARIES} ${JANSSON_LIBRARIES}) - -if(WIN32) - target_link_libraries(ftl ws2_32) -endif() - -add_executable(ftl_app - ftl_app/main.c - ftl_app/main.h - ftl_app/file_parser.c - ftl_app/file_parser.h - ftl_app/gettimeofday.c - ftl_app/gettimeofday.h - ftl_app/bitstream.c - ftl_app/bitstream.h - ftl_app/cavlc.c - ftl_app/cavlc.h - ftl_app/decode.c - ftl_app/decode.h - ftl_app/nalu.c - ftl_app/nalu.h - ftl_app/utils.c - ftl_app/utils.h - ftl_app/dec_obj.h - ${FTL_PLATFORM_FILES}) - -target_link_libraries(ftl_app ftl ${CURL_LIBRARIES} ${JANSSON_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${FTL_PLATFORM_LIBS}) -target_include_directories(ftl_app PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/ftl_app) - -# Install rules -install(TARGETS ftl DESTINATION lib) \ No newline at end of file diff --git a/plugins/obs-outputs/ftl-sdk/Doxyfile b/plugins/obs-outputs/ftl-sdk/Doxyfile deleted file mode 100644 index faf2a4fe0..000000000 --- a/plugins/obs-outputs/ftl-sdk/Doxyfile +++ /dev/null @@ -1,2303 +0,0 @@ -# Doxyfile 1.8.6 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = "FTL SDK" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = 0.1 - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "Source Developement Kit for Mixer's FTL Protocol" - -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = docs - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. -# -# Note For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. -# Note: If this tag is empty the current directory is searched. - -INPUT = - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = "*.h" - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /