diff --git a/.travis.yml b/.travis.yml index 00376f9..a61ade1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: cpp +cache: + ccache: true + env: global: # AWS S3 creds @@ -8,6 +11,12 @@ env: # secret - secure: "JRQVU2zgC3hY6CEY+Crmh/upp93En0BzKaLcsuBT538johNlK7m5hn3m2UOw63seLvBvVaKKWUDj9N986a3DwcXxWPMyF/9ctXgNWy39WzaVWxrbVR5nQB1fdiRp5YEgkoVN+gEm3OVF7sV5AGzh5/8CvEdRCoTLIGgMGHxW9mc=" + # ccache + - USE_CCACHE=1 + - CCACHE_COMPRESS=1 + - CCACHE_MAXSIZE=200M + - CCACHE_CPP2=1 + matrix: include: - os: osx @@ -41,12 +50,19 @@ deploy: all_branches: true # The channel name "azubu.il.us.quakenet.org#obs-dev" is encrypted against jp9000/obs-studio to prevent IRC spam of forks +#notifications: +# irc: +# skip_join: false +# template: +# - "[Travis CI|%{result}] %{repository_name}/%{branch} (%{author} - %{commit_subject}) %{build_url}" +# channels: +# - secure: k9j7+ogVODMlveZdd5pP73AVLCFl1VbzVaVon0ECn3EQcxnLSpiZbc6l+PnIUKgee5pRKtUB4breufgmr4puq3s69YeQiOVKk5gx2yJGZ5jGacbSne0xTspzPxapiEbVUkcJ2L7gKntDG4+SUiW67dtt4G26O7zsErDF/lY/woQ= +# on_failure: always +# on_success: change notifications: - irc: - skip_join: false - template: - - "[Travis CI|%{result}] %{repository_name}/%{branch} (%{author} - %{commit_subject}) %{build_url}" - channels: - - secure: k9j7+ogVODMlveZdd5pP73AVLCFl1VbzVaVon0ECn3EQcxnLSpiZbc6l+PnIUKgee5pRKtUB4breufgmr4puq3s69YeQiOVKk5gx2yJGZ5jGacbSne0xTspzPxapiEbVUkcJ2L7gKntDG4+SUiW67dtt4G26O7zsErDF/lY/woQ= - on_failure: always + webhooks: + urls: + - secure: T5RBY818nO40nr5eC8pdrCfAdQKGkjQdbyYw7mfFrhxWxgt/U5tyKXpX0l9zNGfobS0SnLSqF71OrfW04V97oijXx3q5Y24xV6mSrlLQZOq19+XvGp82LDpkVd4yi2N0kBYpoANB9Pkof4jWT/rKfdQCQttluOLjgr5SM0uWHRg= on_success: change + on_failure: always + diff --git a/AUTHORS b/AUTHORS index 5ca5194..a2f5192 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,19 +7,20 @@ fryshorts BtbN John Bradley Gol-D-Ace -Colin Edwards Richard Stanway +Colin Edwards Zachary Lund Michael Fabian Dirks Martell Malone +cg2121 Christoph Hohmann HomeWorld -cg2121 dodgepong -derrod Ryan Foster +derrod Radzaquiel Socapex +juvester Skyler Lipthay SuslikV Arkkis @@ -29,13 +30,14 @@ Danni Jess Mayo Kris Moore Carl Fürstenberg -juvester Anry CoDEmanX Ján Mlynek +Kurt Kartaltepe Manuel Kroeber sorayuki Alexandre Vicenzi +Andrew Surzhynskyi Benjamin Klettbach bl Bl00drav3n @@ -44,21 +46,21 @@ Charles Ray Shisler III Jeremiah Senkpiel John R. Bradley Kilian von Pflugk +mntone Serge Paquet shiina424 shousa Timo Gurr adray Andrei Nistor -Andrew Surzhynskyi Azat Khasanshin Ben Torell bootkiller Brian S. Stephan +e00E Eric Bataille Joseph El-Khouri jpk -Kurt Kartaltepe Lexsus Lionheart Zhang paibox @@ -89,6 +91,8 @@ CallumHoward Cam CommanderRoot Copy Liu +craftwar +Cray Elliott cryptonaut Dan Dascalescu David Cooper @@ -105,6 +109,7 @@ Guillermo A. Amaral Gökberk Yaltıraklı Haden F Iblis Lin +Igor Bochkariov Jake Probst Jamy Timmermans Jimmy Berry @@ -134,8 +139,10 @@ nd Nicolas F Night Olle Kelderman +Palakis pantonvich partouf +Patrick Ancillotti Peter SZTANOJEV pipll raincomplex @@ -147,11 +154,15 @@ Ryan Sullivan sam8641 Seth Murphy Seung-Woo Kim +Shamun +Shaolin Simon +SoraYuki Teemu Kauhanen thekrzos Thomas McGrew TotalCaesar659 +trwnh vic vividnightmare VodBox @@ -182,6 +193,8 @@ Basque etxondoko Gol D. Ace (goldace) dodgepong +Bengali + shamuntohamd Bulgarian kalmarin Seyhan Halil (yildirim17) @@ -205,6 +218,8 @@ Chinese Simplified copyliu wwj402_github Bing Feng (fengbing123) + Hexcolyte + Gol D. Ace (goldace) dodgepong WaterOtaku Chinese Traditional @@ -217,7 +232,10 @@ Chinese Traditional Inndy.Lin (inndy) Meng Hao Li (GazCore) Gol D. Ace (goldace) + Wingo Chan (wingo19932003) + ak-47root Watson Tsai (ashaneba) + Han-Jen Cheng (notexist) cai_miao Jimmy Huang (f56112000) chaironeko @@ -233,9 +251,9 @@ Czech Kiznoh Danish Jens Hyllegaard (Hyllegaard) + NCAA Anders G. Jørgensen (spirit55555) MaltahlGaming (maltahlgaming) - NCAA Anders Urban (minikaliffen) Christian Henriksen (cnhenriksen) Gol D. Ace (goldace) @@ -243,34 +261,43 @@ Danish Dutch Eric Bataille (ThoNohT) Michel Snippe (michelsnippe) - Nicole (NIsengo) + Greendweller + robbert0891 (robbertoorschot38) + Nicole (Dutchess_Nicole) Jasper J (JassieJ) Gol D. Ace (goldace) + Bo Alsemgeest (bo.alsemgeest.wausie) JorRy + Julian Meijboom (julianmeijboom) + markpc Estonian MartinEwing AndresTraks Finnish ArkkisN (j) - dodgepong Jarska - Jarppi (Jarppi01) + dodgepong + Obama (Obama44) Gol D. Ace (goldace) chaironeko French radzaquiel - Yberion Nunzio Conte (nunzioconte54) - Stéphane Lepin (Palakis) + Yberion Léo (leeo97one) + Stéphane Lepin (Palakis) DoK_- BoboopTeam DarkInFire + Ben Turner (ben-turner) steve_fr Grisou2907 + Theguiguix McGuygnol Gabriel Dugny (Gabigabigo) Gol D. Ace (goldace) + GANGAT Naeem (zboggum) + kyllian (tardigradeus) dodgepong chaironeko Galician @@ -288,12 +315,12 @@ German Dennis Giebert (Isegrim) (isegrimderwolf) Jonathan (macburgerjunior) Robin Hielscher (Jack0r) + Tim (robske_110) (robske110) BoJustus Jonas Otto (jottosmail) mdod Prince_of_Raop Tiim - Tim (robske_110) (robske110) WurstOnAir Greek Mepharees @@ -306,6 +333,8 @@ Hebrew amirsher Chemi epic_ziver_D +Hindi + shamuntohamd Hungarian Gige Adam Liszkai (adamos42) @@ -314,13 +343,13 @@ Hungarian Italian LordShadow95 Marocco2 + smart2128 dodgepong Edoardo Macrì (edomacri) - smart2128 Edoardo “OfficialDJMela” Macrì (agersforum) ScemEnzo - Fisherozzo Gol D. Ace (goldace) + Fisherozzo Sergio Beneduce (sbeneduce) SkyLion Japanese @@ -344,9 +373,10 @@ Norwegian Bokmal Tommy (nwgat) Oddbjørn Grytdal (Fooshi) Decicus + Sander Skjegstad (r530er) + Legend27 Gol D. Ace (goldace) areedw - Legend27 chaironeko Mats Andreassen (MatsA) Pirate English @@ -371,17 +401,18 @@ Portuguese joaoboia Gol D. Ace (goldace) Portuguese, Brazilian + Shaolin (admshao) Ramon Mendes (rbrgameplays) Fabio Madia (Shaolin) - Burkes TFSThiagoBR98 CaioWzy clr0dr1g + mizifih aalonsomb André Gama (ToeOficial) Gol D. Ace (goldace) - dodgepong ThisGuy + dodgepong Romanian Cristian Silaghi (stelistcristi) banrek @@ -396,12 +427,12 @@ Russian dodgepong Pavel (Shevalie) Maxim Gribanov (MaximGribanov) + Bugo Andy (anry025) fromgate Gol D. Ace (goldace) Andrei Stepanov (adem4ik) Vlad (KoTmaxHo) - Bugo Mixaill Sergei Fug1t1v3 (fug) Walt Gee (vovanych) @@ -418,17 +449,19 @@ Slovak Ján M (longmoped) Anton Lokaj (anlo) LoLLy Nka (lollynka279) + Gol D. Ace (goldace) Slovenian kristjan.krusic (krusic22) - ArcaneWater Gol D. Ace (goldace) + ArcaneWater dodgepong Spanish Roberto Lorenzo (HonzoNebro) - Marcos Vidal Martinez (M4RK22) Jaime Martinez Rincon (mrjaime1999) + Marcos Vidal Martinez (M4RK22) Jaime Muñoz Martín (jmmartin_5) Maximiliano Schtroumpftech Pena-Roig (som2tokmynam) + Ruben Deig Ramos (rdeigramos) Eleazar Córcoles (MtrElee3) Gol D. Ace (goldace) Sigge Stjärnholm (Kladdy) @@ -453,6 +486,7 @@ Thai 盛凤阁 (execzero) nongnoobjung (kitcharuk_4) dodgepong + Gol D. Ace (goldace) Turkish Ali Kömesöğütlü (Mobile46) (byzlo685) omer.karagoz (mrkaragoz) @@ -469,9 +503,11 @@ Ukrainian Юрій (Devinit) Andy (anry025) Maksym Tymoshyk (maximillian_) +Urdu (Pakistan) + shamuntohamd Vietnamese Johnny “max20091” Utah (boostyourprogram) Hưng Nguyễn (hoyostudio) Hà Phi Hùng (haphihungcom) + Gol D. Ace (goldace) dodgepong - NCAA diff --git a/CI/before-deploy-win.cmd b/CI/before-deploy-win.cmd index 2be9c5f..0597fe0 100644 --- a/CI/before-deploy-win.cmd +++ b/CI/before-deploy-win.cmd @@ -1,3 +1,3 @@ -xcopy /e C:\projects\obs-studio\build32\rundir\RelWithDebInfo C:\projects\obs-studio\build\ -robocopy C:\projects\obs-studio\build64\rundir\RelWithDebInfo C:\projects\obs-studio\build\ /E /XC /XN /XO +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 diff --git a/CI/before-script-linux.sh b/CI/before-script-linux.sh index 7a42daa..88ba09f 100755 --- a/CI/before-script-linux.sh +++ b/CI/before-script-linux.sh @@ -1,5 +1,6 @@ #!/bin/sh set -ex +ccache -s || echo "CCache is not available." mkdir build && cd build cmake .. diff --git a/CI/before-script-osx.sh b/CI/before-script-osx.sh index 922816b..e76772d 100755 --- a/CI/before-script-osx.sh +++ b/CI/before-script-osx.sh @@ -1,3 +1,6 @@ +# Make sure ccache is found +export PATH=/usr/local/opt/ccache/libexec:$PATH + mkdir build cd build cmake -DENABLE_SPARKLE_UPDATER=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 -DDepsPath=/tmp/obsdeps -DVLCPath=$PWD/../../vlc-master -DBUILD_BROWSER=ON -DCEF_ROOT_DIR=$PWD/../../cef_binary_${CEF_BUILD_VERSION}_macosx64 .. diff --git a/CI/install-dependencies-linux.sh b/CI/install-dependencies-linux.sh index 134756b..51dc82c 100755 --- a/CI/install-dependencies-linux.sh +++ b/CI/install-dependencies-linux.sh @@ -1,19 +1,14 @@ #!/bin/sh set -ex -sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-next -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 \ libcurl4-openssl-dev \ + libfdk-aac-dev \ libfontconfig-dev \ libfreetype6-dev \ libgl1-mesa-dev \ @@ -22,8 +17,6 @@ sudo apt-get install -y \ libpulse-dev \ libqt5x11extras5-dev \ libspeexdsp-dev \ - libswresample-ffmpeg-dev \ - libswscale-ffmpeg-dev \ libudev-dev \ libv4l-dev \ libvlc-dev \ @@ -34,4 +27,14 @@ sudo apt-get install -y \ libxcomposite-dev \ libxinerama-dev \ pkg-config \ - qtbase5-dev + qtbase5-dev \ + yasm \ + zlib1g-dev + +# FFmpeg +cd .. +git clone --depth 1 git://source.ffmpeg.org/ffmpeg.git +cd ffmpeg +./configure --enable-shared +make -j2 +sudo make install diff --git a/CI/install-dependencies-osx.sh b/CI/install-dependencies-osx.sh index 69c71e9..7e7ce68 100755 --- a/CI/install-dependencies-osx.sh +++ b/CI/install-dependencies-osx.sh @@ -1,37 +1,43 @@ # Exit if something fails set -e -git fetch --tags +# Echo all commands before executing +set -v + +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 -curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg -f --retry 5 -C - +wget --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 -brew install qt5 jack speexdsp +#Base OBS Deps and ccache +brew install qt5 jack speexdsp ccache + +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 -curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/osx-deps.tar.gz -f --retry 5 -C - +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 -curl -L -o vlc-master.zip https://github.com/videolan/vlc/archive/master.zip -f --retry 5 -C - +wget --retry-connrefused --waitretry=1 -O vlc-master.zip https://github.com/videolan/vlc/archive/master.zip unzip -q ./vlc-master.zip # Get sparkle -curl -L -o ./sparkle.tar.bz2 https://github.com/sparkle-project/Sparkle/releases/download/1.16.0/Sparkle-1.16.0.tar.bz2 +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 -curl -kLO https://obs-nightly.s3-us-west-2.amazonaws.com/cef_binary_${CEF_BUILD_VERSION}_macosx64.tar.bz2 -f --retry 5 -C - +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 mkdir build diff --git a/CI/install/osx/obs.png b/CI/install/osx/obs.png index 364211c..44f19c0 100644 Binary files a/CI/install/osx/obs.png and b/CI/install/osx/obs.png differ diff --git a/CONTRIBUTING b/CONTRIBUTING deleted file mode 100644 index 59b209e..0000000 --- a/CONTRIBUTING +++ /dev/null @@ -1,64 +0,0 @@ -Contributing Information: - - - Our bug tracker is located at (currently linked to forum accounts): - https://obsproject.com/mantis/ - - - Development forums are currently located at: - https://obsproject.com/forum/list/general-development.21/ - - - Development IRC channel is primarily #obs-dev on QuakeNet - - - To contribute translations, see: - https://obsproject.com/forum/threads/how-to-contribute-translations-for-obs.16327/ - - -Contributing Guidelines: - - - Code and commits will be reviewed. Reviews will be blunt and honest, and - your change probably won't go through the first time, or may be outright - rejected. It may require many revisions before changes are finally - accepted. - - - If you want to avoid doing a lot of work only to have it rejected, discuss - what you want to change it in the main channels/forums/mailing lists before - actually working on it. Open source requires thick skin. Please don't be - discouraged if your changes don't go through, learn from it and get better. - - - Coding style is Linux-style KNF (kernel normal form). This means K&R, 80 - columns max, preferable maximum function length of approximately 42 lines, 8 - character width tabs, lower_case_names, etc. I chose this for the sake of - the project. Don't argue about it, just do it. It takes no time to do. - - See https://www.kernel.org/doc/Documentation/CodingStyle for a general - guideline (though not necessarily a rulebook, for example we allow the use - of boolean return values instead of ints for failure). - - NOTE: C++ is an exception to the lower_case_only rule, CamelCase is - encouraged (though not required) to distinguish it from C code. Just a - personal and subjective stylistic thing on my part. - - - Commits are not just changes to code; they should also be treated as - annotation to code. For that reason, do not put unrelated changes in a - single commit. Separate out different changes in to different commits, and - make separate pull requests for unrelated changes. Commits should be - formatted with the 50/72 standard, and should be descriptive and concise. - See http://chris.beams.io/posts/git-commit/ for a summary of how to make - good commit messages. - - - Core code is C only (unless there's an operating system specific thing that - absolutely requires another language). - - - Modules and UI may use C, C++, or Objective-C (for apple), though please try - to use C unless an API you're using requires C++ or Objective-C (such as - windows COM classes, or apple Objective-C APIs). - - - If you don't quite know what to work on and just want to help, the bug - tracker has a list of things that need to be worked on. - - - Try to respect the wishes of the author(s)/maintainer(s). A good maintainer - will always listen, and will often ask others on the project for their - opinions, but don't expect things to be completely democratic. - - - Do not use dependencies for the sake of convenience. There's enough - dependencies as it is. Use them only if you absolutely have to depend on - them. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..70cb070 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,90 @@ +Contributing +============ + +Quick Links for Contributing +---------------------------- + + - Compiling and building OBS Studio: + https://github.com/jp9000/obs-studio/wiki/Install-Instructions + + - Our bug tracker (linked to forum accounts): + https://obsproject.com/mantis/ + + - Development IRC channel: #obs-dev on QuakeNet + + - Development forum: + https://obsproject.com/forum/list/general-development.21/ + + - To contribute language translations, do not make pull requests. + Instead, use crowdin. Read here for more information: + https://obsproject.com/forum/threads/how-to-contribute-translations-for-obs.16327/ + +Coding Guidelines +----------------- + + - OBS Studio uses kernel normal form (linux variant), for more + information, please read here: + https://github.com/torvalds/linux/blob/master/Documentation/process/coding-style.rst + + - Avoid trailing spaces. To view trailing spaces before making a + commit, use "git diff" on your changes. If colors are enabled for + git in the command prompt, it will show you any whitespace issues + marked with red. + + - Tabs for indentation, spaces for alignment. Tabs are treated as 8 + columns wide. + + - 80 columns max + +Commit Guidlines +---------------- + + - OBS Studio uses the 50/72 standard for commits. 50 characters max + for the title (excluding module prefix), an empty line, and then a + full description of the commit, wrapped to 72 columns max. See this + link for more information: http://chris.beams.io/posts/git-commit/ + + - Make sure commit titles are always in present tense, and are not + followed by punctuation. + + - Prefix commit titles with the module name, followed by a colon and a + space (unless modifying a file in the base directory). When + modifying cmake modules, prefix with "cmake". So for example, if you + are modifying the obs-ffmpeg plugin:: + + obs-ffmpeg: Fix bug with audio output + + Or for libobs:: + + libobs: Fix source not displaying + + - If you still need examples, please view the commit history. + +Headers +------- + + There's no formal documentation as of yet, so it's recommended to read + the headers (which are heavily commented) to learn the API. + + Here are the most important headers to check out:: + + libobs/obs.h Main header + + libobs/obs-module.h Main header for plugin modules + + libobs/obs-source.h Creating video/audio sources + + libobs/obs-output.h Creating outputs + + libobs/obs-encoder.h Implementing encoders + + libobs/obs-service.h Implementing custom streaming services + + libobs/graphics/graphics.h Graphics API + + UI/obs-frontend-api/obs-frontend-api.h + Front-end API + + If you would like to learn from example, examine the default plugins + (in the subdirectory). All features of OBS Studio are + implemented as plugins. diff --git a/README b/README deleted file mode 100644 index c11d512..0000000 --- a/README +++ /dev/null @@ -1,174 +0,0 @@ - -What is OBS? - - This project is a rewrite of what was formerly known as "Open Broadcaster - Software", software originally designed for recording and streaming live - video content, efficiently. - -Bug Tracker: https://obsproject.com/mantis/ - - We are no longer using GitHub issues! Please use Mantis, and only report - bugs and major issues. Do NOT use mantis to ask questions or request - features, please keep that to the forums. - - Forum accounts are now linked to Mantis Bug Tracker. To use the bug - tracker, simply log in to the forums and then go to the bug tracker link - above. - -What's the goal of rewriting OBS? - - - Make it multiplatform. Use multiplatform libraries/functions/classes where - possible to allow this. Multi-platform support was one of the primary - reasons for the rewrite. This also means using a UI toolkit will be - necessary for user interface. It also means allowing the use of OpenGL as - well as Direct3D. - - - Separate the application from the core, allowing custom application of - the core if desired, and easier extending of the user interface. - - - Simplify complex systems to not only make it easier to use, but easier to - maintain. - - - Write a better core API, and design the entire system to be modular. - - - Now that we have much more experience, improve the overall design of all - the subsystems/API, and minimize/eliminate design flaws. Make it so we can - do all the things we've had trouble with before, such as custom outputs, - multiple outputs at once, better handling of inputs, custom services. - - - Make a better/cleaner code base, use better coding standards, use standard - libraries where possible (not just STL and C standard library, but also - things like ffmpeg as well), and improve maintainability of the project as a - whole. - - - Implement a new API-independent shader/effect system allowing better and - easier shaders usage and customization without having to duplicate shader - code. - - - Better device support. Again, I didn't know what I was getting into when - I originally started writing code for devices. It evolved into a totally - convoluted mess. I would have improved the existing device plugin code, but - it was just all so fundamentally bad and flawed that it would have been - detrimental to progression to continue working on it rather than rewrite it. - - -What was wrong with the original OBS? - - The original OBS was rewritten not because it was bad, at least in terms of - optimization. Optimization and graphics are things I love. However, there - were some serious problems with the code and design that were deep and - fundamental, which prevented myself and other developers from being able to - improve/extend the application or add new features very easily. - - First, the design flaws: - - - The original OBS was completely and hopelessly hard-coded for windows, - and only windows. It was just totally impossible to use it on other - systems. - - - All the sub-systems were written before I really knew what I was getting - into. When I started the project, I didn't really fully comprehend the - scope of what I would need or how to properly design the project. My - design and plans for the application were just to write something that - would "stream games and a webcam, with things like overlays and such." - This turned out fine for most casual gamers and streamers (and very - successful), but left anyone wanting to do anything more advanced left - massively wanting. - - - Subsystems and core functionalities intermingled in such a way that it - was a nightmare to get proper custom functionality out of it. Things - like QSV had to be meshed in with the main encoding loop, and it just - made things a nightmare to deal with. Custom outputs were nigh - impossible. - - - The API was poorly designed because most of it came after I originally - wrote the application, it was more of an afterthought, and plugin API - would routinely break for plugin developers due to changing C++ - interfaces (one of the reasons the core is now C). - - - API was intermeshed with the main executable. The OBSApi DLL was - nothing more than basically this mutant growth upon OBS.exe that allowed - plugin developers to barely write plugins, but all the important API - code was actually stored in the executable. Navigation was a total mess. - - - The graphics subsystem, while not bad, was incomplete, and though far - easier to use than bare D3D, wasn't ideal, and was hard-coded for D3D - specifically. - - - The devices and audio code was poor, I had no idea what I was getting into - when I started writing them in. I did not realize beforehand all the - device-specific quirks that each device/system could have. Some devices - had bad timing and quirks that I never anticipated while writing them. - I struggled with devices, and my original design for the audio subsystem - for example morphed over and over into an abomination that, though works, - is basically this giant duct-taped zombie monster. - - - Shaders were difficult to customize because they had to be duplicated if - you wanted slightly different functionality that required more than just - changing shader constants. - - - Orientation of sources was fixed, and required special code for each - source to do any custom modification of rotation/position/scale/etc. - This is one of those fundamental flaws that I look back on and regret, as - it was a stupid idea from the beginning. I originally thought I could - get more accurate source position/sizes, but it just turned out to be - totally bad. Should have been matrices from the beginning just like with - a regular 3D engine. - - Second, the coding flaws: - - - The coding style was inconsistent. - - - C++98, C-Style C++, there was no exception usage, no STL. C++ used - poorly. - - - Not Invented Here Syndrome everywhere. Custom string functions/classes, - custom templates, custom everything everywhere. To be fair, it was all - hand-me-down code from the early 2000s that I had become used to, but - that was no excuse -- C-standard libraries and the STL should have been - used from the beginning over anything else. That doesn't mean to say - that using custom stuff is always bad, but doing it to the extent I did - definitely was. Made it horrible to maintain as well, required extra - knowledge for plugin developers and anyone messing with the code. - - - Giant monolithic classes everywhere, the main OBS class was paricularly - bad in this regard. This meant navigation was a nightmare, and no one - really knew where to go or where to add/change things. - - - Giant monolithic functions everywhere. This was particularly bad - because it meant that functions became harder to debug and harder to - keep track of what was going on in any particular function at any given - time. These large functions, though not inefficient, were delicate and - easily breakable. (See OBS::MainCaptureLoop for a nightmarish example, - or the listbox subclass window procedure in WindowStuff.cpp) - - - Very large file sizes with everything clumped up into single files (for - another particularly nightmarish example, see WindowStuff.cpp) - - - Bad formatting. Code could go beyond 200 columns in some cases, making - it very unpleasant to read with many editors. Spaces instead of tabs, - K&R mixed with allman (which was admittedly my fault). - - -New (actual) coding guidelines - - - For the C code (especially in the core), guidelines are pretty strict K&R, - kernel style. See the linux kernel "CodingStyle" document for more - information. That particular coding style guideline is for more than just - style, it actually helps produce a better overall code base. - - - For C++ code, I still use CamelCase instead of all_lowercase just because - I prefer it that way, it feels right with C++ for some reason. It also - helps make it distinguishable from C code. - - - I've started using 8-column tabs for almost everything -- I really - personally like it over 4-column tabs. I feel that 8-column tabs are very - helpful in preventing large amounts of indentation. A self-imposed - limitation, if you will. I also use actual tabs now, instead of spaces. - Also, I feel that the K&R style looks much better/cleaner when viewed with - 8-column tabs. - - - Preferred maximum columns: 80. I've also been doing this because in - combination with 8-column tabs, it further prevents large/bad functions - with high indentation. Another self-imposed limitation. Also, it makes - for much cleaner viewing in certain editors that wrap (like vim). diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..6148529 --- /dev/null +++ b/README.rst @@ -0,0 +1,44 @@ +OBS Studio +=================================== + +What is OBS Studio? +------------------- + + OBS Studio is software designed for capturing, compositing, encoding, + recording, and streaming video content, efficiently. + + It's distributed under the GNU General Public License v2 - see the + accompanying COPYING file for more details. + +Quick Links +----------- + + - Website: https://obsproject.com + + - Help/Guides: https://github.com/jp9000/obs-studio/wiki + + - Forums: https://obsproject.com/forum/ + + - Build Instructions: https://github.com/jp9000/obs-studio/wiki/Install-Instructions + + - Bug Tracker: https://obsproject.com/mantis/ + + (Note: The bug tracker is linked to forum accounts. To use the bug + tracker, log in to a forum account) + +Contributing +------------ + + - If you wish to contribute code to the project, please make sure read + the coding and commit guidelines: + https://github.com/jp9000/obs-studio/blob/master/CONTRIBUTING.rst + + - If you wish to contribute translations, do not submit pull requests. + Instead, please use Crowdin. For more information read this thread: + https://obsproject.com/forum/threads/how-to-contribute-translations-for-obs.16327/ + + - Other ways to contribute are by helping people out with support on + our forums or in our community chat. Please limit support to topics + 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. diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 0dae204..1293514 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -36,6 +36,7 @@ else() endif() find_package(Qt5Widgets ${FIND_MODE}) +find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat) if(NOT Qt5Widgets_FOUND) if (ENABLE_UI) @@ -46,8 +47,11 @@ if(NOT Qt5Widgets_FOUND) endif() endif() + +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") find_package(Libcurl REQUIRED) include_directories(${LIBCURL_INCLUDE_DIRS}) @@ -107,17 +111,35 @@ elseif(UNIX) Qt5::X11Extras) endif() +set(obs_libffutil_SOURCES + ../deps/libff/libff/ff-util.c + ) +set(obs_libffutil_HEADERS + ../deps/libff/libff/ff-util.h + ) + +if(MSVC) + set_source_files_properties( + ../deps/libff/libff/ff-util.c + PROPERTIES COMPILE_FLAGS -Dinline=__inline + ) +endif() + set(obs_SOURCES ${obs_PLATFORM_SOURCES} + ${obs_libffutil_SOURCES} obs-app.cpp api-interface.cpp window-basic-main.cpp + window-basic-stats.cpp window-basic-filters.cpp window-basic-settings.cpp window-basic-interaction.cpp window-basic-properties.cpp + window-basic-auto-config.cpp window-basic-main-outputs.cpp window-basic-source-select.cpp + window-basic-auto-config-test.cpp window-basic-main-scene-collections.cpp window-basic-main-transitions.cpp window-basic-main-dropfiles.cpp @@ -153,14 +175,17 @@ set(obs_SOURCES set(obs_HEADERS ${obs_PLATFORM_HEADERS} + ${obs_libffutil_HEADERS} obs-app.hpp platform.hpp window-main.hpp window-basic-main.hpp + window-basic-stats.hpp window-basic-filters.hpp window-basic-settings.hpp window-basic-interaction.hpp window-basic-properties.hpp + window-basic-auto-config.hpp window-basic-main-outputs.hpp window-basic-source-select.hpp window-license-agreement.hpp @@ -197,6 +222,10 @@ set(obs_HEADERS set(obs_UI forms/NameDialog.ui + forms/AutoConfigStartPage.ui + forms/AutoConfigVideoPage.ui + forms/AutoConfigStreamPage.ui + forms/AutoConfigTestPage.ui forms/OBSLicenseAgreement.ui forms/OBSLogReply.ui forms/OBSBasic.ui @@ -234,9 +263,9 @@ endif() target_link_libraries(obs libobs - libff Qt5::Widgets obs-frontend-api + ${FFMPEG_LIBRARIES} ${LIBCURL_LIBRARIES} ${obs_PLATFORM_LIBRARIES}) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 4612100..025dd2c 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -354,6 +354,21 @@ struct OBSStudioAPI : obs_frontend_callbacks { App()->PopUITranslation(); } + void obs_frontend_set_streaming_service(obs_service_t *service) override + { + main->SetService(service); + } + + obs_service_t *obs_frontend_get_streaming_service(void) override + { + return main->GetService(); + } + + void obs_frontend_save_streaming_service(void) override + { + main->SaveService(); + } + void on_load(obs_data_t *settings) override { for (auto cb : saveCallbacks) diff --git a/UI/data/locale.ini b/UI/data/locale.ini index 0e4e93f..efc9f70 100644 --- a/UI/data/locale.ini +++ b/UI/data/locale.ini @@ -119,4 +119,13 @@ Name=தமிழ் Name=Lietuvių kalba [et-EE] -Name=eesti keel \ No newline at end of file +Name=eesti keel + +[bn-BD] +Name=বাংলা ভাষা + +[hi-IN] +Name=हिन्दी + +[ur-PK] +Name=اردو \ No newline at end of file diff --git a/UI/data/locale/ar-SA.ini b/UI/data/locale/ar-SA.ini index f4ad127..146d2a5 100644 --- a/UI/data/locale/ar-SA.ini +++ b/UI/data/locale/ar-SA.ini @@ -50,6 +50,12 @@ Top="أعلى" Bottom="أسفل" + + + + + + QuickTransitions.SwapScenes="التبديل بين مشهدي المعاينة و الاخراج بعد عملية الانتقال" QuickTransitions.SwapScenesTT="يقوم بتبديل مشهد المعاينة مع مشهد الاخراج بعد عملية الانتقال بين المشاهد (اذا كان مشهد الاخراج الاصلي لازال موجوداً) \n هذا لن يقوم بالتراجع عن اي تغييرات قمت بها على مشهد الاخراج الأصلي." QuickTransitions.DuplicateScene="استنساخ المشهد" @@ -364,3 +370,4 @@ Basic.Settings.Advanced.Video.ColorRange.Full="كامل" + diff --git a/UI/data/locale/bg-BG.ini b/UI/data/locale/bg-BG.ini index 33ddabe..bba5fee 100644 --- a/UI/data/locale/bg-BG.ini +++ b/UI/data/locale/bg-BG.ini @@ -46,6 +46,12 @@ ResetOSXVSyncOnExit="Рестартиране на OSX V-синхронизац + + + + + + TitleBar.Profile="Профил" TitleBar.Scenes="Сцени" @@ -302,3 +308,4 @@ Hotkeys.NumLock="Num Lock" + diff --git a/UI/data/locale/bn-BD.ini b/UI/data/locale/bn-BD.ini new file mode 100644 index 0000000..53a3630 --- /dev/null +++ b/UI/data/locale/bn-BD.ini @@ -0,0 +1,561 @@ + +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 শতাংশ)" +PreviewProjector="পূর্ণ পর্দা জুড়ে প্রোজেক্টর (প্রিভিউ)" +SceneProjector="পূর্ণ পর্দা জুড়ে প্রোজেক্টর (দৃশ্য)" +SourceProjector="পূর্ণ পর্দা জুড়ে প্রোজেক্টর (সূত্র)" +Clear="পরিষ্কার" +Revert="প্রত্যাবর্তন" +Show="প্রদর্শন করা হবে" +Hide="সরিয়ে রাখ" +Untitled="শিরোনামহীন" +New="নতুন" +Duplicate="প্রতিলিপি" +Enable="সচল" +DisableOSXVSync="OSX ভি-সমকালীন নিষ্ক্রিয় করা হবে" +ResetOSXVSyncOnExit="প্রস্থান করার সময় ভি-সমকালীন OSX রিসেট করুন" +HighResourceUsage="এনকোডিং overloaded! ভিডিও সেটিংস ডাউন বাঁক বা দ্রুত এনকোডিং প্রিসেট ব্যবহার করে বিবেচনা করুন." +Transition="স্থানান্তর" +QuickTransitions="দ্রুত অবস্থান্তর" +Left="বামদিকে" +Right="ডানদিকে" +Top="শীর্ষ" +Bottom="নিচে" +Reset="পুনরায় ধার্য করুন" +Hours="ঘন্টা" +Minutes="মিনিট" +Seconds="সেকেন্ড" +Deprecated="অবচিত" +ReplayBuffer="রিপ্লে বাফার" +Import="ডাটা ইম্পোর্ট" +Export="ডাটা এক্সপোর্ট" + + + + + + + +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="যদি (আউটপুট মূল দৃশ্য এখনো বিদ্যমান আছে) প্রাক্-বীক্ষণ এবং আউটপুট দৃশ্য হলে পরে প্রক্রিয়াতে swaps। \nThis যে আউটপুট মূল দৃশ্যের জন্য তৈরি করা হয়েছে যাতে কোন পরিবর্তন বাতিল হবে না." +QuickTransitions.DuplicateScene="অনুরূপ দৃশ্য" +QuickTransitions.DuplicateSceneTT="একই দৃশ্য সম্পাদনা করতে পারবেন আউটপুট। \nTo উত্স সম্পাদনা বৈশিষ্ট্য আউটপুট পরিবর্তন ছাড়াই পরিবর্তন ছাড়াই রূপান্তর/দৃশ্যমানতা উৎস সম্পাদনা, অনুরূপ উৎস সক্রিয় করা হবে। \nChanging এই মান হবে রিসেট আউটপুট দৃশ্য বর্তমান থাকে (এটা এখনও আছে)." +QuickTransitions.EditProperties="উৎসের অনুলিপি" +QuickTransitions.EditPropertiesTT="সূত্র বৈশিষ্ট্য সম্পাদনা ছাড়া অনুরূপ দৃশ্য সক্রিয় হলে আউটপুট। \nThis পরিবর্তন কেবল ব্যবহার করা যেতে পারে অনুমতি দেয়, যখন একই দৃশ্যের এডিটিং,। \nCertain সূত্র (যেমন ধারণ বা প্রচার মাধ্যম) এটি সমর্থন করে না এবং আলাদাভাবে সম্পাদনা করা যাবে না। \nChanging এই মূল্য বর্তমান আউটপুট দৃশ্য পুনর্বিন্যাস করবে (যদি exists).\n\nWarning এখনো: উৎস duplicated করা হবে কারন -এর অতিরিক্ত সিস্টেম বা ভিডিও সম্পদ প্রয়োজন হতে পারে." +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="আউটপুট শুরু করতে ব্যর্থ হয়েছে। Details.\n\nNote জন্য লগ চেক করুন: NVENC অথবা এএমডি encoders ব্যবহার করলে আপনার ভিডিও ড্রাইভার আপ-টু-ডেট রয়েছে নিশ্চিত করুন." + +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="কোন hotkey সেট!" +Output.ReplayBuffer.NoHotkey.Msg="কোন রিপ্লে বাফারের জন্য সেট hotkey ছাড়া। \"সংরক্ষণ\" hotkey রিপ্লে রেকর্ডিং সংরক্ষণের জন্য ব্যবহার করতে সেট করুন." + +Output.BadPath.Title="খারাপ পার্ট অনুসন্ধানname" +Output.BadPath.Text="কনফিগার ফাইল আউটপুট পথটি সঠিক নয়। অনুগ্রহ করে একটি কার্যকর পথ তৈরি করা হয়েছে নিশ্চিত করতে, আপনার সেটিংস পরীক্ষা করুন." + +LogReturnDialog="সফলভাবে আপলোড লগ" +LogReturnDialog.CopyURL="URL অনুলিপি করুন" +LogReturnDialog.ErrorUploadingLog="লগ ফাইল আপলোড করা হচ্ছে সমস্যা" + +LicenseAgreement="লাইসেন্স চুক্তি" +LicenseAgreement.PleaseReview="দয়া করে আগে OBS. ব্যবহার করে এই অনুজ্ঞা শর্তাবলী পর্যালোচনা করুন এই প্রোগ্রামটি ব্যবহার করে আপনি যে আপনি পড়েছেন এবং v2.0 GNU সাধারণ পাবলিক লাইসেন্সের শর্তাবলীর সাথে সম্মত হচ্ছেন স্বীকার করেন। অনুগ্রহ করে এই চুক্তির বাকি দেখার সংঘ." +LicenseAgreement.ClickIAgreeToContinue="আপনি এই চুক্তির শর্তাদি স্বীকার করেন, আমি Agree অব্যাহত রাখার জন্য ক্লিক করুন। আপনি OBS. ব্যবহার করতে এই চুক্তি স্বীকার করতে হবে." +LicenseAgreement.IAgree="আমি তোমাকে শ্রদ্ধা করি" +LicenseAgreement.Exit="প্রস্থান করুন" + +Remux.SourceFile="OBS রেকর্ডিং" +Remux.TargetFile="গন্তব্য ফাইল" +Remux.Remux="Remux" +Remux.OBSRecording="OBS রেকর্ডিং" +Remux.FinishedTitle="Remuxing শেষ" +Remux.Finished="Remuxed রেকর্ড" +Remux.FinishedError="রেকর্ড remuxed, কিন্তু ফাইলটি অসম্পূর্ণ হতে পারে" +Remux.SelectRecording="OBS রেকর্ডিং করুন …" +Remux.SelectTarget="গন্তব্য ফাইল নির্বাচন করুন…" +Remux.FileExistsTitle="লক্ষ্যবস্তু ফাইলটি বিদ্যমান" +Remux.FileExists="লক্ষ্যবস্তু ফাইলটি বিদ্যমান, এটি প্রতিস্থাপন করতে চান?" +Remux.ExitUnfinishedTitle="Remuxing অগ্রসর হচ্ছে" +Remux.ExitUnfinished="Remuxing শেষ হয় না, এখন বন্ধ হতে পারে যেন গন্তব্য ফাইলটি ব্যবহারের অযোগ্য। আপনি কি নিশ্চিত যে আপনি remuxing বন্ধ করতে \nAre?" + +UpdateAvailable="নতুন আপডেট পাওয়া যাবে" +UpdateAvailable.Text="সংস্করণ %1.%2.%3 পাওয়া যায়। ডাউনলোড করতে এখানে ক্লিক করুন।" + +Basic.DesktopDevice1="ডেস্কটপ অডিও" +Basic.DesktopDevice2="ডেস্কটপ অডিও 2" +Basic.AuxDevice1="মাইক/সহায়ক" +Basic.AuxDevice2="মাইক/সহায়ক 2" +Basic.AuxDevice3="মাইক/সহায়ক 3" +Basic.AuxDevice4="মাইক/4 সহায়ক" + +Basic.Scene="দৃশ্য" +Basic.DisplayCapture="অধিগ্রহণ প্রদর্শন করুন" + +Basic.Main.PreviewConextMenu.Enable="প্রাকদর্শন সক্রিয় করুন" + +ScaleFiltering="ফিল্টারিং এর আকার পরিবর্তন করো" +ScaleFiltering.Point="বিন্দু" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicubic" +ScaleFiltering.Lanczos="Lanczos" + +Deinterlacing="Deinterlacing" +Deinterlacing.Discard="অগ্রাহ্য" +Deinterlacing.Retro="রেট্রো" +Deinterlacing.Blend="মিশ্রণ" +Deinterlacing.Blend2x="2 x মিশে" +Deinterlacing.Linear="রৈখিক" +Deinterlacing.Linear2x="রৈখিক 2 x" +Deinterlacing.Yadif="Yadif" +Deinterlacing.Yadif2x="Yadif 2 x" +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.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="পথ/ইউআরএল যুক্ত করুন" +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 (s) (%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="আপনি সূত্র যোগ করার নূন্যতম ১ দৃশ্য থাকতে হবে." + +Basic.Main.Scenes="দৃশ্য" +Basic.Main.Sources="সোর্স" +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="(অবকাশ বাতিল) Streaming বন্ধ" + +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="CW 90 ডিগ্রী ঘোরাও" +Basic.MainMenu.Edit.Transform.Rotate90CCW="CW 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.Toolbars.Listboxes="এবং Listboxes" +Basic.MainMenu.View.SceneTransitions="ঘটনাস্থলে অবস্থান্তর" +Basic.MainMenu.View.StatusBar="স্ট্যাটাস বার" + +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.Website="দর্শন ও ওয়েবসাইট" +Basic.MainMenu.Help.Logs="এবং লগ ফাইল" +Basic.MainMenu.Help.Logs.ShowLogs="এবং লগ ফাইলগুলো প্রদর্শন করুন" +Basic.MainMenu.Help.Logs.UploadCurrentLog="আপলোড এবং বর্তমান লগ ফাইল" +Basic.MainMenu.Help.Logs.UploadLastLog="আপলোড এবং সর্বশেষ লগ ফাইল" +Basic.MainMenu.Help.Logs.ViewCurrentLog="&View লগ দেখাও" +Basic.MainMenu.Help.CheckForUpdates="হালনাগাদের জন্য পরীক্ষা করুন" + +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.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.Stream="ধারা" +Basic.Settings.Stream.StreamType="স্ট্রিম ধরন" + +Basic.Settings.Output="আউটপুট" +Basic.Settings.Output.Format="আমল লেখকবৃন্দ বিন্যাস" +Basic.Settings.Output.Encoder="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.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="Muxer সেটিংস (যদি থাকে)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Keyframe বিরতি (ফ্রেম)" +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 বছর, চার digits\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%% এক শতাংশ sign\n%a সংক্ষিপ্ত সপ্তান্তের name\n%A পূর্ণ সপ্তান্তের name\n%b সংক্ষিপ্ত মাস name\n%B পুরো মাসের name\n%d দিনে মাসের শূন্য-প্যাড (01-31)\n%H ঘন্টা 24 জ বিন্যাস (00-23) \n%I ঘন্টা 12h বিন্যাস (01-12)\n%m মাস দশমিক সংখ্যা হিসেবে (01-12)\n%M মিনিট (00-59) \n%p বিকাল বা রাত designation\n%S দ্বিতীয় (00-61)\n%y সালের শেষ দুটি অঙ্ক থেকে ইউটিসি বা timezone\n নাম বা abbreviation\n%Z (00-99)\n%Y Year\n%z ISO 8601 অফসেট সময় অঞ্চল নাম বা abbreviation\n" + +Basic.Settings.Video="ভিডিও" +Basic.Settings.Video.Adapter="ভিডিও অভিযোজক:" +Basic.Settings.Video.BaseResolution="বেস (ক্যানভাস) নিয়মন:" +Basic.Settings.Video.ScaledResolution="আউটপুট রেজুলেশন: (আকার পরিবর্তিত):" +Basic.Settings.Video.DownscaleFilter="Downscale ফিল্টার:" +Basic.Settings.Video.DisableAeroWindows="এ্যারো (শুধুমাত্র 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] (অর্থাৎ 1920 x 1080) থাকতে হবে" +Basic.Settings.Video.CurrentlyActive="ভিডিও আউটপুট বর্তমানে সক্রিয় আছে। ভিডিও বিন্যাসনসমূহ পরিবর্তন করতে কোন ভয়েস থেকে চালু করুন।." +Basic.Settings.Video.DisableAero="এরো নিষ্ক্রিয় করুন" + +Basic.Settings.Video.DownscaleFilter.Bilinear="Bilinear (সবচেয়ে দ্রুত, কিন্তু ঘোলাটে হলে স্কেল)" +Basic.Settings.Video.DownscaleFilter.Bicubic="Bicubic (Sharpened স্কেল, 16 নমুনা)" +Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Sharpened স্কেল, 32 নমুনা)" + +Basic.Settings.Audio="অডিও" +Basic.Settings.Audio.SampleRate="নমুনা হার" +Basic.Settings.Audio.Channels="চ্যানেলসমূহ" +Basic.Settings.Audio.DesktopDevice="ডেস্কটপ অডিও ডিভাইস" +Basic.Settings.Audio.DesktopDevice2="ডেস্কটপ অডিও ডিভাইস" +Basic.Settings.Audio.AuxDevice="মাইক/সহযোগী অডিও ডিভাইস" +Basic.Settings.Audio.AuxDevice2="মাইক/সহযোগী অডিও ডিভাইস" +Basic.Settings.Audio.AuxDevice3="মাইক/সহযোগী অডিও ডিভাইস" +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="Normal" +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.StreamDelay="স্ট্রিম বিলম্ব" +Basic.Settings.Advanced.StreamDelay.Duration="দৈর্ঘ্য (সেকেন্ড)" +Basic.Settings.Advanced.StreamDelay.Preserve="বিরোধিতায় লিপ্ত পয়েন্ট (বাড়তে দেরি) সংরক্ষণের সময় পুনরায় সংযোগ করা হচ্ছে" + +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="মুছে ফেলা" +Hotkeys.Home="হোম" +Hotkeys.End="সমাপ্ত" +Hotkeys.PageUp="উপরের পাতা" +Hotkeys.PageDown="নিচের পাতা" +Hotkeys.NumLock="Num লক" +Hotkeys.ScrollLock="স্ক্রল লক" +Hotkeys.CapsLock="ক্যাপস লক" +Hotkeys.Backspace="ব্যাকস্পেস" +Hotkeys.Tab="ট্যাব" +Hotkeys.Print="প্রিন্ট" +Hotkeys.Pause="বিরাম" +Hotkeys.Left="বামদিকে" +Hotkeys.Right="ডানদিকে" +Hotkeys.Up="উপরে" +Hotkeys.Down="নিচের" +Hotkeys.Windows="উইন্ডোজ" +Hotkeys.Super="সুপার" +Hotkeys.Menu="মেনু" +Hotkeys.Space="জায়গা" +Hotkeys.NumpadNum="Numpad %1" +Hotkeys.NumpadMultiply="Numpad দ্বিগুণ" +Hotkeys.NumpadDivide="Numpad ভাগ" +Hotkeys.NumpadAdd="Numpad যুক্ত করুন" +Hotkeys.NumpadSubtract="Numpad বিয়োগ" +Hotkeys.NumpadDecimal="Numpad দশমিক" +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 কাছে সংরক্ষিত ফাইল (যেমন: BSODs ফলে, বিদ্যুৎ লোকসান, ইত্যাদি।) চূড়ান্ত করা না হলে নির্বাহ হওয়ার সময়ের অপুনরুদ্ধারযোগ্য করা হবে। আপনি যদি রেকর্ড করতে চান একাধিক অডিও ট্র্যাক MKV এবং remux রেকর্ড করা mp4 ব্যবহার করে এটি সম্পন্ন করার পর বিবেচনা (Remux রেকর্ডিং-> ফাইল)" + + diff --git a/UI/data/locale/ca-ES.ini b/UI/data/locale/ca-ES.ini index 8242139..b203ba4 100644 --- a/UI/data/locale/ca-ES.ini +++ b/UI/data/locale/ca-ES.ini @@ -56,6 +56,19 @@ Deprecated="Obsolet" ReplayBuffer="Memòria intermèdia de reproducció" Import="Importa" Export="Exporta" +Copy="Copia" +Paste="Enganxa" +PasteReference="Enganxa (referència)" +PasteDuplicate="Enganxa (duplicat)" +RemuxRecordings="Conversions enregistrades" + + +Copy.Filters="Copia els filtres" +Paste.Filters="Enganxa els filtres" + + + + Updater.Title="Nova actualització disponible" Updater.Text="Hi ha una nova actualització disponible:" @@ -617,3 +630,6 @@ OutputWarnings.NoTracksSelected="Heu de seleccionar almenys una cançó" OutputWarnings.MultiTrackRecording="Advertiment: Alguns formats (com FLV) no suporten múltiples cançons per gravació" OutputWarnings.MP4Recording="Advertència: Els enregistraments desats en MP4 seran irrecuperables si l'arxiu no va poder finalitzar (p.ex. com a resultat de BSODs, pèrdues de potència, etc.). Si voleu enregistrar diverses pistes d'àudio utilitzi MKV i multiplexeu l'enregistrament a MP4 després que acabi (Fitxer-> Multiplexació d'enregistraments)" +FinalScene.Title="Supressió de l'escena" +FinalScene.Text="Cal que hi hagi almenys una escena." + diff --git a/UI/data/locale/cs-CZ.ini b/UI/data/locale/cs-CZ.ini index 838de27..cd3f3b8 100644 --- a/UI/data/locale/cs-CZ.ini +++ b/UI/data/locale/cs-CZ.ini @@ -28,9 +28,12 @@ Browse="Procházet" Mono="Mono" Stereo="Stereo" DroppedFrames="Ztracené snímky %1 (%2%)" -PreviewProjector="Náhled přes celou obrazovku" -SceneProjector="Projektor (Scéna)" -SourceProjector="Projektor (Zdroj)" +PreviewProjector="Celoobrazovkový projektor (náhled)" +SceneProjector="Celoobrazovkový projektor (scéna)" +SourceProjector="Celoobrazovkový projektor (zdroj)" +PreviewWindow="Projektor v okně (náhled)" +SceneWindow="Projektor v okně (scéna)" +SourceWindow="Projektor v okně (zdroj)" Clear="Vyčistit" Revert="Zvrátit" Show="Zobrazit" @@ -56,6 +59,92 @@ Deprecated="Zastaralé" ReplayBuffer="Záznam do paměti" Import="Importovat" Export="Exportovat" +Copy="Kopírovat" +Paste="Vložit" +PasteReference="Vložit (jako odkaz)" +PasteDuplicate="Vložit (jako kopii)" +RemuxRecordings="Převést nahrávky" +Next="Další" +Back="Zpět" + +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." +AlreadyRunning.LaunchAnyway="Spustit tak či tak" + +Copy.Filters="Kopírovat filtry" +Paste.Filters="Vložit filtry" + +BandwidthTest.Region="Oblast" +BandwidthTest.Region.US="Spojené státy" +BandwidthTest.Region.EU="Evropa" +BandwidthTest.Region.Asia="Asie" +BandwidthTest.Region.Other="Jiná" + +Basic.FirstStartup.RunWizard="Chtěli byste použít průvodce nastavením? Pokud ne, nastavení můžete provést i manuálně a to po kliknutí na tlačítko Nastavení v hlavním okně." +Basic.FirstStartup.RunWizard.BetaWarning="(Poznámka: Průvodce nastavením je aktuálně v betě)" +Basic.FirstStartup.RunWizard.NoClicked="Pokud změníte názor, tak můžete kdykoliv spustit průvodce z nabídky Nástrojů." + +Basic.AutoConfig="Průvodce nastavením" +Basic.AutoConfig.Beta="Průvodce nastavením (beta)" +Basic.AutoConfig.ApplySettings="Použít" +Basic.AutoConfig.StartPage="Informace o použití" +Basic.AutoConfig.StartPage.SubTitle="Řekněte nám, k čemu chcete tento program používat" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimalizovat pro vysílání, nahrávání je druhořadé" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimalizovat pouze pro nahrávání, nebudu vysílat" +Basic.AutoConfig.VideoPage="Nastavení obrazu" +Basic.AutoConfig.VideoPage.SubTitle="Specifikujte nastavení obrazu, které budete chtít použít" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Použít aktuální (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Obrazovka %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Použít aktuální (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 nebo 30, pokud to jde, použij 60" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 nebo 30, ale preferuji vyšší rozlišení" +Basic.AutoConfig.VideoPage.CanvasExplanation="Poznámka: Základní rozlišení není nutně stejné jako rozlišení, ve kterém budete vysílat či nahrávat. Vaše opravdové rozlišení může být několikrát zmenšeno oproti základnímu rozlišení, aby bylo sníženo využití prostředků nebo požadavků na bitrate." +Basic.AutoConfig.StreamPage="Informace o vysílání" +Basic.AutoConfig.StreamPage.SubTitle="Prosím zadejte své informace o vysílání" +Basic.AutoConfig.StreamPage.Service="Služba" +Basic.AutoConfig.StreamPage.Service.ShowAll="Zobrazit vše..." +Basic.AutoConfig.StreamPage.Server="Server" +Basic.AutoConfig.StreamPage.StreamKey="Vysílací klíč" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Odkaz)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Odhadnout bitrate za pomocí testu rychlosti spojení (může to zabrat pár minut)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferovat hardwarové kódování" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardwarové kódování rapidně snižuje využití CPU, ale může vyžadovat použití vyššího bitratu k dosažení stejné kvality." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Varování o vysílání" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Test rychlosti spojení bude vysílat náhodná data obrazu bez zvuku na váš kanál. Pokud jste schopni, doporučujeme abyste dočasně vypnuli ukládání nahrávek vysílání a nastavili kanál na soukromý, dokud nebude tento test dokončen. Přejete si pokračovat?" +Basic.AutoConfig.TestPage="Konečné výsledky" +Basic.AutoConfig.TestPage.SubTitle.Testing="Program právě provádí soubor testů pro výpočet ideálního nastavení" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testování dokončeno" +Basic.AutoConfig.TestPage.TestingBandwidth="Probíhá testování rychlosti spojení, zabere to pár minut..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Připojuji k: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Nepodařilo se připojit k žádnému serveru, zkontrolujte své připojení a zkuste to znovu." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Testování rychlosti spojení pro: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Testování kodéru pro vysílání, bude to minutka..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testování kodéru pro nahrávání, bude to minutka..." +Basic.AutoConfig.TestPage.TestingRes="Testování rozlišení, zabere to pár minut..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Nezdařilo se spuštění kodéru" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Testování %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Kodér pro vysílání" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Kodér pro nahrávání" +Basic.AutoConfig.TestPage.Result.Header="Tento program určil, že tato přibližná nastavení jsou pro vás jako dělaná:" +Basic.AutoConfig.TestPage.Result.Footer="Pro použití těchto nastavení klikněte na Použít. Pro změnu v průvodci klikněte na Zpět. Pokud si přejete změnit nastavení manuálně, klikněte na Zrušit a poté otevřete Nastavení." + +Basic.Stats="Statistika" +Basic.Stats.CPUUsage="Využití CPU" +Basic.Stats.HDDSpaceAvailable="Dostupné místo na HDD" +Basic.Stats.MemoryUsage="Využití paměti" +Basic.Stats.AverageTimeToRender="Průměrný čas vykreslování snímku" +Basic.Stats.SkippedFrames="Přeskočené snímky kvůli chybě v kódování" +Basic.Stats.MissedFrames="Nevyužité snímky z důvodu chyby ve vykreslování" +Basic.Stats.Output.Stream="Vysílání" +Basic.Stats.Output.Recording="Nahrávání" +Basic.Stats.Status="Stav" +Basic.Stats.Status.Recording="Nahrává se" +Basic.Stats.Status.Live="ŽIVĚ" +Basic.Stats.Status.Reconnecting="Obnovování připojení" +Basic.Stats.Status.Inactive="Neaktivní" +Basic.Stats.DroppedFrames="Ztracené snímky (síť)" +Basic.Stats.MegabytesSent="Celkový datový výstup" +Basic.Stats.Bitrate="Bitrate" Updater.Title="Aktualizace" Updater.Text="K dispozici je nová verze:" @@ -375,11 +464,12 @@ Basic.Settings.General="Hlavní" Basic.Settings.General.Theme="Vzhled" Basic.Settings.General.Language="Jazyk" Basic.Settings.General.EnableAutoUpdates="Automaticky kontrolovat aktualizace při spuštění" +Basic.Settings.General.OpenStatsOnStartup="Otevřít okno statistik při spuštění" Basic.Settings.General.WarnBeforeStartingStream="Vyžadovat potvrzení pro spuštění vysílání" Basic.Settings.General.WarnBeforeStoppingStream="Vyžadovat potvrzení pro ukončení vysílání" Basic.Settings.General.Projectors="Projektory" Basic.Settings.General.HideProjectorCursor="Skrýt kurzor přes projektor" -Basic.Settings.General.ProjectorAlwaysOnTop="Zobrazovat projektor vždy navrchu" +Basic.Settings.General.ProjectorAlwaysOnTop="Zobrazovat projektory tak, aby vždy byly navrchu" Basic.Settings.General.Snapping="Přichycování zdrojů" Basic.Settings.General.ScreenSnapping="Přichytávat zdroje k okraji obrazovky" Basic.Settings.General.CenterSnapping="Přichytávat zdroje k vertikálnímu a horizontálnímu středu" @@ -392,7 +482,7 @@ Basic.Settings.General.KeepReplayBufferStreamStops="Ponechat záznam do paměti Basic.Settings.General.SysTray="Systémová lišta" Basic.Settings.General.SysTrayWhenStarted="Minimalizovat do systémové lišty při spuštění" Basic.Settings.General.SystemTrayHideMinimize="Vždy minimalizovat do systémové lišty místo hlavního panelu" -Basic.Settings.General.SaveProjectors="Při ukončení uložit projektory" +Basic.Settings.General.SaveProjectors="Ukládat projektory při ukončení" Basic.Settings.Stream="Vysílání" Basic.Settings.Stream.StreamType="Typ vysílání" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Částečné" Basic.Settings.Advanced.Video.ColorRange.Full="Plné" Basic.Settings.Advanced.Audio.MonitoringDevice="Zařízení pro monitorování zvuku" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Výchozí" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Zakázat Windows výchozí utlumování zvuku" Basic.Settings.Advanced.StreamDelay="Zpoždění vysílání" Basic.Settings.Advanced.StreamDelay.Duration="Délka (vteřiny)" Basic.Settings.Advanced.StreamDelay.Preserve="Zachovat zpoždění při obnovení spojení (zvýšení zpoždění)" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Musíte vybrat alespoň jednu stopu" OutputWarnings.MultiTrackRecording="Varování: Některé formáty (např. FLV) nepodporují více zvukových stop na nahrávku" OutputWarnings.MP4Recording="Varování: Nahrávky uložené v MP4 nebude možné obnovit, pokud soubor nemohl být dokončen (např. po BSOD, výpadku napájení atp.). Pokud chcete nahrávat více zvukových stop, promyslete použití MKV a poté převodení do MP4 (Soubor -> Převést nahrávky)" +FinalScene.Title="Odstranění scény" +FinalScene.Text="Musí existovat alespoň jedna scéna, proto tuto není možno odstranit." + diff --git a/UI/data/locale/da-DK.ini b/UI/data/locale/da-DK.ini index 097e923..57e7ed6 100644 --- a/UI/data/locale/da-DK.ini +++ b/UI/data/locale/da-DK.ini @@ -31,6 +31,9 @@ DroppedFrames="Tabte frames %1 (%2%)" PreviewProjector="Fuldskærmsprojektering (forhåndsvisning)" SceneProjector="Fuldskærmsprojektering (scene)" SourceProjector="Fuldskærmsprojektering (kilde)" +PreviewWindow="Vinduesprojektering (forhåndsvisning)" +SceneWindow="Vinduesprojektering (Scene)" +SourceWindow="Vinduesprojektering (kilde)" Clear="Ryd" Revert="Gendan" Show="Vis" @@ -56,6 +59,92 @@ Deprecated="Ophørt" ReplayBuffer="Genafspilningsbuffer" Import="Importér" Export="Eksportér" +Copy="Kopiér" +Paste="Indsæt" +PasteReference="Indsæt (reference)" +PasteDuplicate="Indsæt (dublet)" +RemuxRecordings="Remux-optagelser" +Next="Næste" +Back="Tilbage" + +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." +AlreadyRunning.LaunchAnyway="Start alligevel" + +Copy.Filters="Kopieringsfiltre" +Paste.Filters="Indsætningsfiltre" + +BandwidthTest.Region="Region" +BandwidthTest.Region.US="USA" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Asien" +BandwidthTest.Region.Other="Andet" + +Basic.FirstStartup.RunWizard="Ønsker du at køre autokonfigurationsguiden? Du kan også konfigurere dine indstillinger manuelt, ved at klikke på knappen Indstillinger i hovedvinduet." +Basic.FirstStartup.RunWizard.BetaWarning="(Bemærk: Autokonfigurationsguiden er i øjeblikket i beta)" +Basic.FirstStartup.RunWizard.NoClicked="Skifter du mening, så kan du altid køre autokonfigurationsguiden igen fra menuen Værktøjer." + +Basic.AutoConfig="Autokonfigurationsguide" +Basic.AutoConfig.Beta="Autokonfigurationsguide (beta)" +Basic.AutoConfig.ApplySettings="Anvend indstillinger" +Basic.AutoConfig.StartPage="Brugsoplysninger" +Basic.AutoConfig.StartPage.SubTitle="Vælg hvad du ønsker at bruge programmet til" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimér til streaming, sekundært til optagelse" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimér kun til optagelse, jeg vil ikke foretage streaming" +Basic.AutoConfig.VideoPage="Videoindstillinger" +Basic.AutoConfig.VideoPage.SubTitle="Angiv de videoindstillinger du gerne vil benytte" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Brug aktuel (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Skærm %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Brug aktuel (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Enten 60 eller 30, men foretræk 60 når muligt" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Enten 60 eller 30, men foretræk høj opløsning" +Basic.AutoConfig.VideoPage.CanvasExplanation="Bemærk: Lærredets (basis) opløsning er ikke nødvendigvis identisk med den opløsning, du vil streame med eller optage i. Din aktuelle stream-/optagelsesopløsning kan blive nedskaleret fra lærredsopløsningen for at reducere ressourceforbrug eller krav til bithastigheden." +Basic.AutoConfig.StreamPage="Streamoplysninger" +Basic.AutoConfig.StreamPage.SubTitle="Indtast venligst dine stream oplysninger" +Basic.AutoConfig.StreamPage.Service="Service" +Basic.AutoConfig.StreamPage.Service.ShowAll="Vis alle..." +Basic.AutoConfig.StreamPage.Server="Server" +Basic.AutoConfig.StreamPage.StreamKey="Streamnøgle" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Anslå bitrate med en båndbreddetest (kan tage et par minutter)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Foretræk hardwarekodning" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardware kodning fjerne det meste CPU brug, men kan kræve højere bitrate for at opnå samme kvalitetsniveau." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Streamadvarsel" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Båndbreddetesten er ved at streame tilfældige videodata uden lyd til din kanal. Hvis du har mulighed for det, anbefales det at du midlertidigt fravælger at gemme streams og marker streamen som privat til testen er færdig. Fortsæt?" +Basic.AutoConfig.TestPage="Endelige resultater" +Basic.AutoConfig.TestPage.SubTitle.Testing="Programmet udfører nu en række tests for at estimere de bedste indstillinger" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testen er færdig" +Basic.AutoConfig.TestPage.TestingBandwidth="Foretager båndbreddetest, dette kan tage et par minutter..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Forbinder til: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Kunne ikke forbinde til nogen servere, kontroller venligst din internetforbindelse og prøv igen." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Tester båndbredde for: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Tester streamkodning, dette kan tage et øjeblik..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Tester optagelseskodning, dette kan tage et øjeblik..." +Basic.AutoConfig.TestPage.TestingRes="Tester opløsninger, dette kan tage et par minutter..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Kunne ikke starte kodning" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Tester %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streamkodning" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Optagelseskodning" +Basic.AutoConfig.TestPage.Result.Header="Programmet har bestemt, at disse anslåede indstillinger er de mest ideelle for dig:" +Basic.AutoConfig.TestPage.Result.Footer="Klik på Anvend indstillinger for at benytte denne opsætning. Klik på Tilbage for at genopsætte guiden og forsøge igen. For at opsætte manuelt skal du klikke på Annullér og åbne Indstillinger." + +Basic.Stats="Statistikker" +Basic.Stats.CPUUsage="CPU-forbrug" +Basic.Stats.HDDSpaceAvailable="Harddiskplads tilgængelig" +Basic.Stats.MemoryUsage="Hukommelsesforbrug" +Basic.Stats.AverageTimeToRender="Gennemsnitstid for frame-rendering" +Basic.Stats.SkippedFrames="Oversprang frames grundet kodningsforsinkelse" +Basic.Stats.MissedFrames="Gået glip af frames grundet renderingsforsinkelse" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Optagelse" +Basic.Stats.Status="Status" +Basic.Stats.Status.Recording="Optagelse" +Basic.Stats.Status.Live="LIVE" +Basic.Stats.Status.Reconnecting="Forsøger at oprette forbindelse igen" +Basic.Stats.Status.Inactive="Inaktiv" +Basic.Stats.DroppedFrames="Tabte frames (netværk)" +Basic.Stats.MegabytesSent="Samlede Data Output" +Basic.Stats.Bitrate="Bithastighed" Updater.Title="Ny opdatering tilgængelig" Updater.Text="Der er en ny opdatering tilgængelig:" @@ -375,6 +464,7 @@ Basic.Settings.General="Generelt" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Sprog" Basic.Settings.General.EnableAutoUpdates="Automatisk søgning efter opdateringer ved opstart" +Basic.Settings.General.OpenStatsOnStartup="Åbn statistik dialogboksen ved opstart" Basic.Settings.General.WarnBeforeStartingStream="Vis bekræftelses-dialog ved opstart af stream" Basic.Settings.General.WarnBeforeStoppingStream="Vis bekræftelses-dialog ved afslutning af stream" Basic.Settings.General.Projectors="Projektorer" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Delvis" Basic.Settings.Advanced.Video.ColorRange.Full="Fuld" Basic.Settings.Advanced.Audio.MonitoringDevice="Lyd overvågningsenhed" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Standard" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Deaktiver Windows lyddæmpning" Basic.Settings.Advanced.StreamDelay="Stream forsinkelse" Basic.Settings.Advanced.StreamDelay.Duration="Varighed (sekunder)" Basic.Settings.Advanced.StreamDelay.Preserve="Bevar afskæringspunkt (forøg forsinkelse) ved forbindelsesgendannelse" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Du skal vælge minimum ét spor" OutputWarnings.MultiTrackRecording="Advarsel: Visse formater (såsom FLV) understøtter ikke flere spor pr. optagelse" OutputWarnings.MP4Recording="Advarsel: MP4-optagelser vil ikke kunne genoprettes, hvis filen ikke kan færdiggøres (f.eks. som følge af BSODs, strømafbrydelse m.v.). Ønsker du at optage flere lydspor, overvej da at benytte MKV, og remuxe optagelsen til MP4, efter at den er færdiggjort (fil-> Remux optagelser)" +FinalScene.Title="Slet scene" +FinalScene.Text="Der kræves mindst én scene." + diff --git a/UI/data/locale/de-DE.ini b/UI/data/locale/de-DE.ini index 1c43078..59a75a0 100644 --- a/UI/data/locale/de-DE.ini +++ b/UI/data/locale/de-DE.ini @@ -31,6 +31,9 @@ DroppedFrames="Verworfene Frames %1 (%2%)" PreviewProjector="Vollbild-Projektor (Vorschau)" SceneProjector="Vollbild-Projektor (Szene)" SourceProjector="Vollbild-Projektor (Quelle)" +PreviewWindow="Fenstermodus-Projektor (Vorschau)" +SceneWindow="Fenstermodus-Projektor (Szene)" +SourceWindow="Fenstermodus-Projektor (Quelle)" Clear="Entfernen" Revert="Wiederherstellen" Show="Anzeigen" @@ -56,6 +59,92 @@ Deprecated="Veraltet" ReplayBuffer="Replaypuffer" Import="Importieren" Export="Exportieren" +Copy="Kopieren" +Paste="Einfügen" +PasteReference="Einfügen (Referenz)" +PasteDuplicate="Einfügen (Duplikat)" +RemuxRecordings="Remuxe Aufnahmen" +Next="Weiter" +Back="Zurück" + +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." +AlreadyRunning.LaunchAnyway="Trotzdem starten" + +Copy.Filters="Filter kopieren" +Paste.Filters="Filter einfügen" + +BandwidthTest.Region="Region" +BandwidthTest.Region.US="USA" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Asien" +BandwidthTest.Region.Other="Andere" + +Basic.FirstStartup.RunWizard="Möchten Sie den Autokonfigurationsassistent ausführen? Sie können Ihre Einstellungen auch manuell konfigurieren, indem Sie den Einstellungen-Knopf im Hauptfenster anklicken." +Basic.FirstStartup.RunWizard.BetaWarning="(Hinweis: Der Autokonfigurationsassistent ist zurzeit im Betastatus)" +Basic.FirstStartup.RunWizard.NoClicked="Wenn Sie Ihre Meinung ändern, können Sie den Autokonfigurationsassistent jederzeit erneut aus dem Menü \"Werkzeuge\" ausführen." + +Basic.AutoConfig="Autokonfigurationsassistent" +Basic.AutoConfig.Beta="Autokonfigurationsassistent (Beta)" +Basic.AutoConfig.ApplySettings="Einstellungen übernehmen" +Basic.AutoConfig.StartPage="Informationen zur Verwendung" +Basic.AutoConfig.StartPage.SubTitle="Geben Sie an, wofür Sie das Programm verwenden möchten" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimieren für Streaming, Aufnehmen ist zweitrangig" +Basic.AutoConfig.StartPage.PrioritizeRecording="Nur für die Aufnahme optimieren, ich werde nicht streamen" +Basic.AutoConfig.VideoPage="Videoeinstellungen" +Basic.AutoConfig.VideoPage.SubTitle="Geben Sie die gewünschten Videoeinstellungen an, die Sie verwenden möchten" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Aktuelle verwenden (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Anzeige %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Aktuelle verwenden (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Entweder 60 oder 30, aber bevorzugt 60, wenn möglich" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Entweder 60 oder 30, aber hohe Auflösung bevorzugen" +Basic.AutoConfig.VideoPage.CanvasExplanation="Hinweis: Die Basis (Leinwand) Auflösung ist nicht unbedingt die Gleiche Auflösung mit der Sie Streamen oder Aufnehmen. Ihre tatsächliche Stream- / Aufzeichnungsauflösung kann ausgehend von der Leinwandauflösung herunterskaliert werden, um die Ressourcennutzung oder die Bitratenanforderungen zu reduzieren." +Basic.AutoConfig.StreamPage="Streaminformationen" +Basic.AutoConfig.StreamPage.SubTitle="Bitte geben Sie Ihre Streaminformationen ein" +Basic.AutoConfig.StreamPage.Service="Plattform" +Basic.AutoConfig.StreamPage.Service.ShowAll="Alle anzeigen..." +Basic.AutoConfig.StreamPage.Server="Server" +Basic.AutoConfig.StreamPage.StreamKey="Streamschlüssel" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Schätzung der Bitrate mit Bandbreitentest (kann einige Minuten dauern)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Hardware-Codierung bevorzugen" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardware-Codierung beseitigt die meiste CPU-Auslastung, kann aber mehr Bitrate erfordern, um das gleiche Maß an Qualität zu erhalten." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Streamwarnung" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Der Bandbreitentest ist im Begriff, randomisierte Videodaten ohne Ton zu Ihrem Kanal zu streamen. Wenn Sie in der Lage sind, empfiehlt es sich, vorübergehend das Speichern von Videos abzuschalten und den Stream privat zu schalten, bis der Test abgeschlossen ist. Fortfahren?" +Basic.AutoConfig.TestPage="Endergebnisse" +Basic.AutoConfig.TestPage.SubTitle.Testing="Das Programm führt nun eine Reihe von Tests durch, um die idealen Einstellungen abzuschätzen" +Basic.AutoConfig.TestPage.SubTitle.Complete="Tests abgeschlossen" +Basic.AutoConfig.TestPage.TestingBandwidth="Führe Bandbreitentests durch, dies kann einige Minuten dauern..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Verbinde zu: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Keine Verbindung zu den Servern möglich, bitte überprüfen Sie ihre Internetverbindung und versuchen Sie es erneut." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Teste Bandbreite für: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Teste Streamcodierer, dies kann einige Minuten dauern..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Teste Aufnahmecodierer, dies kann einige Minuten dauern..." +Basic.AutoConfig.TestPage.TestingRes="Teste Auflösungen, dies kann einige Minuten dauern..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Fehler beim Starten des Codierers" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Teste %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streamcodierer" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Aufnahmecodierer" +Basic.AutoConfig.TestPage.Result.Header="Das Programm hat festgestellt, dass diese geschätzten Einstellungen für Sie am idealsten sind:" +Basic.AutoConfig.TestPage.Result.Footer="Um den Assistenten neu zu konfigurieren und es erneut zu versuchen, klicken Sie auf Zurück." + +Basic.Stats="Statistiken" +Basic.Stats.CPUUsage="CPU-Auslastung" +Basic.Stats.HDDSpaceAvailable="Festplattenspeicher verfügbar" +Basic.Stats.MemoryUsage="Speicherauslastung" +Basic.Stats.AverageTimeToRender="Durchschnittliche Zeit, um Frame zu rendern" +Basic.Stats.SkippedFrames="Übersprungene Frames, wegen Codierungsverzögerung" +Basic.Stats.MissedFrames="Ausgelassene Frames. wegen Renderingverzögerung" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Aufnahme" +Basic.Stats.Status="Status" +Basic.Stats.Status.Recording="Aufnahme" +Basic.Stats.Status.Live="LIVE" +Basic.Stats.Status.Reconnecting="Erneut verbinden" +Basic.Stats.Status.Inactive="Inaktiv" +Basic.Stats.DroppedFrames="Verworfene Frames (Netzwerk)" +Basic.Stats.MegabytesSent="Insgesamte Datenausgabe" +Basic.Stats.Bitrate="Bitrate" Updater.Title="Neues Update verfügbar" Updater.Text="Es ist ein neues Update verfügbar:" @@ -113,7 +202,7 @@ ConfirmRemove.TextMultiple="Sind Sie sicher, dass Sie %1 Elemente löschen möch Output.StartStreamFailed="Fehler beim Start des Streams" Output.StartRecordingFailed="Fehler beim Starten der Aufnahme" Output.StartReplayFailed="Fehler beim Starten des Replaypuffers" -Output.StartFailedGeneric="Start der Ausgabe fehlgeschlagen. Bitte überprüfen Sie die Logdatei für Details.\n\nHinweis: Wenn Sie die NVENC- oder AMD-Encoder verwenden, stellen Sie sicher, dass Ihre Videotreiber aktuell sind." +Output.StartFailedGeneric="Start der Ausgabe fehlgeschlagen. Bitte überprüfen Sie die Logdatei für Details.\n\nHinweis: Wenn Sie die NVENC- oder AMD-Codierer verwenden, stellen Sie sicher, dass Ihre Videotreiber aktuell sind." Output.ConnectFail.Title="Verbindung fehlgeschlagen" Output.ConnectFail.BadPath="Ungültiger Pfad oder Verbindungs-URL. Bitte überprüfen Sie Ihre Einstellungen und stellen Sie sicher, dass diese korrekt sind." @@ -375,6 +464,7 @@ Basic.Settings.General="Allgemein" Basic.Settings.General.Theme="Motiv" Basic.Settings.General.Language="Sprache" Basic.Settings.General.EnableAutoUpdates="Beim Start nach Updates suchen" +Basic.Settings.General.OpenStatsOnStartup="Statistikenfenster beim Start öffnen" Basic.Settings.General.WarnBeforeStartingStream="Bestätigungsdialog beim Streamstart anzeigen" Basic.Settings.General.WarnBeforeStoppingStream="Bestätigungsdialog beim Streamstop anzeigen" Basic.Settings.General.Projectors="Projektoren" @@ -489,18 +579,18 @@ FilenameFormatting.completer="%DD-%MM-%CCYY %hh-%mm-%ss\n%DD-%MM-%YY %hh-%mm-%ss FilenameFormatting.TT="%CCYY Jahr, vier Ziffern\n%YY Jahr, letzte zwei Ziffern (00-99)\n%MM Monat als Dezimalzahl (01-12)\n%DD Tag des Monats, mit Nullen aufgefüllt (01-31)\n%hh Stunden im 24 Stunden Format (00-23)\n%mm Minute (00-59)\n%ss Sekunde (00-61)\n%% Ein % Zeichen\n%a Abgekürzter Wochentagsname\n%A Voller Wochentagsname\n%b Abgekürzer Monatsname\n%B Voller Monatsname\n%d Tag des Monats, mit Nullen aufgefüllt (01-31)\n%H Stunden im 24 Stunden Format (00-23)\n%I Stunden im 12 Stunden Format (01-12)\n%m Monat als Dezimalzahl (01-12)\n%M Minute (00-59)\n%p AM oder PM Angabe\n%S Sekunde (00-61)\n%y Jahr, letzte zwei Ziffern (00-99)\n%Y Jahr\n%z ISO 8601 Verschiebung von UTC oder Zeitzone\n Name oder Abkürzung\n%Z Zeitzonenname oder Abkürzung\n" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Grafikkarte:" -Basic.Settings.Video.BaseResolution="Basis (Leinwand) Auflösung:" -Basic.Settings.Video.ScaledResolution="Ausgabe (skaliert) Auflösung:" -Basic.Settings.Video.DownscaleFilter="Skalierungs-Filter:" +Basic.Settings.Video.Adapter="Grafikkarte" +Basic.Settings.Video.BaseResolution="Basis (Leinwand) Auflösung" +Basic.Settings.Video.ScaledResolution="Ausgabe (skaliert) Auflösung" +Basic.Settings.Video.DownscaleFilter="Skalierungs-Filter" Basic.Settings.Video.DisableAeroWindows="Aero deaktivieren (nur Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Übliche FPS Werte" Basic.Settings.Video.FPSInteger="Ganzzahl FPS Wert" Basic.Settings.Video.FPSFraction="Bruchteil FPS Wert" -Basic.Settings.Video.Numerator="Zähler:" -Basic.Settings.Video.Denominator="Nenner:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Zähler" +Basic.Settings.Video.Denominator="Nenner" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Ungültige Auflösung. Muss sein [width]x[height] (z.B. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Videoausgabe ist derzeit aktiv. Bitte schalten Sie alle Ausgaben ab, um die Videoeinstellungen zu ändern." Basic.Settings.Video.DisableAero="Aero deaktivieren" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Begrenzt" Basic.Settings.Advanced.Video.ColorRange.Full="Voll" Basic.Settings.Advanced.Audio.MonitoringDevice="Audiomonitoringgerät" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Standard" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Windows Audioducking deaktivieren" Basic.Settings.Advanced.StreamDelay="Stream-Verzögerung" Basic.Settings.Advanced.StreamDelay.Duration="Dauer (Sekunden)" Basic.Settings.Advanced.StreamDelay.Preserve="Lückenloses Wiederverbinden (erhöht Verzögerung, um Videoverlust zu vermeiden)" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Sie müssen mindestens eine Spur auswählen" OutputWarnings.MultiTrackRecording="Warnung: Bestimmte Formate (z. B. FLV) unterstützen nicht mehrere Spuren pro Aufnahme" OutputWarnings.MP4Recording="Warnung: Aufnahmen, die in MP4 gespeichert werden, sind nicht wiederherstellbar, wenn die Datei nicht abgeschlossen werden kann (zum Beispiel als Folge von BSODs, Stromausfälle, etc). Wenn Sie mehrere Audiospuren aufnehmen möchten, sollten Sie MKV verwenden und die Aufnahme zu MP4 remuxen, nachdem sie fertig ist. (Datei-> Remuxe Aufnahmen)" +FinalScene.Title="Szene löschen" +FinalScene.Text="Es muss mindestens eine Szene vorhanden sein." + diff --git a/UI/data/locale/el-GR.ini b/UI/data/locale/el-GR.ini index 59839a4..f52f0cb 100644 --- a/UI/data/locale/el-GR.ini +++ b/UI/data/locale/el-GR.ini @@ -50,6 +50,12 @@ Seconds="Δευτερόλεπτα" + + + + + + Basic.TransitionDuration="Διάρκεια" Basic.TogglePreviewProgramMode="Λειτουργία στούντιο" @@ -376,3 +382,4 @@ SceneItemShow="Εμφάνιση '%1'" SceneItemHide="Απόκρυψη '%1'" + diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 1d3b3f9..ab35921 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -36,6 +36,9 @@ DroppedFrames="Dropped Frames %1 (%2%)" PreviewProjector="Fullscreen Projector (Preview)" SceneProjector="Fullscreen Projector (Scene)" SourceProjector="Fullscreen Projector (Source)" +PreviewWindow="Windowed Projector (Preview)" +SceneWindow="Windowed Projector (Scene)" +SourceWindow="Windowed Projector (Source)" Clear="Clear" Revert="Revert" Show="Show" @@ -61,6 +64,98 @@ Deprecated="Deprecated" ReplayBuffer="Replay Buffer" Import="Import" Export="Export" +Copy="Copy" +Paste="Paste" +PasteReference="Paste (Reference)" +PasteDuplicate="Paste (Duplicate)" +RemuxRecordings="Remux Recordings" +Next="Next" +Back="Back" + +# 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.LaunchAnyway="Launch Anyway" + +# copy filters +Copy.Filters="Copy Filters" +Paste.Filters="Paste Filters" + +# bandwidth test +BandwidthTest.Region="Region" +BandwidthTest.Region.US="United States" +BandwidthTest.Region.EU="Europe" +BandwidthTest.Region.Asia="Asia" +BandwidthTest.Region.Other="Other" + +# first time startup +Basic.FirstStartup.RunWizard="Would you like to run the auto-configuration wizard? You can also manually configure your settings by clicking the Settings button in the main window." +Basic.FirstStartup.RunWizard.BetaWarning="(Note: The auto-configuration wizard is currently in beta)" +Basic.FirstStartup.RunWizard.NoClicked="If you change your mind, you can run the auto-configuration wizard any time again from the Tools menu." + +# auto config wizard +Basic.AutoConfig="Auto-Configuration Wizard" +Basic.AutoConfig.Beta="Auto-Configuration Wizard (Beta)" +Basic.AutoConfig.ApplySettings="Apply Settings" +Basic.AutoConfig.StartPage="Usage Information" +Basic.AutoConfig.StartPage.SubTitle="Specify what you want to use the program for" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimize for streaming, recording is secondary" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimize just for recording, I will not be streaming" +Basic.AutoConfig.VideoPage="Video Settings" +Basic.AutoConfig.VideoPage.SubTitle="Specify the desired video settings you would like to use" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Use Current (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Display %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Use Current (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Either 60 or 30, but prefer 60 when possible" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Either 60 or 30, but prefer high resolution" +Basic.AutoConfig.VideoPage.CanvasExplanation="Note: The canvas (base) resolution is not necessarily the same as the resolution you will stream or record with. Your actual stream/recording resolution may be scaled down from the canvas resolution to reduce resource usage or bitrate requirements." +Basic.AutoConfig.StreamPage="Stream Information" +Basic.AutoConfig.StreamPage.SubTitle="Please enter your stream information" +Basic.AutoConfig.StreamPage.Service="Service" +Basic.AutoConfig.StreamPage.Service.ShowAll="Show All..." +Basic.AutoConfig.StreamPage.Server="Server" +Basic.AutoConfig.StreamPage.StreamKey="Stream Key" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Estimate bitrate with bandwidth test (may take a few minutes)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Prefer hardware encoding" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardware Encoding eliminates most CPU usage, but may require more bitrate to obtain the same level of quality." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Stream warning" +Basic.AutoConfig.StreamPage.StreamWarning.Text="The bandwidth test is about to stream randomized video data without audio to your channel. If you're able, it's recommended to temporarily turn off saving videos of streams and set the stream to private until after the test has completed. Continue?" +Basic.AutoConfig.TestPage="Final Results" +Basic.AutoConfig.TestPage.SubTitle.Testing="The program is now executing a set of tests to estimate the most ideal settings" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testing complete" +Basic.AutoConfig.TestPage.TestingBandwidth="Performing bandwidth test, this may take a few minutes..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Connecting to: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Failed to connect to any servers, please check your internet connection and try again." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Testing bandwidth for: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Testing stream encoder, this may take a minute..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testing recording encoder, this may take a minute..." +Basic.AutoConfig.TestPage.TestingRes="Testing resolutions, this may take a few minutes..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Failed to start up encoder" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Testing %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streaming Encoder" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Recording Encoder" +Basic.AutoConfig.TestPage.Result.Header="The program has determined that these estimated settings are the most ideal for you:" +Basic.AutoConfig.TestPage.Result.Footer="To use these settings, click Apply Settings. To reconfigure the wizard and try again, click Back. To manually configure settings yourself, click Cancel and open Settings." + +# stats +Basic.Stats="Stats" +Basic.Stats.CPUUsage="CPU Usage" +Basic.Stats.HDDSpaceAvailable="HDD space available" +Basic.Stats.MemoryUsage="Memory Usage" +Basic.Stats.AverageTimeToRender="Average time to render frame" +Basic.Stats.SkippedFrames="Skipped frames due to encoding lag" +Basic.Stats.MissedFrames="Frames missed due to rendering lag" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Recording" +Basic.Stats.Status="Status" +Basic.Stats.Status.Recording="Recording" +Basic.Stats.Status.Live="LIVE" +Basic.Stats.Status.Reconnecting="Reconnecting" +Basic.Stats.Status.Inactive="Inactive" +Basic.Stats.DroppedFrames="Dropped Frames (Network)" +Basic.Stats.MegabytesSent="Total Data Output" +Basic.Stats.Bitrate="Bitrate" # updater Updater.Title="New update available" @@ -428,6 +523,7 @@ Basic.Settings.General="General" Basic.Settings.General.Theme="Theme" Basic.Settings.General.Language="Language" Basic.Settings.General.EnableAutoUpdates="Automatically check for updates on startup" +Basic.Settings.General.OpenStatsOnStartup="Open stats dialog on startup" Basic.Settings.General.WarnBeforeStartingStream="Show confirmation dialog when starting streams" Basic.Settings.General.WarnBeforeStoppingStream="Show confirmation dialog when stopping streams" Basic.Settings.General.Projectors="Projectors" @@ -549,18 +645,18 @@ FilenameFormatting.TT="%CCYY Year, four digits\n%YY Year, last two digits (00-9 # basic mode 'video' settings Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Video Adapter:" -Basic.Settings.Video.BaseResolution="Base (Canvas) Resolution:" -Basic.Settings.Video.ScaledResolution="Output (Scaled) Resolution:" -Basic.Settings.Video.DownscaleFilter="Downscale Filter:" +Basic.Settings.Video.Adapter="Video Adapter" +Basic.Settings.Video.BaseResolution="Base (Canvas) Resolution" +Basic.Settings.Video.ScaledResolution="Output (Scaled) Resolution" +Basic.Settings.Video.DownscaleFilter="Downscale Filter" Basic.Settings.Video.DisableAeroWindows="Disable Aero (Windows only)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Common FPS Values" Basic.Settings.Video.FPSInteger="Integer FPS Value" Basic.Settings.Video.FPSFraction="Fractional FPS Value" -Basic.Settings.Video.Numerator="Numerator:" -Basic.Settings.Video.Denominator="Denominator:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Numerator" +Basic.Settings.Video.Denominator="Denominator" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Invalid resolution value. Must be [width]x[height] (i.e. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Video output is currently active. Please turn off any outputs to change video settings." Basic.Settings.Video.DisableAero="Disable Aero" @@ -601,6 +697,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Partial" Basic.Settings.Advanced.Video.ColorRange.Full="Full" Basic.Settings.Advanced.Audio.MonitoringDevice="Audio Monitoring Device" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Default" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Disable Windows audio ducking" Basic.Settings.Advanced.StreamDelay="Stream Delay" Basic.Settings.Advanced.StreamDelay.Duration="Duration (seconds)" Basic.Settings.Advanced.StreamDelay.Preserve="Preserve cutoff point (increase delay) when reconnecting" @@ -688,3 +785,7 @@ SceneItemHide="Hide '%1'" OutputWarnings.NoTracksSelected="You must select at least one track" OutputWarnings.MultiTrackRecording="Warning: Certain formats (such as FLV) do not support multiple tracks per recording" OutputWarnings.MP4Recording="Warning: Recordings saved to MP4 will be unrecoverable if the file cannot be finalized (e.g. as a result of BSODs, power losses, etc.). If you want to record multiple audio tracks consider using MKV and remux the recording to mp4 after it is finished (File->Remux Recordings)" + +# deleting final scene +FinalScene.Title="Delete Scene" +FinalScene.Text="There needs to be at least one scene." diff --git a/UI/data/locale/es-ES.ini b/UI/data/locale/es-ES.ini index 76aed31..726c6f0 100644 --- a/UI/data/locale/es-ES.ini +++ b/UI/data/locale/es-ES.ini @@ -31,6 +31,9 @@ DroppedFrames="Fotogramas Perdidos %1 (%2%)" PreviewProjector="Proyector de pantalla completa (Previsualización)" SceneProjector="Proyector de pantalla completa (escena)" SourceProjector="Proyector de pantalla completa (fuente)" +PreviewWindow="Proyector con ventana (Pre-visualización)" +SceneWindow="Proyector con ventana (Escena)" +SourceWindow="Proyector con ventana (Fuente)" Clear="Borrar" Revert="Revertir" Show="Mostrar" @@ -56,6 +59,91 @@ Deprecated="Obsoleto" ReplayBuffer="Búfer de reproducción" Import="Importar" Export="Exportar" +Copy="Copiar" +Paste="Pegar" +PasteReference="Pegar (referencia)" +PasteDuplicate="Pegar (duplicado)" +RemuxRecordings="Grabaciones Convertidas" +Next="Siguiente" +Back="Atrás" + +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í." +AlreadyRunning.LaunchAnyway="Lanzar de todas maneras" + +Copy.Filters="Copiar filtros" +Paste.Filters="Pegar filtros" + +BandwidthTest.Region="Región" +BandwidthTest.Region.US="Estados Unidos" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Asia" +BandwidthTest.Region.Other="Otros" + +Basic.FirstStartup.RunWizard="¿Deseas ejecutar el asistente de configuración automática? También puedes configurar tus ajustes manualmente al hacer clic en el botón \"Configuración\" de la ventana principal." +Basic.FirstStartup.RunWizard.BetaWarning="(Nota: El asistente de configuración automática está en beta)" +Basic.FirstStartup.RunWizard.NoClicked="Si cambias tu mente, puede ejecutar al asistente de configuración automática cualquier momento desde el menú \"Herramientas\"." + +Basic.AutoConfig="Asistente de configuración automática" +Basic.AutoConfig.Beta="Asistente de configuración automática (Beta)" +Basic.AutoConfig.ApplySettings="Aplicar configuración" +Basic.AutoConfig.StartPage="Información sobre el uso" +Basic.AutoConfig.StartPage.SubTitle="Especifique para que quiere usar el programa" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimizar para transmisiones, la grabación es secundaria" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimizar solo para grabación, no voy a hacer una transmisión" +Basic.AutoConfig.VideoPage="Ajustes de vídeo" +Basic.AutoConfig.VideoPage.SubTitle="Especifique los ajustes de vídeo deseados que quiera usar" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Usar Actual (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Pantalla %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Usar actual (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 o 30, pero usar 60 cuando sea posible" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 o 30, pero usar alta resolución" +Basic.AutoConfig.VideoPage.CanvasExplanation="Nota: La resolución del lienzo (base) no es necesariamente la misma que la resolución de la transmisión o grabación. La resolución actual puede ser reducida del lienzo para reducir el uso de los recursos o del bitrate." +Basic.AutoConfig.StreamPage="Información de servicio de Stream" +Basic.AutoConfig.StreamPage.SubTitle="Por favor, introduce información sobre tu servicio de stream" +Basic.AutoConfig.StreamPage.Service="Servicio" +Basic.AutoConfig.StreamPage.Service.ShowAll="Mostrar todos..." +Basic.AutoConfig.StreamPage.Server="Servidor" +Basic.AutoConfig.StreamPage.StreamKey="Clave de retransmisión" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Enlace)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferir codificación por hardware" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Codificación por hardware elimina la mayoría del uso de la CPU, pero puede requerir mas bitrate para obtener el mismo nivel de calidad." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Advertencia de transmisión" +Basic.AutoConfig.StreamPage.StreamWarning.Text="La prueba de ancho de banda esta a punto de transmitir datos de video aleatorios sin audio a tu canal. Si puedes, es recomendable desactivar temporalmente el que se guarden los videos de las transmisiones y hacer la transmisión privada después de que la prueba haya finalizado. ¿Desea continuar?" +Basic.AutoConfig.TestPage="Resultado final" +Basic.AutoConfig.TestPage.SubTitle.Testing="El programa ahora esta ejecutando un conjunto de pruebas para estimar los ajustes óptimos" +Basic.AutoConfig.TestPage.SubTitle.Complete="Prueba completada" +Basic.AutoConfig.TestPage.TestingBandwidth="Ejecutando prueba de ancho de banda, esto puede tardar unos minutos..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Conectando a: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="No se pudo conectar a ningún servidor. Por favor revise su conexión a Internet e inténtelo de nuevo." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Probando ancho de banda para: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Probando codificador de transmisión, esto puede tardar un minuto..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Probando codificador de grabación, esto puede tardar un minuto..." +Basic.AutoConfig.TestPage.TestingRes="Probando resoluciones, esto puede tardar unos minutos..." +Basic.AutoConfig.TestPage.TestingRes.Fail="No se pudo iniciar el codificador" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Probando %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Codificador de transmisión" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Codificador de grabación" +Basic.AutoConfig.TestPage.Result.Header="El programa ha determinado que estos ajustes estimados son los mas óptimos para ti:" +Basic.AutoConfig.TestPage.Result.Footer="Para utilizar estos ajustes, haga clic en \"Aplicar Configuración\". Para volver a configurar el asistente, haga clic en Atrás. Para configurar los ajustes por ti mismo, haga clic en Cancelar y abra los ajustes." + +Basic.Stats="Estadísticas" +Basic.Stats.CPUUsage="Uso de CPU" +Basic.Stats.HDDSpaceAvailable="Espacio disponible en disco" +Basic.Stats.MemoryUsage="Uso de memoria" +Basic.Stats.AverageTimeToRender="Tiempo de media para procesar un fotograma" +Basic.Stats.SkippedFrames="Fotogramas saltados por retraso de procesamiento" +Basic.Stats.MissedFrames="Fotogramas perdidos por retraso de procesamiento" +Basic.Stats.Output.Stream="Transmisión" +Basic.Stats.Output.Recording="Grabación" +Basic.Stats.Status="Estado" +Basic.Stats.Status.Recording="Grabando" +Basic.Stats.Status.Live="EN VIVO" +Basic.Stats.Status.Reconnecting="Volviendo a conectar" +Basic.Stats.Status.Inactive="Inactivo" +Basic.Stats.DroppedFrames="Fotogramas Perdidos (Red)" +Basic.Stats.MegabytesSent="Salida de datos total" +Basic.Stats.Bitrate="Bitrate" Updater.Title="Nueva actualización disponible" Updater.Text="Hay una nueva versión disponible:" @@ -242,10 +330,10 @@ Basic.InteractionWindow="Interactuando con '%1'" Basic.StatusBar.Reconnecting="Desconectado, reconexión en %2 segundo(s) (tentativa%1)" Basic.StatusBar.AttemptingReconnect="Intentando volver a conectar... (intento %1)" Basic.StatusBar.ReconnectSuccessful="Reconexión exitosa" -Basic.StatusBar.Delay="Retraso (%1 sec)" -Basic.StatusBar.DelayStartingIn="Retraso (iniciando en %1 sec)" -Basic.StatusBar.DelayStoppingIn="Retraso (parando en %1 sec)" -Basic.StatusBar.DelayStartingStoppingIn="Retraso (parando en %1 sec, iniciando en %2 sec)" +Basic.StatusBar.Delay="Retardo (%1 sec)" +Basic.StatusBar.DelayStartingIn="Retardo (iniciando en %1 sec)" +Basic.StatusBar.DelayStoppingIn="Retardo (parando en %1 sec)" +Basic.StatusBar.DelayStartingStoppingIn="Retardo (parando en %1 sec, iniciando en %2 sec)" Basic.Filters="Filtros" Basic.Filters.AsyncFilters="Filtros de audio/vídeo" @@ -298,7 +386,7 @@ Basic.Main.StopReplayBuffer="Detener la reproducción del búfer" 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 retraso)" +Basic.Main.ForceStopStreaming="Parar Transmisión (descartar retardo)" Basic.MainMenu.File="&Archivo" Basic.MainMenu.File.Export="&Exportar" @@ -375,6 +463,7 @@ Basic.Settings.General="General" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.EnableAutoUpdates="Comprobar actualizaciones automáticamente al inicio" +Basic.Settings.General.OpenStatsOnStartup="Abrir dialogo de estadísticas al inicio" Basic.Settings.General.WarnBeforeStartingStream="Mostrar diálogo de confirmación cuando se inicia una transmisión" Basic.Settings.General.WarnBeforeStoppingStream="Mostrar diálogo de confirmación cuando se para una transmisión" Basic.Settings.General.Projectors="Proyectores" @@ -436,7 +525,7 @@ Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 bajo uso de Basic.Settings.Output.VideoBitrate="Bitrate de vídeo" Basic.Settings.Output.AudioBitrate="Bitrate de audio" Basic.Settings.Output.Reconnect="Reconectar automáticamente" -Basic.Settings.Output.RetryDelay="Demora al reintentar (segundos)" +Basic.Settings.Output.RetryDelay="Retardo al re-intentar (segundos)" Basic.Settings.Output.MaxRetries="Reintentos máximos" Basic.Settings.Output.Advanced="Habilitar la configuración de codificador avanzada" Basic.Settings.Output.EncoderPreset="Perfil de Codificador (más rapido = menos CPU)" @@ -489,18 +578,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY Año, cuatro dígitos\n%YY Año, ultimo dos dígitos (00-99)\n%MM Mes como numero decimal (01-12)\n%DD Día del mes, cero-añadido (01-31)\n%hh Hora en formato 24h (00-23)\n%mm Minuto (00-59)\n%ss Segundo (00-61)\n%% A % signo\n%a Nombre del día semanal Abreviado\n%A completo nombre dia semanal\n%b Nombre del Mes Abreviado\n%B Nombre completo del nombre del mes\n%d Día del mes, cero-añadido (01-31)\n%H Formato Hora en 24h (00-23)\n%I Formato Hora en 12h (01-12)\n%m Mes como numero decimal (01-12)\n%M Minuto (00-59)\n%p Designación AM o PM\n%S Segundo (00-61)\n%y Año, últimos dos dígitos (00-99)\n%Y Year\n%z ISO 8601 ajuste de UTC o zona horaria\n nombre o abreviación\n%Z Nombre de zona horaria o abreviación\n" Basic.Settings.Video="Vídeo" -Basic.Settings.Video.Adapter="Adaptador de vídeo:" -Basic.Settings.Video.BaseResolution="Resolución de la base (Canvas):" -Basic.Settings.Video.ScaledResolution="Resolución de salida (Escalada):" -Basic.Settings.Video.DownscaleFilter="Filtro de escala:" +Basic.Settings.Video.Adapter="Adaptador de vídeo" +Basic.Settings.Video.BaseResolution="Resolución de la base (Lienzo)" +Basic.Settings.Video.ScaledResolution="Resolución de salida (Escalada)" +Basic.Settings.Video.DownscaleFilter="Filtro de escala" Basic.Settings.Video.DisableAeroWindows="Desactivar Aero (sólo Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Valores comunes de FPS" Basic.Settings.Video.FPSInteger="Valor entero de FPS" Basic.Settings.Video.FPSFraction="Valor fraccional de FPS" -Basic.Settings.Video.Numerator="Numerador:" -Basic.Settings.Video.Denominator="Denominador:" -Basic.Settings.Video.Renderer="Procesador:" +Basic.Settings.Video.Numerator="Numerador" +Basic.Settings.Video.Denominator="Denominador" +Basic.Settings.Video.Renderer="Renderizador" Basic.Settings.Video.InvalidResolution="Valor de la resolución no válido. Debe ser [ancho] x [altura] (por ejemplo, 1920 x 1080)" Basic.Settings.Video.CurrentlyActive="La salida de vídeo está actualmente activa. Por favor apague cualquier salida para cambiar la configuración de vídeo." Basic.Settings.Video.DisableAero="Deshabilitar Aero" @@ -518,9 +607,9 @@ Basic.Settings.Audio.AuxDevice="Dispositivo de audio Mic/auxiliar" Basic.Settings.Audio.AuxDevice2="Dispositivo de audio Mic/auxiliar 2" Basic.Settings.Audio.AuxDevice3="Dispositivo de audio Mic/auxiliar 3" Basic.Settings.Audio.EnablePushToMute="Habilitar Pulsar para Silenciar" -Basic.Settings.Audio.PushToMuteDelay="Retraso de Presionar para Silenciar" +Basic.Settings.Audio.PushToMuteDelay="Retardo de Presionar para Silenciar" Basic.Settings.Audio.EnablePushToTalk="Habilitar Pulsar para Hablar" -Basic.Settings.Audio.PushToTalkDelay="Retraso de Pulsar para Hablar" +Basic.Settings.Audio.PushToTalkDelay="Retardo de Pulsar para Hablar" Basic.Settings.Audio.UnknownAudioDevice="[Dispositivo no conectado o no está disponible]" Basic.Settings.Advanced="Avanzado" @@ -538,9 +627,10 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Parcial" Basic.Settings.Advanced.Video.ColorRange.Full="Completo" Basic.Settings.Advanced.Audio.MonitoringDevice="Dispositivo de monitorización de audio" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Por defecto" -Basic.Settings.Advanced.StreamDelay="Retraso de la transmisión" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Desactivar reducción de audio de Windows" +Basic.Settings.Advanced.StreamDelay="Retardo de la transmisión" Basic.Settings.Advanced.StreamDelay.Duration="Duración (segundos)" -Basic.Settings.Advanced.StreamDelay.Preserve="Preservar el punto de corte (aumento de retraso) al volver a conectar" +Basic.Settings.Advanced.StreamDelay.Preserve="Preservar el punto de corte (aumento de retardo) al volver a conectar" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Uso estimado de memoria: %1 MB" Basic.Settings.Advanced.Network="Red" Basic.Settings.Advanced.Network.BindToIP="Enlazar con IP" @@ -617,3 +707,6 @@ OutputWarnings.NoTracksSelected="Debe seleccionar al menos una pista" OutputWarnings.MultiTrackRecording="ADVERTENCIA: Ciertos formatos (como FLV) no admiten varias pistas por grabación" OutputWarnings.MP4Recording="ADVERTENCIA: Las grabaciones guardadas en MP4 será irrecuperables si el archivo no puede finalizarse (e.g. como resultado de BSODs, pérdidas de potencia, etcetera). Si quieres grabar varias pistas de audio utiliza MKV y reune la grabación a mp4 después de que termine (archivo-> Remux de grabaciones)" +FinalScene.Title="Eliminar escena" +FinalScene.Text="Debe haber al menos una escena." + diff --git a/UI/data/locale/et-EE.ini b/UI/data/locale/et-EE.ini index 8362469..775aaf5 100644 --- a/UI/data/locale/et-EE.ini +++ b/UI/data/locale/et-EE.ini @@ -8,7 +8,7 @@ Cancel="Loobu" Close="Sulge" Save="Salvesta" Discard="Loobu muudatustest" -Disable="Välja lülitatud" +Disable="Lülita välja" Yes="Jah" No="Ei" Add="Lisa" @@ -57,6 +57,12 @@ ReplayBuffer="Taasesituse puhver" Import="Impordi" Export="Ekspordi" + + + + + + Updater.Title="Uus värskendus saadaval" Updater.Text="Uus värskendus on saadaval:" Updater.UpdateNow="Värskenda kohe" @@ -105,6 +111,7 @@ ConfirmExit.Text="OBS on hetkel aktiivne. Kõik voogedastused ja salvestused pea ConfirmRemove.Title="Ümbernimetamise kinnitamine" ConfirmRemove.Text="Kas soovid kindlasti eemaldada '$1'?" +ConfirmRemove.TextMultiple="Kas soovite kindlasti eemaldada %1 üksust?" Output.StartStreamFailed="Voogedastuse alustamine nurjus" Output.StartRecordingFailed="Salvestamise alustamine nurjus" @@ -113,11 +120,24 @@ Output.ConnectFail.Title="Ühendamine ei õnnestunud" Output.ConnectFail.BadPath="Vigane rada või ühenduse URL. Palun veendu, et valitud sätted on õiged." Output.ConnectFail.ConnectFailed="Serveriga ühendamine ebaõnnestus" +Output.RecordFail.Title="Salvestamise alustamine nurjus" +Output.RecordNoSpace.Title="Kettaruumi pole piisavalt" +Output.RecordNoSpace.Msg="Pole piisavalt kettaruumi, et jätkata salvestamist." Output.RecordError.Title="Salvestamise tõrge" +Output.RecordError.Msg="Salvestamise ajal ilmnes tundmatu tõrge." +Output.ReplayBuffer.NoHotkey.Title="Kiirklahv pole seadistatud!" +Output.ReplayBuffer.NoHotkey.Msg="\"Salvesta Taasesitus\" kiirklahv on seadistamata. Palun seadistage kiirklahv, mida soovite kasutada." +Output.BadPath.Title="Halb failitee" +Output.BadPath.Text="Konfigureeritud failiväljundi tee on vale. Palun kontrollige oma sätetes, kas õige failitee on valitud." +LogReturnDialog="Logi üleslaadimine õnnestus" LogReturnDialog.CopyURL="Kopeeri aadress" +LogReturnDialog.ErrorUploadingLog="Esines viga logifaili üleslaadimisel" +LicenseAgreement="Litsentsileping" +LicenseAgreement.PleaseReview="Palun vaadake üle lisentsi tingimused enne OBS-i kasutamist. Selle programmi kasutamisega tunnistate, et olete antud tingimused läbilugenud ja nõustute GNU General Public License v2.0 tingimustega. Palun kerige alla, et näha tervet lepingut." +LicenseAgreement.ClickIAgreeToContinue="Kui nõustute lepingu tingimustega, klõpsake nõustun, et jätkata. Te peate nõustuma, et kasutada OBS-i." LicenseAgreement.IAgree="Nõustun" LicenseAgreement.Exit="Välju" @@ -140,14 +160,22 @@ UpdateAvailable.Text="Saadaval on version %1.%2.%3. Allalaadimisek Basic.DesktopDevice1="Töölaua heli" Basic.DesktopDevice2="Töölaua heli 2" +Basic.AuxDevice1="Mikrofon/Aux" +Basic.AuxDevice2="Mikrofon/Aux 2" +Basic.AuxDevice3="Mikrofon/Aux 3" +Basic.AuxDevice4="Mikrofon/Aux 4" Basic.Scene="Stseen" +Basic.DisplayCapture="Kuvari hõive" Basic.Main.PreviewConextMenu.Enable="Lülita eelvaade sisse" ScaleFiltering.Point="Punkt" +Deinterlacing.Retro="Retro" +Deinterlacing.Blend="Sulanda" +VolControl.Mute="Vaigista '%1'" VolControl.Properties="'%1' atribuudid" Basic.Main.AddSceneDlg.Title="Stseeni lisamine" @@ -173,16 +201,27 @@ Basic.SourceSelect.AddExisting="Lisa olemasolev" Basic.SourceSelect.AddVisible="Tee allikas nähtavaks" Basic.PropertiesWindow="'%1' omadused" +Basic.PropertiesWindow.SelectColor="Vali värv" +Basic.PropertiesWindow.SelectFont="Vali font" +Basic.PropertiesWindow.Confirm="On salvestamata muutusi. Kas soovite neid säilitada?" Basic.PropertiesWindow.AddFiles="Lisa failid" Basic.PropertiesWindow.AddDir="Lisa kataloog" Basic.PropertiesWindow.AddURL="Lisa tee/URL" +Basic.PropertiesView.FPS.Simple="Lihtsad FPS väärtused" +Basic.StatusBar.ReconnectSuccessful="Taasühendumine edukas" +Basic.StatusBar.Delay="Viivitus (%1 s)" +Basic.StatusBar.DelayStartingIn="Viivitus (algab %1 s pärast)" +Basic.StatusBar.DelayStoppingIn="Viivitus (lõppeb %1 s pärast)" +Basic.StatusBar.DelayStartingStoppingIn="Viivitus (lõppeb %1 s pärast, algab %2 s pärast)" Basic.Filters="Filtrid" +Basic.Filters.AsyncFilters="Audio/Video filtrid" Basic.Filters.AudioFilters="Helifiltrid" Basic.Filters.AddFilter.Title="Filtri nimi" +Basic.Filters.AddFilter.Text="Palun määrake filtri nimi" Basic.TransformWindow.Position="Asukoht" Basic.TransformWindow.Rotation="Pööramine" @@ -200,6 +239,8 @@ Basic.TransformWindow.Alignment.BottomLeft="Alla vasakule" Basic.TransformWindow.Alignment.BottomCenter="Alla keskele" Basic.TransformWindow.Alignment.BottomRight="Alla paremale" +Basic.TransformWindow.BoundsType.None="Piirid määramata" +Basic.TransformWindow.BoundsType.MaxOnly="Maksimaalne suurus ainult" Basic.Main.AddSourceHelp.Title="Allikat ei saa lisada" Basic.Main.AddSourceHelp.Text="Allika lisamiseks peab olema vähemalt üks stseen." @@ -210,7 +251,12 @@ Basic.Main.Connecting="Ühendamine..." Basic.Main.StartRecording="Alusta salvestamist" Basic.Main.StartStreaming="Alusta voogedastust" Basic.Main.StopRecording="Lõpeta salvestamine" +Basic.Main.StoppingRecording="Salvestamise peatamine..." +Basic.Main.StopReplayBuffer="Peata taasesituse puhver" +Basic.Main.StoppingReplayBuffer="Taasesitus puhvri peatamine..." Basic.Main.StopStreaming="Lõpeta voogedastus" +Basic.Main.StoppingStreaming="Voogedastuse peatamine..." +Basic.Main.ForceStopStreaming="Lõpeta voogedastus (unusta viivitus)" Basic.MainMenu.File="&Fail" Basic.MainMenu.File.Export="&Ekspordi" @@ -225,6 +271,9 @@ Basic.MainMenu.File.Exit="Välju (&X)" Basic.MainMenu.Edit="Muuda (&E)" Basic.MainMenu.Edit.Undo="Võta tagasi (&U)" Basic.MainMenu.Edit.UndoAction="&Võta tagasi $1" +Basic.MainMenu.Edit.LockPreview="&Lukusta eelvaade" +Basic.MainMenu.Edit.Scale.Canvas="Lõuend (%1x%2)" +Basic.MainMenu.Edit.Scale.Output="Väljund (%1x%2)" Basic.MainMenu.Edit.Transform.Rotate90CW="Pööra 90 kraadi paremale" Basic.MainMenu.Edit.Transform.Rotate90CCW="Pööra 90 kraadi vasakule" Basic.MainMenu.Edit.Transform.Rotate180="Pööra 180 kraadi" @@ -244,15 +293,36 @@ Basic.MainMenu.View.StatusBar="&Olekuriba" Basic.MainMenu.SceneCollection="&Stseeni kogumik" Basic.MainMenu.Profile="&Profiil" +Basic.MainMenu.Profile.Import="Impordi profiil" +Basic.MainMenu.Profile.Export="Ekspordi profiil" +Basic.MainMenu.SceneCollection.Import="Impordi stseeni kollektsioon" +Basic.MainMenu.SceneCollection.Export="Ekspordi stseeni kollektsioon" +Basic.MainMenu.Profile.Exists="Profiil on juba olemas" +Basic.MainMenu.SceneCollection.Exists="Stseeni kollektsioon on juba olemas" +Basic.MainMenu.Tools="&Tööriistad" +Basic.MainMenu.Help="&Abi" +Basic.MainMenu.Help.Website="Külasta &Kodulehte" Basic.MainMenu.Help.Logs="&Logifailid" +Basic.MainMenu.Help.Logs.ShowLogs="&Näita logifaile" +Basic.MainMenu.Help.Logs.UploadCurrentLog="Lae ülesse &praegune logifail" +Basic.MainMenu.Help.Logs.UploadLastLog="Lae ülesse &viimatine logifail" +Basic.MainMenu.Help.Logs.ViewCurrentLog="&Vaata praegust logifaili" Basic.MainMenu.Help.CheckForUpdates="Otsi värskendusi" +Basic.Settings.ProgramRestart="Nende sätete jõustumiseks tuleb taaskäivitada programm." +Basic.Settings.ConfirmTitle="Kinnita muudatused" +Basic.Settings.Confirm="Teil on salvestamata muutusi. Salvestame muudatused?" +Basic.Settings.General="Üldine" Basic.Settings.General.Theme="Teema" Basic.Settings.General.Language="Keel" Basic.Settings.General.Projectors="Projektorid" +Basic.Settings.General.SysTray="Süsteemi salve" +Basic.Settings.General.SysTrayWhenStarted="Minimeeri süsteemi salve käivitumisel" +Basic.Settings.General.SystemTrayHideMinimize="Alati Minimeeri süsteemi salve käivitumisel, tegumiriba asemel" +Basic.Settings.General.SaveProjectors="Salvesta projektorid väljumisel" Basic.Settings.Stream="Voogedastus" Basic.Settings.Stream.StreamType="Voogedastuse tüüp" @@ -261,14 +331,25 @@ Basic.Settings.Output="Väljund" Basic.Settings.Output.Format="Salvestusvorming" Basic.Settings.Output.Encoder="Kodeerija" Basic.Settings.Output.SelectDirectory="Vali salvestuskaust" +Basic.Settings.Output.SelectFile="Vali salvestus fail" Basic.Settings.Output.Mode="Väljundrežiim" Basic.Settings.Output.Mode.Simple="Lihtne" Basic.Settings.Output.Mode.Adv="Täpsemad seaded" +Basic.Settings.Output.Mode.FFmpeg="FFmpeg väljund" +Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Ei saa hinnata mälukasutust. Palun määrake maksimaalne piir." +Basic.Settings.Output.Simple.RecordingQuality.Lossless="Kadudeta kvaliteet, tohutult suur failimaht" +Basic.Settings.Output.Simple.Warn.Lossless="Hoiatus: Kadudeta kvaliteet tekitab tohutult suuri faile! Kadudeta kvaliteet võib kasutada kuni 7 Gb kettaruumi minuti kohta, kõrge eraldusvõime ja kaadrisagedusega. Kadudeta kvaliteeti ei ole soovitatav kasutada pikkade salvestiste jaoks, kui teil just ei ole väga palju vaba kettaruumi." +Basic.Settings.Output.Simple.Warn.Lossless.Msg="Kas soovite kindlasti kasutada kadudeta kvaliteeti?" +Basic.Settings.Output.Simple.Warn.Lossless.Title="Kadudeta kvaliteedi hoiatus!" Basic.Settings.Output.Simple.Encoder.Software="Tarkvara (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Riistvara (QSV)" Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Riistvara (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Riistvara (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Tarkvara (x264 madal CPU kasutus, suurendab faili suurust)" +Basic.Settings.Output.VideoBitrate="Video bitikiirus" +Basic.Settings.Output.AudioBitrate="Audio bitikiirus" +Basic.Settings.Output.Reconnect="Taasühendu automaatselt" +Basic.Settings.Output.EncoderPreset="Kodeerija eelseadistus(kõrgem = vähem CPU)" Basic.Settings.Output.Adv.AudioTrack="Helirada" Basic.Settings.Output.Adv.Streaming="Voogedastus" @@ -276,45 +357,97 @@ Basic.Settings.Output.Adv.Audio.Track1="Rada 1" Basic.Settings.Output.Adv.Audio.Track2="Rada 2" Basic.Settings.Output.Adv.Audio.Track3="Rada 3" Basic.Settings.Output.Adv.Audio.Track4="Rada 4" +Basic.Settings.Output.Adv.Audio.Track5="Rada 5" +Basic.Settings.Output.Adv.Audio.Track6="Rada 6" Basic.Settings.Output.Adv.Recording="Salvestus" Basic.Settings.Output.Adv.Recording.Type="Tüüp" +Basic.Settings.Output.Adv.Recording.Type.Standard="Standartne" +Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Kohandatud väljund (FFmpeg)" +Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Kasuta voogedastuse kodeerijat)" Basic.Settings.Output.Adv.Recording.Filename="Failinime vorming" Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Kirjuta üle, kui fail on olemas" Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg väljundi tüüp" Basic.Settings.Output.Adv.FFmpeg.Type.URL="Kirjuta URL-ile" Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Kirjuta faili" +Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common="Levinud salvestus formaadid" Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Kõik failid" Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Faili rada või URL" Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Heli" Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Video" Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Vaikevorming" +Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Failiteest või URL-ist arvatud Audio/Video kodeering" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Vaikekodeerija" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Lülita välja kodeerija" +Basic.Settings.Output.Adv.FFmpeg.VEncoder="Video kodeerija" +Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Video kodeerija sätted (kui on)" +Basic.Settings.Output.Adv.FFmpeg.AEncoder="Heli kodeerija" +Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Heli kodeerija sätted (kui on)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Võtmekaadri intervall (kaadrit)" Basic.Settings.Video="Video" Basic.Settings.Video.Adapter="Kuvaadapter:" +Basic.Settings.Video.BaseResolution="Alus (lõuendi) resolutsioon:" +Basic.Settings.Video.FPS="FPS:" Basic.Settings.Video.Numerator="Lugeja:" Basic.Settings.Video.Denominator="Nimetaja:" Basic.Settings.Video.InvalidResolution="Eraldusvõime ei sobi. See peab olema kujul [width]x[height] (nt 1920x1080)" +Basic.Settings.Audio="Heli" +Basic.Settings.Audio.Channels="Kanalid" Basic.Settings.Audio.DesktopDevice="Töölaua heliseade" Basic.Settings.Audio.DesktopDevice2="Töölaua heliseade 2" +Basic.Settings.Audio.UnknownAudioDevice="[Seade ühendamata või pole saadaval]" +Basic.Settings.Advanced.General.ProcessPriority.High="Kõrge" +Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Normaalsest kõrgem" +Basic.Settings.Advanced.General.ProcessPriority.Normal="Normaalne" +Basic.Settings.Advanced.General.ProcessPriority.Idle="Tegevusetu" +Basic.Settings.Advanced.Video.ColorFormat="Värvi formaat" +Basic.Settings.Advanced.Video.ColorRange="YUV värviruumi vahemik" Basic.Settings.Advanced.Video.ColorRange.Partial="Osaline" Basic.Settings.Advanced.Video.ColorRange.Full="Täielik" +Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Vaikeseade" +Basic.Settings.Advanced.StreamDelay="Voogedastuse Viivitus" +Basic.Settings.Advanced.StreamDelay.Duration="Kestvus (sekundit)" +Basic.Settings.Advanced.StreamDelay.MemoryUsage="Hinnanguline mälu hõivatus: %1 MB" +Basic.Settings.Advanced.Network="Võrk" +Basic.Settings.Advanced.Network.EnableLowLatencyMode="Madal-viivitus režiim" Basic.AdvAudio.Name="Nimi" Basic.AdvAudio.Volume="Helitugevus (%)" +Basic.AdvAudio.Monitoring.None="Välja lülitatud" Basic.AdvAudio.AudioTracks="Rajad" +Basic.Settings.Hotkeys="Kiirklahvid" +Basic.SystemTray.Show="Näita" +Basic.SystemTray.Hide="Peida" Hotkeys.Insert="Sisesta" Hotkeys.Delete="Kustuta" +Hotkeys.Home="Home" +Hotkeys.PageUp="Page Up" +Hotkeys.PageDown="Page Down" +Hotkeys.NumLock="Num Lock" +Hotkeys.ScrollLock="Scroll Lock" +Hotkeys.CapsLock="Suurtähelukk" +Hotkeys.Backspace="Tagasisammuklahv" +Hotkeys.Tab="Tab" +Hotkeys.Print="Prindi" +Hotkeys.Left="Vasakule" +Hotkeys.Right="Paremale" +Hotkeys.Up="Üles" +Hotkeys.Down="Alla" +Hotkeys.Windows="Windows" +Hotkeys.Menu="Menüü" + +Mute="Vaigista" diff --git a/UI/data/locale/eu-ES.ini b/UI/data/locale/eu-ES.ini index 98208a6..8cd8754 100644 --- a/UI/data/locale/eu-ES.ini +++ b/UI/data/locale/eu-ES.ini @@ -31,6 +31,9 @@ DroppedFrames="Galdutako fotogramak %1 (%2%)" PreviewProjector="Pantaila osoko proiektorea (aurrebista)" SceneProjector="Pantaila osoko proiektorea (eszena)" SourceProjector="Pantaila osoko proiektorea (iturburua)" +PreviewWindow="Leihodun proiektorea (aurrebista)" +SceneWindow="Leihodun proiektorea (Eszena)" +SourceWindow="Leihodun proiektorea (iturburua)" Clear="Garbitu" Revert="Leheneratu" Show="Erakutsi" @@ -56,6 +59,92 @@ Deprecated="Zaharkitua" ReplayBuffer="Erreprodukzio bufferra" Import="Inportatu" Export="Esportatu" +Copy="Kopiatu" +Paste="Itsatsi" +PasteReference="Itsatsi (Erreferentzia)" +PasteDuplicate="Itsatsi (Bikoiztu)" +RemuxRecordings="Birmultiplexatu grabazioak" +Next="Hurrengoa" +Back="Atzera" + +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." +AlreadyRunning.LaunchAnyway="Abiarazi hala ere" + +Copy.Filters="Kopiatu iragazkiak" +Paste.Filters="Itsatsi iragazkiak" + +BandwidthTest.Region="Eskualdea" +BandwidthTest.Region.US="Estatu Batuak" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Asia" +BandwidthTest.Region.Other="Beste bat" + +Basic.FirstStartup.RunWizard="Martxan jarri nahi duzu ezarpen automatikoen morroia? Bestela eskuz konfigura dezakezu zure ezarpenak leiho nagusiko Ezarpenak botoia klikatuta." +Basic.FirstStartup.RunWizard.BetaWarning="(Oharra: ezarpen automatikoen morroia beta egoeran dago une honetan)" +Basic.FirstStartup.RunWizard.NoClicked="Iritziz aldatzen baduzu, martxan jar dezakezu ezarpen automatikoen morroia berriro Tresnen menuan." + +Basic.AutoConfig="Ezarpen automatikoen morroia" +Basic.AutoConfig.Beta="Ezarpen automatikoen morroia (Beta)" +Basic.AutoConfig.ApplySettings="Aplikatu ezarpenak" +Basic.AutoConfig.StartPage="Erabilera-informazioa" +Basic.AutoConfig.StartPage.SubTitle="Zehaztu zertarako erabili nahi duzun programa" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimizatu transmisiorako, grabazioa bigarren mailakoa da" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimizatu grabazioa, ez du transmitituko" +Basic.AutoConfig.VideoPage="Bideo-ezarpenak" +Basic.AutoConfig.VideoPage.SubTitle="Zehaztu erabili nahi dituzun bideo-ezarpenak" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Erabili unekoa (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Erakutsi %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Erabili unekoa (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Izan daiteke 60 edo 30, baina ahal izanez gero 60 nahiago" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Izan daiteke 60 edo 30, baina nahiago dut bereizmen altua" +Basic.AutoConfig.VideoPage.CanvasExplanation="Oharra: grabaziorako erabiliko duzun bereizmena ez du zertan oihalaren (oinarriaren) bereizmen berdina. Uneko transmisioaren/grabazioaren bereizmena behera eskalatu daiteke txikiagotzeko baliabideen erabilera edo bit emariaren eskakizuna." +Basic.AutoConfig.StreamPage="Transmisioaren informazioa" +Basic.AutoConfig.StreamPage.SubTitle="Sartu transmisioaren informazioa" +Basic.AutoConfig.StreamPage.Service="Zerbitzua" +Basic.AutoConfig.StreamPage.Service.ShowAll="Erakutsi denak..." +Basic.AutoConfig.StreamPage.Server="Zerbitzaria" +Basic.AutoConfig.StreamPage.StreamKey="Transmisio giltza" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Esteka)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Kalkulatu bit emaria banda zabaleraren testaren bidez (minutu batzuk beharko ditu)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Hobetsi hardware-kodeketa" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardware-kodeketa txikiagotzen du PUZaren erabilera, baina bit emari handiagoa eskatzen du kalitate maila bera lortzeko." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Transmisioaren alarma" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Banda zabaleraren testa zure kanalera audiorik gabeko ausazko bideo datuak transmitituko ditu. Posible baduzu, komeni da behin behinean transmisioen bideoen gordeketa ezgaitzea eta transmisio modua pribatua ezartzea testa bukatu arte. Jarraitu nahi duzu?" +Basic.AutoConfig.TestPage="Azken emaitzak" +Basic.AutoConfig.TestPage.SubTitle.Testing="Programak une honetan test sorta bat exekutatzen ari da kalkulatzeko zein diren ezarpenik egokienak" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testa osatu da" +Basic.AutoConfig.TestPage.TestingBandwidth="Banda zabalerako test bat egikaritzen, minutu batzuk beharko ditu..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Hona konektatzen: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Huts egin du hainbat zerbitzarirekin konektatzen, probatu zure Internet konexioa eta saiatu berriro." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Banda zabaleraren proba: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Transmisioaren kodeketa probatzen, minutu batzuk beharko ditu..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Grabazioaren kodeketa probatzen, minutu batzuk beharko ditu..." +Basic.AutoConfig.TestPage.TestingRes="Bereizmenak probatzen, minutu batzuk beharko ditu..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Huts egin du kodetzailea abiatzen" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Probatzen %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Transmisioaren kodetzailea" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Grabazioaren kodetzailea" +Basic.AutoConfig.TestPage.Result.Header="Programaren arabera hauek dira zuretzako ezarpenik egokienak:" +Basic.AutoConfig.TestPage.Result.Footer="Ezarpen hauek onartzeko, klika Aplikatu ezarpenak. Morroiaren bidez birkonfiguratzeko eta saiatzeko berriro klika Atzera. Ezarpenak eskuz zehazteko klika Utzi eta ireki Ezarpenak." + +Basic.Stats="Estatistikak" +Basic.Stats.CPUUsage="PUZ erabilpena" +Basic.Stats.HDDSpaceAvailable="Toki erabilgarria disko gogorrean" +Basic.Stats.MemoryUsage="Memoria erabilpena" +Basic.Stats.AverageTimeToRender="Fotograma errendatzeko batez besteko denbora" +Basic.Stats.SkippedFrames="Kodetze atzerapena dela eta saltatutako fotogramak" +Basic.Stats.MissedFrames="Kodetze atzerapena dela eta galdutako fotogramak" +Basic.Stats.Output.Stream="Transmisioa" +Basic.Stats.Output.Recording="Grabazioa" +Basic.Stats.Status="Egoera" +Basic.Stats.Status.Recording="Grabatzen" +Basic.Stats.Status.Live="Martxan" +Basic.Stats.Status.Reconnecting="Birkonektatzen" +Basic.Stats.Status.Inactive="Inaktiboa" +Basic.Stats.DroppedFrames="Jaregindako fotogramak (Sarean)" +Basic.Stats.MegabytesSent="Datu irteera denetara" +Basic.Stats.Bitrate="Bit-emaria" Updater.Title="Eguneraketa berria eskuragarri" Updater.Text="Eguneraketa berri bat eskuragarri dago:" @@ -375,6 +464,7 @@ Basic.Settings.General="Orokorra" Basic.Settings.General.Theme="Gaia" Basic.Settings.General.Language="Hizkuntza" Basic.Settings.General.EnableAutoUpdates="Abiaraztean begiratu automatikoki eguneraketarik ba ote dagoen" +Basic.Settings.General.OpenStatsOnStartup="Ireki estatistikak abiatzean" Basic.Settings.General.WarnBeforeStartingStream="Erakutsi baieztapen elkarrizketa transmisioak hasterakoan" Basic.Settings.General.WarnBeforeStoppingStream="Erakutsi baieztapen elkarrizketa transmisioak gelditzean" Basic.Settings.General.Projectors="Projektoreak" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Partziala" Basic.Settings.Advanced.Video.ColorRange.Full="Osoa" Basic.Settings.Advanced.Audio.MonitoringDevice="Audioa kontrolatzeko gailua" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Lehenetsia" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Ezgaitu Windows audio ducking" Basic.Settings.Advanced.StreamDelay="Taansmisio-atzerapena" Basic.Settings.Advanced.StreamDelay.Duration="Iraupena (segundoak)" Basic.Settings.Advanced.StreamDelay.Preserve="Mantendu ebaketa puntua (handitu atzerapena) birkonektatzean" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Gutxienez pista bat hautatu behar duzu" OutputWarnings.MultiTrackRecording="Oharra: Zenbait formatuk (esaterako FLV-k) ez ditu pista anitzak onartzen grabazioan" OutputWarnings.MP4Recording="Kontuz: MP4 formatuz gordetako grabazioak izan daitezke berreskuraezinak fitxategia ezin bada bukatu (esate baterako energia etenagatik). Hainbat audio pista grabatu nahi baduzu erabil dezakezu MKV formatua eta mp4 bihurtu grabazioa bukatu ondoren (Fitxategia->Bihurtu grabazioak)" +FinalScene.Title="Ezabatu eszena" +FinalScene.Text="Gutxienez eszena bat egon behar du." + diff --git a/UI/data/locale/fi-FI.ini b/UI/data/locale/fi-FI.ini index cb8d84d..1806ab8 100644 --- a/UI/data/locale/fi-FI.ini +++ b/UI/data/locale/fi-FI.ini @@ -31,6 +31,9 @@ DroppedFrames="Pudotettuja frameja %1 (%2%)" PreviewProjector="Peilaa monitoriin (Esikatselu)" SceneProjector="Peilaa monitoriin (Skene)" SourceProjector="Peilaa monitoriin (Lähde)" +PreviewWindow="Peilaa ikkunaan (Esikatselu)" +SceneWindow="Peilaa ikkunaan (Skene)" +SourceWindow="Peilaa ikkunaan (Lähde)" Clear="Tyhjennä" Revert="Palauta" Show="Näytä" @@ -56,6 +59,92 @@ Deprecated="Vanhentunut" ReplayBuffer="Toistopuskuri" Import="Tuo" Export="Vie" +Copy="Kopioi" +Paste="Liitä" +PasteReference="Liitä (viite)" +PasteDuplicate="Liitä (Kopio)" +RemuxRecordings="Muunna tallenteet" +Next="Seuraava" +Back="Edellinen" + +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ä." +AlreadyRunning.LaunchAnyway="Käynnistä joka tapauksessa" + +Copy.Filters="Kopioi suodattimet" +Paste.Filters="Liitä suodattimet" + +BandwidthTest.Region="Alue" +BandwidthTest.Region.US="Yhdysvallat" +BandwidthTest.Region.EU="Eurooppa" +BandwidthTest.Region.Asia="Aasia" +BandwidthTest.Region.Other="Muu" + +Basic.FirstStartup.RunWizard="Haluatko määrittää asetukset automaattisesti? Voit myös määrittää asetukset käsin painamalla \"Asetukset\" -painiketta päänäkymässä." +Basic.FirstStartup.RunWizard.BetaWarning="(Huomautus: automaattinen määritystoiminto on tällä hetkellä beta)" +Basic.FirstStartup.RunWizard.NoClicked="Jos muutat mielesi, voit käynnistää automaattisen määritystoiminnon milloin tahansa \"Työkalut\" -valikosta." + +Basic.AutoConfig="Automaattinen määritystoiminto" +Basic.AutoConfig.Beta="Automaattinen määritystoiminto (Beta)" +Basic.AutoConfig.ApplySettings="Ota käyttöön" +Basic.AutoConfig.StartPage="Käyttötiedot" +Basic.AutoConfig.StartPage.SubTitle="Määrittele kuinka haluat käyttää ohjelmaa" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimoi lähettämiseen, tallentaminen on toissijaista" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimoi tallentamiseen, lähettäminen on toissijaista" +Basic.AutoConfig.VideoPage="Kuva-asetukset" +Basic.AutoConfig.VideoPage.SubTitle="Määrittele haluamasi kuva-asetukset" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Käytä nykyistä (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Monitori %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Käytä nykyistä (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Joko 60 tai 30, mutta suositaan 60 kun mahdollista" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Joko 60 tai 30, mutta suositaan suurta resoluutiota" +Basic.AutoConfig.VideoPage.CanvasExplanation="Huomautus: Piirtoalueen resoluutio ei välttämättä ole sama kuin resoluutio jolla lähetetään tai tallennetaan. Lähetyksen/tallennuksen resoluutio saatetaan skaalata piirtoalueesta pienemmäksi resurssien käytön vähentämiseksi tai bitrate vaatimusten vuoksi." +Basic.AutoConfig.StreamPage="Lähetyksen tiedot" +Basic.AutoConfig.StreamPage.SubTitle="Ole hyvä ja syötä lähetyksen tiedot" +Basic.AutoConfig.StreamPage.Service="Palvelu" +Basic.AutoConfig.StreamPage.Service.ShowAll="Näytä kaikki..." +Basic.AutoConfig.StreamPage.Server="Palvelin" +Basic.AutoConfig.StreamPage.StreamKey="Striimiavain" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Linkki)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Arvioi bitrate kaistan nopeustestin avulla (saattaa kestää muutaman minuutin)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Suosi laitteistoenkoodausta" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Laitteistoenkoodaus poistaa suurimman osan CPU-käytöstä, mutta saattaa vaatia suuremman bitraten saavuttaakseen saman laadun." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Lähetysvaroitus" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Kaistan testauksessa lähetetään satunnaista videodataa kanavallesi ilman ääntä. On suositeltavaa ottaa pois käytöstä lähetysten tallennus ja asettaa lähetys yksityiseksi, kunnes testi on suoritettu. Jatketaanko?" +Basic.AutoConfig.TestPage="Lopulliset tulokset" +Basic.AutoConfig.TestPage.SubTitle.Testing="Ohjelma käynnistää nyt muutamia testejä ihanteellisten asetusten arvioimiseksi" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testaus valmis" +Basic.AutoConfig.TestPage.TestingBandwidth="Suoritetaan kaistan testausta, tämä saattaa kestää muutaman minuutin..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Yhdistetään: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Palvelimiin ei saada yhteyttä, ole hyvä ja tarkista internet-yhteys ja yritä uudelleen." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Testataan kaistaa: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Testataan lähetysenkooderia, tämä saattaa kestää hetken..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testataan tallennusenkooderia, tämä saattaa kestää hetken..." +Basic.AutoConfig.TestPage.TestingRes="Testataan resoluutioita, tämä saattaa kestää muutaman minuutin..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Enkooderin käynnistys epäonnistui" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Testataan %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Lähetysenkooderi" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Tallennusenkooderi" +Basic.AutoConfig.TestPage.Result.Header="Ohjelma on määrittänyt nämä asetukset ihanteellisimmaksi juuri sinulle:" +Basic.AutoConfig.TestPage.Result.Footer="Ottaaksesi asetukset käyttöön, paina \"Ota käyttöön\". Määrittääksesi asetukset uudelleen, paina \"Edellinen\". Jos haluat määrittää asetukset itse, paina \"Peruuta\" ja avaa asetukset." + +Basic.Stats="Tilastot" +Basic.Stats.CPUUsage="CPU-käyttö" +Basic.Stats.HDDSpaceAvailable="Tallennustilaa vapaana" +Basic.Stats.MemoryUsage="Muistinkäyttö" +Basic.Stats.AverageTimeToRender="Keskimääräinen aika framen renderoimiseen" +Basic.Stats.SkippedFrames="Ohitettuja frameja enkoodauksen hidastelusta" +Basic.Stats.MissedFrames="Frameja menetetty renderoijan hidastelusta" +Basic.Stats.Output.Stream="Lähetys" +Basic.Stats.Output.Recording="Tallennus" +Basic.Stats.Status="Tila" +Basic.Stats.Status.Recording="Tallennus" +Basic.Stats.Status.Live="LIVE" +Basic.Stats.Status.Reconnecting="Yhdistetään uudelleen" +Basic.Stats.Status.Inactive="Ei käytössä" +Basic.Stats.DroppedFrames="Pudotettuja frameja (verkko)" +Basic.Stats.MegabytesSent="Datan ulostulo yhteensä" +Basic.Stats.Bitrate="Bitrate" Updater.Title="Uusi päivitys on saatavilla" Updater.Text="Uusi päivitys on saatavilla:" @@ -303,7 +392,7 @@ Basic.Main.ForceStopStreaming="Lopeta lähetys (ohita viive)" Basic.MainMenu.File="&Tiedosto" Basic.MainMenu.File.Export="&Vie" Basic.MainMenu.File.Import="T&uo" -Basic.MainMenu.File.ShowRecordings="Näytä &tallennukset" +Basic.MainMenu.File.ShowRecordings="Näytä &tallenteet" Basic.MainMenu.File.Remux="Mu&unna tallenne" Basic.MainMenu.File.Settings="&Asetukset" Basic.MainMenu.File.ShowSettingsFolder="Avaa asetuskansio" @@ -375,6 +464,7 @@ Basic.Settings.General="Yleiset" Basic.Settings.General.Theme="Teema" Basic.Settings.General.Language="Kieli" Basic.Settings.General.EnableAutoUpdates="Tarkista päivitykset automaattisesti käynnistäessä" +Basic.Settings.General.OpenStatsOnStartup="Avaa tilastoikkuna käynnistyksessä" Basic.Settings.General.WarnBeforeStartingStream="Näytä varmistus-ikkuna kun lähetys aloitetaan" Basic.Settings.General.WarnBeforeStoppingStream="Näytä varmistus-ikkuna kun lähetys pysäytetään" Basic.Settings.General.Projectors="Peilaukset" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Osittainen" Basic.Settings.Advanced.Video.ColorRange.Full="Täysi" Basic.Settings.Advanced.Audio.MonitoringDevice="Äänen monitorointilaite" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Oletusarvo" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Poista Windowsin äänien hiljennys käytöstä (Audio Ducking)" Basic.Settings.Advanced.StreamDelay="Lähetyksen viive" Basic.Settings.Advanced.StreamDelay.Duration="Kesto (sekunteina)" Basic.Settings.Advanced.StreamDelay.Preserve="Säilytä katkaisupiste (lisää viivettä) uudelleenyhdistettäessä" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Sinun täytyy valita ainakin yksi raita" OutputWarnings.MultiTrackRecording="Varoitus: Jotkin muodot (kuten FLV), eivät tue useampaa raitaa per tallennus" OutputWarnings.MP4Recording="Varoitus: MP4-muotoon tallentaessa tiedostoista tulee lukukelvottomia, mikäli niitä ei voi viimeistellä. (esim. johtuen BSOD:sta, sähkökatkosta jne.) Jos haluat tallentaa useampaa ääniraitaa, kannattaa käyttää MKV-muotoa ja muuntaa jälkikäteen MP4:ksi. (Tiedosto->Muunna tallenne)" +FinalScene.Title="Poista skene" +FinalScene.Text="Ainakin yksi skene pitää olla olemassa." + diff --git a/UI/data/locale/fr-FR.ini b/UI/data/locale/fr-FR.ini index fc438bf..9ade94f 100644 --- a/UI/data/locale/fr-FR.ini +++ b/UI/data/locale/fr-FR.ini @@ -31,6 +31,9 @@ DroppedFrames="Images perdues : %1 (%2%)" PreviewProjector="Projecteur plein écran (aperçu)" SceneProjector="Projecteur plein écran (scène)" SourceProjector="Projecteur plein écran (source)" +PreviewWindow="Projecteur fenêtré (aperçu)" +SceneWindow="Projecteur fenêtré (scène)" +SourceWindow="Projecteur fenêtré (source)" Clear="Effacer" Revert="Annuler" Show="Afficher" @@ -56,6 +59,92 @@ Deprecated="Obsolète" ReplayBuffer="Tampon de relecture" Import="Importer" Export="Exporter" +Copy="Copier" +Paste="Coller" +PasteReference="Coller (Référence)" +PasteDuplicate="Coller (Dupliquer)" +RemuxRecordings="Convertir les enregistrements" +Next="Suivant" +Back="Retour" + +AlreadyRunning.Title="OBS est déjà en cours d'exécution" +AlreadyRunning.Text="OBS est déjà en cours d'exécution, s'il vous plait, fermer toute autre instances existantes d'OBS avant d'en exécuter une nouvelle. Vérifier dans votre barre d'état s'il n'est pas réduit et en cours d’exécution." +AlreadyRunning.LaunchAnyway="Démarrer tout de même" + +Copy.Filters="Copier les filtres" +Paste.Filters="Coller les filtres" + +BandwidthTest.Region="Région" +BandwidthTest.Region.US="États-Unis" +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.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." + +Basic.AutoConfig="Assistant de Configuration automatique" +Basic.AutoConfig.Beta="Assistant de Configuration automatique (version bêta)" +Basic.AutoConfig.ApplySettings="Appliquer les paramètres" +Basic.AutoConfig.StartPage="Informations sur l’utilisation" +Basic.AutoConfig.StartPage.SubTitle="Spécifiez ce que vous voulez utiliser le programme pour" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimiser pour diffusion, l’enregistrement est secondaire" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimiser pour l’enregistrement, je ne diffuse pas" +Basic.AutoConfig.VideoPage="Paramètres vidéo" +Basic.AutoConfig.VideoPage.SubTitle="Spécifiez les réglages vidéo que vous souhaitez utiliser" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Courant (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Écran %1 (%2x%3)_" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Courant (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 ou 30, mais préférez 60" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 ou 30, mais préfèrent haute résolution" +Basic.AutoConfig.VideoPage.CanvasExplanation="Remarque : la résolution (de base) du canevas n'est pas nécessairement la même que la résolution avec laquelle vous allez diffuser (stream) ou enregistrer. La résolution actuelle de stream/enregistrement peut être réduite à partir de la résolution du canevas afin de diminuer l'utilisation des ressources et de la bande passante." +Basic.AutoConfig.StreamPage="Information de flux" +Basic.AutoConfig.StreamPage.SubTitle="Entrez vos informations de stream" +Basic.AutoConfig.StreamPage.Service="Service" +Basic.AutoConfig.StreamPage.Service.ShowAll="Afficher tout..." +Basic.AutoConfig.StreamPage.Server="Serveur" +Basic.AutoConfig.StreamPage.StreamKey="Clé de stream" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Lien)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Test de la bande passantes pour estimer le débit(peut prendre quelques minutes)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Préférez l’encodage matériel" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="L'encodage matériel minimise l'utilisation du processeur (CPU), mais peut nécessiter un débit vidéo plus élevé pour obtenir le même niveau de qualité." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Avertissement de diffusion" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Le test de bande passante est sur le point de diffuser (stream) des données vidéo aléatoire sans audio sur votre chaîne. Si vous avez la possibilité, il est recommandé de désactiver temporairement l'enregistrement des diffusions et de configurer le stream en privé jusqu'à ce que le test soit terminé. Continuer ?" +Basic.AutoConfig.TestPage="Résultat final" +Basic.AutoConfig.TestPage.SubTitle.Testing="Le programme s’exécute maintenant une série de tests pour estimer les paramètres idéales" +Basic.AutoConfig.TestPage.SubTitle.Complete="Test complet" +Basic.AutoConfig.TestPage.TestingBandwidth="Test de la bande passante en cours, cela peut prendre quelques minutes..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Connection à : %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Impossible de se connecter à aucun serveur, veuillez vérifier votre connexion internet et réessayez." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Test de la bande passante pour: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Test de l'encodeur de diffusion (stream), cela peut prendre une minute..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Test de l'encodeur d'enregistrement, cela peut prendre une minute..." +Basic.AutoConfig.TestPage.TestingRes="Tests de résolutions, cela peut prendre quelques minutes..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Échec lors du démarrage de l'encodeur" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Test FPS %1x%2 %3..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Encodeur de diffusion" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Erreur d'enregistrement" +Basic.AutoConfig.TestPage.Result.Header="Le programme a déterminé que ces estimations de paramètres sont idéal pour vous :" +Basic.AutoConfig.TestPage.Result.Footer="Pour utiliser ces paramètres, cliquez sur « appliquer les paramètres. » Pour reconfigurer l’Assistant et essayer de nouveau, cliquez sur « précédent. » Pour configurer les paramètres vous-même, cliquez sur « Annuler et ouvrir les paramètres. »" + +Basic.Stats="Statistiques" +Basic.Stats.CPUUsage="Utilisation CPU" +Basic.Stats.HDDSpaceAvailable="Espace disque dur disponible" +Basic.Stats.MemoryUsage="Utilisation de la mémoire" +Basic.Stats.AverageTimeToRender="Temps moyen pour rendre une image" +Basic.Stats.SkippedFrames="Sauts d'image dû à la latence d'encodage" +Basic.Stats.MissedFrames="Images manqués en raison du retard de rendu" +Basic.Stats.Output.Stream="Flux" +Basic.Stats.Output.Recording="Enregistrement" +Basic.Stats.Status="État" +Basic.Stats.Status.Recording="Enregistrement en cours" +Basic.Stats.Status.Live="DIRECT" +Basic.Stats.Status.Reconnecting="Reconnexion" +Basic.Stats.Status.Inactive="Inactif" +Basic.Stats.DroppedFrames="Perte d’images (réseau)" +Basic.Stats.MegabytesSent="Sortie Total des Données" +Basic.Stats.Bitrate="Débit" Updater.Title="Nouvelle mise à jour disponible" Updater.Text="Une nouvelle mise à jour est disponible :" @@ -375,6 +464,7 @@ Basic.Settings.General="Général" Basic.Settings.General.Theme="Thème" Basic.Settings.General.Language="Langue" Basic.Settings.General.EnableAutoUpdates="Vérifier automatiquement les mises à jour au démarrage" +Basic.Settings.General.OpenStatsOnStartup="Ouvrir la boîte de dialogue des statistiques au démarrage" Basic.Settings.General.WarnBeforeStartingStream="Afficher une boîte de dialogue de confirmation au démarrage d'un stream" Basic.Settings.General.WarnBeforeStoppingStream="Afficher une boîte de dialogue de confirmation à l'arrêt d'un stream" Basic.Settings.General.Projectors="Projecteurs" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Partielle" Basic.Settings.Advanced.Video.ColorRange.Full="Complète" Basic.Settings.Advanced.Audio.MonitoringDevice="Dispositif de surveillance audio" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Par défaut" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Désactiver l'atténuation audio de Windows" Basic.Settings.Advanced.StreamDelay="Retard du stream" Basic.Settings.Advanced.StreamDelay.Duration="Durée (en secondes)" Basic.Settings.Advanced.StreamDelay.Preserve="Préserver le point de coupure (augmente le retard) lors d'une reconnexion" @@ -615,5 +706,8 @@ SceneItemHide="Cacher '%1'" OutputWarnings.NoTracksSelected="Vous devez sélectionner au moins une piste" OutputWarnings.MultiTrackRecording="Attention : Certains formats (comme FLV) ne supportent pas les pistes multiples pour un même enregistrement" -OutputWarnings.MP4Recording="Avertissement: Les enregistrements sauvegardés sur MP4 seront irrécupérables si le fichier ne peut pas être finalisé (par exemple, à cause des BSOD, des pertes de puissance, etc.). Si vous voulez enregistrer plusieurs pistes audio, pensez à utiliser MKV et remux l'enregistrement au mp4 après qu'il est terminé (File-> Remux Recordings)" +OutputWarnings.MP4Recording="Avertissement : les enregistrements sauvegardés en MP4 seront irrécupérables si le fichier ne peut pas être finalisé (ex. : à cause des BSOD, coupure de l'alimentation, etc...). Si vous voulez enregistrer plusieurs pistes audio, pensez à utiliser MKV et convertir l'enregistrement en mp4 après qu'il soit terminé (Fichier -> Convertir un enregistrement)" + +FinalScene.Title="Supprimer la scène" +FinalScene.Text="Il doit y avoir au moins une scène." diff --git a/UI/data/locale/gl-ES.ini b/UI/data/locale/gl-ES.ini index 4ba0744..6f3bc17 100644 --- a/UI/data/locale/gl-ES.ini +++ b/UI/data/locale/gl-ES.ini @@ -48,6 +48,12 @@ Bottom="Abaixo" + + + + + + Basic.AddTransition="Engadir transición configurable" Basic.RemoveTransition="Eliminar transición configurable" Basic.TransitionProperties="Propiedades da transición" @@ -404,3 +410,4 @@ SceneItemHide="Agochar '%1'" 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 c4470c9..e9e7e02 100644 --- a/UI/data/locale/he-IL.ini +++ b/UI/data/locale/he-IL.ini @@ -31,6 +31,9 @@ DroppedFrames="השמטת תמונות %1 (%2%)" PreviewProjector="מקרן מסך מלא (תצוגה מקדימה)" SceneProjector="מקרן מסך מלא (סצנה)" SourceProjector="מקרן מסך מלא (מקור)" +PreviewWindow="הקרנה בחלון (תצוגה מקדימה)" +SceneWindow="הקרנה בחלון (סצנה)" +SourceWindow="הקרנה בחלון (מקור)" Clear="נקה" Revert="החזר לקדמותו" Show="הצג" @@ -48,7 +51,111 @@ Left="שמאל" Right="ימין" Top="עליון" Bottom="תחתון" +Reset="אפס" +Hours="שעות" +Minutes="דקות" +Seconds="שניות" +Deprecated="הוצא משימוש" +ReplayBuffer="הרץ מחדש חוצץ" +Import="יבא" +Export="יצא" +Copy="העתק" +Paste="הדבק" +PasteReference="הדבק (הפניה)" +PasteDuplicate="הדבק (כפול)" +RemuxRecordings="רימיקס הקלטות" +Next="הבא" +Back="קודם" +AlreadyRunning.Title="OBS פועל כבר" +AlreadyRunning.Text="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="אשף קביעת התצורה האוטומטית (ביתא)" +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="קידוד בחומרה מבטלת רוב השימוש ב- CPU, אבל עשויים לדרוש קצב נתונים גבוה יותר בכדי להשיג את אותה רמת איכות." +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.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 מסגרות לדקה..." +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="שימוש במעבד" +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="לכידת משחק פעיל" QuickTransitions.SwapScenes="החלף סצינות תצוגה מקדימה/פלט לאחר המעבר" QuickTransitions.SwapScenesTT="החלף הסצינות של התצוגה המקדימה ושל הפלט לאחר המעבר (באם הסצינה המקורית של הפלט עדיין קיימת). \n פעולה זו לא תבטל כל שינוי שייתכן ובוצע לסצינה המקורית של הפלט." @@ -90,6 +197,9 @@ ConfirmRemove.Title="אשר הסרה" ConfirmRemove.Text="האם אתה בטוח שברצונך להסיר את '$1'?" ConfirmRemove.TextMultiple="האם אתה בטוח שברצונך להסיר %1 פריטים?" +Output.StartStreamFailed="נכשלה הפעלת זרימה" +Output.StartRecordingFailed="נכשלה הפעלת הקלטה" +Output.StartReplayFailed="נכשלה הפעלת מאגר החוזר" Output.ConnectFail.Title="ההתחברות נכשלה" Output.ConnectFail.BadPath="URL לא חוקי של נתיב או חיבור. נא בדוק את ההגדרות שלך כדי לוודא כי הם נכונים." @@ -165,6 +275,10 @@ 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="אנא הזן את השם של הסצנה" @@ -260,9 +374,12 @@ Basic.Main.Scenes="סצינות" Basic.Main.Sources="מקורות" 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="עצור זרם נתונים (בטל השהייה)" @@ -284,8 +401,14 @@ Basic.MainMenu.Edit.Redo="בצע שוב(&R)" Basic.MainMenu.Edit.UndoAction="בטל $1(&U)" Basic.MainMenu.Edit.RedoAction="בצע שוב $1(&R)" 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="שנה(&T)" Basic.MainMenu.Edit.Transform.EditTransform="ערוך שינוי...(&E)" +Basic.MainMenu.Edit.Transform.CopyTransform="העתק מעבר" +Basic.MainMenu.Edit.Transform.PasteTransform="הדבק מעבר" Basic.MainMenu.Edit.Transform.ResetTransform="אפס שינוי(&R)" Basic.MainMenu.Edit.Transform.Rotate90CW="סובב 90 מעלות בכיוון השעון" Basic.MainMenu.Edit.Transform.Rotate90CCW="סובב 90 מעלות בניגוד לכיוון השעון" @@ -310,6 +433,9 @@ Basic.MainMenu.View.StatusBar="&שורת מצב" Basic.MainMenu.SceneCollection="אוסף סצינות(&S)" Basic.MainMenu.Profile="פרופיל(&P)" +Basic.MainMenu.Profile.Import="ייבא פרופיל" +Basic.MainMenu.Profile.Export="ייצא פרופיל" +Basic.MainMenu.Profile.Exists="הפרופיל קיים כבר" Basic.MainMenu.Tools="& כלים" @@ -329,8 +455,10 @@ Basic.Settings.Confirm="קיימים שינויים שלא נשמרו. האם Basic.Settings.General="כללי" Basic.Settings.General.Theme="ערכת עיצוב" Basic.Settings.General.Language="שפה" +Basic.Settings.General.EnableAutoUpdates="בדוק באופן אוטומטי אם יש עדכונים בעת ההפעלה" Basic.Settings.General.WarnBeforeStartingStream="הצג תיבת דו-שיח לאישור בעת הפעלת זרם נתונים" Basic.Settings.General.WarnBeforeStoppingStream="הצג תיבת דו-שיח לאישור בעת עצירת זרם נתונים" +Basic.Settings.General.Projectors="מקרנים" Basic.Settings.General.HideProjectorCursor="הסתר את הסמן מעל מקרנים" Basic.Settings.General.Snapping="יישור הצמדת מקור" Basic.Settings.General.ScreenSnapping="הצמד מקורות לקצה המסך" @@ -339,6 +467,10 @@ Basic.Settings.General.SourceSnapping="הצמד מקור למקור נוסף" Basic.Settings.General.SnapDistance="רגישות צמד" Basic.Settings.General.RecordWhenStreaming="הקלטה אוטומטית בעת הזרמת נתונים" Basic.Settings.General.KeepRecordingWhenStreamStops="המשך הקלטה כאשר הזרמת נתונים מפסיקה" +Basic.Settings.General.SysTray="מגש המערכת" +Basic.Settings.General.SysTrayWhenStarted="מזער למגש המערכת בתחילה" +Basic.Settings.General.SystemTrayHideMinimize="מזער תמיד למגש המערכת במקום שורת המשימות" +Basic.Settings.General.SaveProjectors="שמור את המקרנים ביציאה" Basic.Settings.Stream="זרם נתונים" Basic.Settings.Stream.StreamType="סוג זרם נתונים" @@ -353,6 +485,14 @@ 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="(הערה: הקפד להגדיר hotkey עבור מאגר החוזר במקטע מקשי הקיצור)" +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="כמו זרם הנתונים" @@ -368,6 +508,7 @@ 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="תוכנה (x 264 השימוש בהגדרת מעבד נמוך, גודל הקובץ גדל)" Basic.Settings.Output.VideoBitrate="קצב סיביות וידאו" @@ -389,6 +530,8 @@ 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="סוג" @@ -416,6 +559,8 @@ Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="הגדרות מקודד וי Basic.Settings.Output.Adv.FFmpeg.AEncoder="מקודד אודיו" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="הגדרות מקודד שמע (אם בכלל)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="הגדרות Muxer (אם בכלל)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="מרווח מפתח(מסגרות)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="הצג את כל רכיבי codec (גם אם אינם תואמים)" 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" @@ -469,11 +614,17 @@ 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.AdvAudio="מאפייני קול מתקדמים" Basic.AdvAudio.Name="שם" @@ -481,6 +632,10 @@ 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="מקשי קיצור" @@ -491,6 +646,7 @@ Basic.Hotkeys.SelectScene="עבור לסצנה" Basic.SystemTray.Show="הצג" Basic.SystemTray.Hide="הסתר" +Basic.SystemTray.Message.Reconnecting="ההתקשרות נותקה. התחברות מחדש..." Hotkeys.Insert="הוסף" Hotkeys.Delete="מחק" @@ -538,4 +694,8 @@ SceneItemHide="הסתר '%1'" OutputWarnings.NoTracksSelected="עליך לבחור ערוץ אחד לפחות" OutputWarnings.MultiTrackRecording="אזהרה: תבניות מסוימות (כגון FLV) אינם תומכים במספר רצועות להקלטה" +OutputWarnings.MP4Recording="אזהרה: הקלטות שנשמרו MP4 תהיה בלתי שמישה במידה ולא ניתן להשלים את הקובץ (למשל כתוצאה מחלון כחול, אובדן כוח, וכו '). אם אתה רוצה להקליט מספר רצועות שמע, שקול להשתמש ב- MKV ובצע רימיקס ל- mp4 לאחר סיומה (קובץ-> Remux הקלטות)" + +FinalScene.Title="מחק סצינה" +FinalScene.Text="נדרשת סצנה אחת לפחות." diff --git a/UI/data/locale/hr-HR.ini b/UI/data/locale/hr-HR.ini index 8fb1343..9144202 100644 --- a/UI/data/locale/hr-HR.ini +++ b/UI/data/locale/hr-HR.ini @@ -55,6 +55,12 @@ Seconds="Sekundi" Deprecated="Prevaziđeno" + + + + + + QuickTransitions.SwapScenes="Zameni scene pregleda/izlaza nakon prelaza" QuickTransitions.SwapScenesTT="Zamenjuje scene pregleda i izlaza nakon prelaza (ako originalna scena izlaza još uvek postoji).\nOvo neće poništiti promene koje su načinjene nad originalnom scenom izlaza." QuickTransitions.DuplicateScene="Dupliraj scenu" @@ -553,3 +559,4 @@ SceneItemHide="Sakrij '%1'" 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 e5a74be..f79e644 100644 --- a/UI/data/locale/hu-HU.ini +++ b/UI/data/locale/hu-HU.ini @@ -31,6 +31,9 @@ DroppedFrames="Ejtett képkockák: %1 (%2 %)" PreviewProjector="Teljes képernyős projektor (Előnézet)" SceneProjector="Teljes képernyős projektor (Jelenet)" SourceProjector="Teljes képernyős projektor (Forrás)" +PreviewWindow="Ablakos projektor (Előnézet)" +SceneWindow="Ablakos projektor (Jelenet)" +SourceWindow="Ablakos projektor (Forrás)" Clear="Törlés" Revert="Visszavonás" Show="Mutat" @@ -56,6 +59,92 @@ Deprecated="Elavult" ReplayBuffer="Visszajátszás puffer" Import="Importálás" Export="Exportálás" +Copy="Másolás" +Paste="Beillesztés" +PasteReference="Beillesztés (Referencia)" +PasteDuplicate="Beillesztés (Másolat)" +RemuxRecordings="Remux Felvételek" +Next="Következő" +Back="Vissza" + +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." +AlreadyRunning.LaunchAnyway="Indítás mégis" + +Copy.Filters="Szűrők másolása" +Paste.Filters="Szűrők beillesztése" + +BandwidthTest.Region="Régió" +BandwidthTest.Region.US="Egyesült Államok" +BandwidthTest.Region.EU="Európa" +BandwidthTest.Region.Asia="Ázsia" +BandwidthTest.Region.Other="Egyéb" + +Basic.FirstStartup.RunWizard="Kívánja futtatni az automatikus konfiguráció varázslót? Természetesen manuálisan is konfigurálhatja a beállításokat a Beállítások gombra kattintva a fő ablakban." +Basic.FirstStartup.RunWizard.BetaWarning="(Megjegyzés: az automatikus konfiguráció varázsló jelenleg béta fázisban van)" +Basic.FirstStartup.RunWizard.NoClicked="Ha meggondolja magát, bármikor futtathatja az automatikus konfiguráció varázslót az eszközök menüből." + +Basic.AutoConfig="Automatikus konfiguráció varázsló" +Basic.AutoConfig.Beta="Automatikus konfiguráció varázsló (Béta)" +Basic.AutoConfig.ApplySettings="Beállítások alkalmazása" +Basic.AutoConfig.StartPage="Használati tudnivalók" +Basic.AutoConfig.StartPage.SubTitle="Adja meg, hogy mire szeretné használni a programot" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Streamre optimalizálás, a felvétel másodlagos" +Basic.AutoConfig.StartPage.PrioritizeRecording="Felvételre optimalizálás, nem fogok streamelni" +Basic.AutoConfig.VideoPage="Videó beállítások" +Basic.AutoConfig.VideoPage.SubTitle="Adja meg milyen videó beállításokat kíván használni" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Aktuális (%1x%2) használata" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Megjelenítő %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Aktuális (%1) használata" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 vagy 30, de inkább 60, ha lehetséges" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 vagy 30, de inkább nagyobb felbontás" +Basic.AutoConfig.VideoPage.CanvasExplanation="Megjegyzés: A vászon (alap) felbontása nem feltétlenül ugyanaz, mint a stream vagy felvétel felbontása. A tényleges stream/felvétel felbontása esetlegesen lefele lesz skálázva az erőforrások leterheltsége vagy a bitsebesség követelmények elérése érdekében." +Basic.AutoConfig.StreamPage="Stream információ" +Basic.AutoConfig.StreamPage.SubTitle="Adja meg a stream információit" +Basic.AutoConfig.StreamPage.Service="Szolgáltató" +Basic.AutoConfig.StreamPage.Service.ShowAll="Összes megjelenítése..." +Basic.AutoConfig.StreamPage.Server="Szerver" +Basic.AutoConfig.StreamPage.StreamKey="Stream kulcs" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Bitsebesség megbecsülése sávszélesség teszttel (néhány percig is eltarthat)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Hardveres kódolás előnyben részesítése" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="A Hardveres kódolás megszünteti a CPU erőforrás tetemes részét, viszont sokkal több bitsebesség szükséges az azonos szintű képminőség eléréséhez." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Stream figyelmeztetés" +Basic.AutoConfig.StreamPage.StreamWarning.Text="A sávszélesség teszt véletlenszerűsített videoadatokat fog közvetíteni hang nélkül a csatornájára. Ha tudja, ideiglenesen kapcsolja ki a streamek rögzítését és állítsa a streamet privátra amíg a teszt befejeződik. Folytatja?" +Basic.AutoConfig.TestPage="Végeredmény" +Basic.AutoConfig.TestPage.SubTitle.Testing="A program most különböző tesztekkel megbecsüli a legideálisabb beállításokat" +Basic.AutoConfig.TestPage.SubTitle.Complete="A teszt befejeződött" +Basic.AutoConfig.TestPage.TestingBandwidth="Sávszélesség teszt végrehajtása, ez egy pár percig eltarthat..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Csatlakozás szerverhez: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Nem sikerült kapcsolódni egy szerverhez sem, ellenőrizze az internetkapcsolatát és próbálja újra." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Sávszélesség vizsgálat: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Stream kódoló tesztelése, ez néhány percig is eltarthat..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Felvétel kódoló tesztelése, ez néhány percig is eltarthat..." +Basic.AutoConfig.TestPage.TestingRes="Felbontás tesztelése, ez néhány percig is eltarthat..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Kódoló indítása sikertelen" +Basic.AutoConfig.TestPage.TestingRes.Resolution="%1x%2 %3 FPS tesztelése..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Stream kódoló" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Felvétel kódoló" +Basic.AutoConfig.TestPage.Result.Header="A program megállapította, hogy ezek a becsült beállítások a legideálisabbak az ön számára:" +Basic.AutoConfig.TestPage.Result.Footer="A beállítások használatához kattintson a Beállítások alkalmazása gombra. A varázsló újrakonfigurálásához és az újrapróbálkozáshoz, kattintson a Vissza gombra. A manuális beállításokhoz kattintson a Mégse gombra és nyissa meg a Beállításokat." + +Basic.Stats="Statisztika" +Basic.Stats.CPUUsage="Processzorhasználat" +Basic.Stats.HDDSpaceAvailable="Szabad merevlemez terület" +Basic.Stats.MemoryUsage="Memóriahasználat" +Basic.Stats.AverageTimeToRender="Átlagos képkocka feldolgozási idő" +Basic.Stats.SkippedFrames="Kihagyott képkockák kódolási lag miatt" +Basic.Stats.MissedFrames="Renderlag miatt nem fogadott képkockák" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Felvétel" +Basic.Stats.Status="Állapot" +Basic.Stats.Status.Recording="Rögzítés" +Basic.Stats.Status.Live="ÉLŐ" +Basic.Stats.Status.Reconnecting="Újracsatlakozás" +Basic.Stats.Status.Inactive="Inaktív" +Basic.Stats.DroppedFrames="Képkockák ejtve (Hálózat)" +Basic.Stats.MegabytesSent="Összes kimeneti adat" +Basic.Stats.Bitrate="Bitsebesség" Updater.Title="Új frissítés elérhető" Updater.Text="Új frissítés elérhető:" @@ -375,6 +464,7 @@ Basic.Settings.General="Általános" Basic.Settings.General.Theme="Téma" Basic.Settings.General.Language="Nyelv" Basic.Settings.General.EnableAutoUpdates="Indításkor a frissítések automatikus ellenőrzése" +Basic.Settings.General.OpenStatsOnStartup="Statisztikai párbeszédpanel megnyitása indításkor" Basic.Settings.General.WarnBeforeStartingStream="Megerősítő párbeszédpanel megjelenítése stream indításakor" Basic.Settings.General.WarnBeforeStoppingStream="Megerősítő párbeszédpanel megjelenítése stream leállításakor" Basic.Settings.General.Projectors="Projektorok" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Részleges" Basic.Settings.Advanced.Video.ColorRange.Full="Teljes" Basic.Settings.Advanced.Audio.MonitoringDevice="Hangfigyelő eszköz" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Alapértelmezett" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Windows hangerőcsökkentés lekapcsolása" Basic.Settings.Advanced.StreamDelay="Stream késleltetés" Basic.Settings.Advanced.StreamDelay.Duration="Időtartam (másodperc)" Basic.Settings.Advanced.StreamDelay.Preserve="Töréspont megőrzése (Késleltetés növeléssel) újrakapcsolódás esetén" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Ki kell jelölnie legalább egy sávot!" OutputWarnings.MultiTrackRecording="Figyelem: Bizonyos formátumok (mint az FLV) nem támogatják a több sávot felvételenként" OutputWarnings.MP4Recording="Figyelem: Az MP4-be mentett állományok javíthatatlanok, ha a fájl nem kerül lezárásra (pl: BSOD vagy áramkimaradás esetén, stb.). Ha mindenképpen több hangsávval kíván felvételt készíteni, akkor használja az MKV állományt és remuxolja a felvételt MP4-be, miután elkészült. (Fájl->Felvételek remuxolása)" +FinalScene.Title="Jelenet törlése" +FinalScene.Text="Legalább egy jelenetnek lennie kell." + diff --git a/UI/data/locale/it-IT.ini b/UI/data/locale/it-IT.ini index c35ed2b..9753f92 100644 --- a/UI/data/locale/it-IT.ini +++ b/UI/data/locale/it-IT.ini @@ -56,8 +56,33 @@ Deprecated="Deprecato" ReplayBuffer="Buffer di replay" Import="Importa" Export="Esporta" +Copy="Copia" +Paste="Incolla" +PasteReference="Incolla (riferimento)" +PasteDuplicate="Incolla (duplicato)" +RemuxRecordings="Remux registrazioni" +Copy.Filters="Filtri di copia" +Paste.Filters="Filtri di incollamento" + + + + + +Updater.Title="Nuovo aggiornamento disponibile" +Updater.Text="C'è un nuovo aggiornamento disponibile:" +Updater.UpdateNow="Aggiorna ora" +Updater.RemindMeLater="Ricordamelo più tardi" +Updater.Skip="Salta versione" +Updater.Running.Title="Programma attualmente attivo" +Updater.Running.Text="Uscite sono attualmente attive, chiudi qualsiasi uscita attiva prima di tentare l'aggiornamento" +Updater.NoUpdatesAvailable.Title="Nessun aggiornamento disponibile" +Updater.NoUpdatesAvailable.Text="Nessun aggiornamento attualmente disponibile" +Updater.FailedToLaunch="Avvio dello strumento di aggiornamento non riuscito" +Updater.GameCaptureActive.Title="Cattura gioco attivo" +Updater.GameCaptureActive.Text="L'hook della libreria di cattura gioco è attualmente in uso. Chiudi qualsiasi gioco/programma oggetto di acquisizione (o riavvia Windows) e prova ancora." + QuickTransitions.SwapScenes="Scambia scene di anteprima/uscita dopo la transizione" QuickTransitions.SwapScenesTT="Scambia le scene di uscita con quella in anteprima dopo la transizione (ammesso che la scena in uscita originale ci sia ancora).\nQuesto non modificherà eventuali cambiamenti apportati alla scena di uscita originale." QuickTransitions.DuplicateScene="Duplica scena" @@ -98,6 +123,10 @@ ConfirmRemove.Title="Conferma la rimozione" ConfirmRemove.Text="Sei sicuro di voler rimuovere '$1'?" ConfirmRemove.TextMultiple="Sei sicuro di volere rimuovere %1 elementi?" +Output.StartStreamFailed="Avvio della trasmissione non riuscito" +Output.StartRecordingFailed="Avvio della registrazione non riuscito" +Output.StartReplayFailed="Avvio del buffer di riproduzione non riuscito" +Output.StartFailedGeneric="L'avvio dell'uscita non è riuscito. Controlla il log per i dettagli.\n\nNote: se utilizzi i codificatori NVENC o AMD, assicurati che i driver video siano aggiornati." Output.ConnectFail.Title="Impossibile connettersi" Output.ConnectFail.BadPath="Percorso o URL di connessione non valido. Controlla le tue impostazioni per confermare che siano valide." @@ -175,6 +204,10 @@ Deinterlacing.Yadif2x="Yadif 2x" Deinterlacing.TopFieldFirst="Priorità livello superiore" Deinterlacing.BottomFieldFirst="Priorità livello inferiore" +VolControl.SliderUnmuted="Cursore del volume per '%1': %2" +VolControl.SliderMuted="Cursore del volume per '%1': %2 (attualmente silenziato)" +VolControl.Mute="Silenzia '%1'" +VolControl.Properties="Proprietà di '%1'" Basic.Main.AddSceneDlg.Title="Aggiungi scena" Basic.Main.AddSceneDlg.Text="Inserisci il nome della scena" @@ -354,8 +387,10 @@ Basic.Settings.Confirm="Hai dei cambiamenti non salvati. Vuoi salvarli?" Basic.Settings.General="Generali" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Lingua" +Basic.Settings.General.EnableAutoUpdates="Controlla aggiornamenti automaticamente all'avvio" Basic.Settings.General.WarnBeforeStartingStream="Chiedi conferma quando si avvia una diretta" Basic.Settings.General.WarnBeforeStoppingStream="Chiedi conferma quando si termina una diretta" +Basic.Settings.General.Projectors="Proiettori" Basic.Settings.General.HideProjectorCursor="Nascondi cursore sopra proiettori" Basic.Settings.General.ProjectorAlwaysOnTop="Rendono i proiettori sempre in primo piano" Basic.Settings.General.Snapping="Allineamento Snapping Source" @@ -365,8 +400,12 @@ Basic.Settings.General.SourceSnapping="Snap sources ad altre sources" Basic.Settings.General.SnapDistance="Sensibilità Snap" Basic.Settings.General.RecordWhenStreaming="Registra automaticamente quando si è in diretta" Basic.Settings.General.KeepRecordingWhenStreamStops="Continua a registrare quando la diretta s'interrompe" +Basic.Settings.General.ReplayBufferWhileStreaming="Avvia automaticamente il buffer di riproduzione durante la trasmissione" +Basic.Settings.General.KeepReplayBufferStreamStops="Mantieni il buffer di riproduzione attiva quando la trasmissione si interrompe" +Basic.Settings.General.SysTray="Vassoio di sistema" Basic.Settings.General.SysTrayWhenStarted="Minimizza all'area di notifica all'avvio" Basic.Settings.General.SystemTrayHideMinimize="Minimizza sempre nel vassoio di sistema invece che nella barra delle applicazioni" +Basic.Settings.General.SaveProjectors="Salva i proiettori all'uscita" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Tipo di stream" @@ -455,24 +494,26 @@ Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Impostazioni codifica video ( Basic.Settings.Output.Adv.FFmpeg.AEncoder="Encoder Audio" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Impostazioni codifica audio (se presente)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Impostazioni Muxer (se possibile)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Intervallo fotogrammi chiave (fotogrammi)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Mostra tutti i codec (anche se potenzialmente incompatibili)" 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 Anno, quattro cifre\n%YY Anno, ultime due cifre (00-99)\n%MM Mese come numero decimale (01-12)\n%DD Giorno del mese, zero-padded (01-31)\n%hh Ore in formato 24 ore (00-23)\n%mm Minuto (00-59)\n%ss Secondi (00-61)\n%% Un % segno\n%a Giorno della settimana abbreviato\n%A Un nome del giorno della settimana intero\n%b Nome del mese abbreviato\n%B Nome del mese intero\n%d Giorno del mese, zero-padded (01-31)\n%H Ore in formato 24 ore (00-23)\n%I Ore in formato 12 ore (01-12)\n%m Mese come numero decimale (01-12)\n%M Minuto (00-59)\n%p Designazione AM o PM\n%S Secondi (00-61)\n%y Anno, ultime due lettere (00-99)\n%Y Anno\n%z ISO 8601 offset da UTC o fuso orario\n Nome o abbreviazione\n%Z Nome o abbreviazione del fuso orario\n" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Adattatore video:" -Basic.Settings.Video.BaseResolution="Risoluzione base (Canvas):" -Basic.Settings.Video.ScaledResolution="Risoluzione output (scaled):" -Basic.Settings.Video.DownscaleFilter="Filtro di rimpicciolimento:" +Basic.Settings.Video.Adapter="Adattatore video" +Basic.Settings.Video.BaseResolution="Risoluzione base (Canvas)" +Basic.Settings.Video.ScaledResolution="Risoluzione output (scalata)" +Basic.Settings.Video.DownscaleFilter="Filtro di rimpicciolimento" Basic.Settings.Video.DisableAeroWindows="Disabilita Aero (solo Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Valori FPS comuni" Basic.Settings.Video.FPSInteger="Valore FPS con numero intero" Basic.Settings.Video.FPSFraction="Valore FPS con numero frazionario" -Basic.Settings.Video.Numerator="Numeratore:" -Basic.Settings.Video.Denominator="Denominatore:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Numeratore" +Basic.Settings.Video.Denominator="Denominatore" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Valore di risoluzione invalido. Deve essere [larghezza]x[altezza] (es. 1920x1080)" Basic.Settings.Video.CurrentlyActive="L'output video è attualmente attivo. Spegni qualunque output per cambiare le impostazioni." Basic.Settings.Video.DisableAero="Disabilita Aero" @@ -516,6 +557,8 @@ Basic.Settings.Advanced.StreamDelay.Preserve="Preserva il punto di taglio (aumen Basic.Settings.Advanced.StreamDelay.MemoryUsage="Utilizzo di memoria stimato: %1 MB" 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.AdvAudio="Proprietà audio avanzate" Basic.AdvAudio.Name="Nome" @@ -585,4 +628,8 @@ SceneItemHide="Nascondi '%1'" OutputWarnings.NoTracksSelected="Devi selezionare almeno una traccia" OutputWarnings.MultiTrackRecording="Attenzione: Alcuni formati (come FLV) non supportano più di una traccia per registrazione" +OutputWarnings.MP4Recording="Avviso: le registrazioni salvate in MP4 non saranno recuperabili se il file non può essere finalizzato (ad es. a seguito di BSOD, perdite di potenza, ecc.). Se desideri registrare più tracce audio prendi in considerazione l'utilizzo di MKV e esegui il remux della registrazione in mp4 dopo che è finito (File-> Remux registrazioni)" + +FinalScene.Title="Elimina scena" +FinalScene.Text="Deve esserci almeno una scena." diff --git a/UI/data/locale/ja-JP.ini b/UI/data/locale/ja-JP.ini index 4225e00..dce421a 100644 --- a/UI/data/locale/ja-JP.ini +++ b/UI/data/locale/ja-JP.ini @@ -31,6 +31,9 @@ DroppedFrames="ドロップしたフレーム %1 (%2%)" PreviewProjector="全画面プロジェクター (プレビュー)" SceneProjector="全画面プロジェクター (シーン)" SourceProjector="全画面プロジェクター (ソース)" +PreviewWindow="ウィンドウ プロジェクター (プレビュー)" +SceneWindow="ウィンドウ プロジェクター (シーン)" +SourceWindow="ウィンドウ プロジェクター (ソース)" Clear="クリア" Revert="元に戻す" Show="表示" @@ -56,6 +59,92 @@ Deprecated="非推奨" ReplayBuffer="リプレイバッファー" Import="インポート" Export="エクスポート" +Copy="コピー" +Paste="貼り付け" +PasteReference="貼り付け (参照)" +PasteDuplicate="貼り付け (複製)" +RemuxRecordings="録画の再多重化" +Next="次へ" +Back="戻る" + +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="自動構成ウィザード (ベータ版)" +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="ハードウェアエンコードは CPU 使用率がほとんどなくなりますが、同レベルの品質を得るためにはより多くのビットレートが必要になります。" +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="HDDの空き容量" +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="利用可能な更新があります:" @@ -119,7 +208,7 @@ Output.ConnectFail.Title="接続失敗" Output.ConnectFail.BadPath="パスかURLが無効です。再確認して下さい。" Output.ConnectFail.ConnectFailed="サーバーへの接続に失敗しました" Output.ConnectFail.InvalidStream="指定したチャンネルまたはストリームキーにアクセスできませんでした。ストリームキーを再確認してください。 それが正しい場合は、サーバーへの接続に問題があります。" -Output.ConnectFail.Error="サーバー接続時に予期しないエラーが発生しました。ログファイルを確認して下さい。" +Output.ConnectFail.Error="サーバー接続時に予期しないエラーが発生しました。ログファイルを確認してください。" Output.ConnectFail.Disconnected="サーバーから切断されました。" Output.RecordFail.Title="録画を開始できませんでした" @@ -144,7 +233,7 @@ LicenseAgreement.ClickIAgreeToContinue="契約の条項に同意する場合、 LicenseAgreement.IAgree="同意する" LicenseAgreement.Exit="終了" -Remux.SourceFile="OBS録画" +Remux.SourceFile="OBS 録画" Remux.TargetFile="対象ファイル" Remux.Remux="再多重化" Remux.OBSRecording="OBS 録画" @@ -375,11 +464,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.ProjectorAlwaysOnTop="プロジェクターを常に手前に表示させる" Basic.Settings.General.Snapping="ソース配置のスナップ" Basic.Settings.General.ScreenSnapping="画面の端にソースをスナップする" Basic.Settings.General.CenterSnapping="水平方向および垂直方向の中心にソースをスナップする" @@ -445,7 +535,7 @@ Basic.Settings.Output.CustomMuxerSettings="カスタムマルチプレクサー Basic.Settings.Output.NoSpaceFileName="スペースなしのファイル名を生成" Basic.Settings.Output.Adv.Rescale="出力をリスケールする" -Basic.Settings.Output.Adv.AudioTrack="オーディオ トラック" +Basic.Settings.Output.Adv.AudioTrack="音声トラック" Basic.Settings.Output.Adv.Streaming="配信" Basic.Settings.Output.Adv.ApplyServiceSettings="ストリーミングサービスのエンコーダ設定を適用する" Basic.Settings.Output.Adv.Audio.Track1="トラック 1" @@ -489,18 +579,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY 年, 4桁\n%YY 年, 下2桁 (00-99)\n%MM 月 数値 (01-12)\n%DD 日, 0埋め (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 日, 0埋め (01-31)\n%H 時 24時間形式 (00-23)\n%I 時 12時間形式 (01-12)\n%m 月 数値 (01-12)\n%M 分 (00-59)\n%p 午前または午後の指定\n%S 秒 (00-61)\n%y 年, 下2桁 (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.Adapter="ビデオアダプター" +Basic.Settings.Video.BaseResolution="基本 (キャンバス) 解像度" +Basic.Settings.Video.ScaledResolution="出力 (スケーリング) 解像度" +Basic.Settings.Video.DownscaleFilter="縮小フィルタ" Basic.Settings.Video.DisableAeroWindows="エアロ無効 (Windows のみ)" -Basic.Settings.Video.FPS="FPS:" +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.Numerator="分子" +Basic.Settings.Video.Denominator="分母" +Basic.Settings.Video.Renderer="レンダラー" Basic.Settings.Video.InvalidResolution="解像度の値が不正です。[幅]x[高さ] (例 1920x1080)にしてください" Basic.Settings.Video.CurrentlyActive="映像出力中です。映像設定を変更するには出力を停止してください。" Basic.Settings.Video.DisableAero="Aeroを無効にする" @@ -538,6 +628,7 @@ 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="再接続時にカットオフポイントを保持する (増加遅延)" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="少なくとも 1 つのトラックを選択 OutputWarnings.MultiTrackRecording="警告: 特定のフォーマット (FLVなど) は1つの録画で複数のトラックをサポートしていません" OutputWarnings.MP4Recording="警告: ファイルをファイナライズ出来ない場合 (例えば、BSOD、電力損失などの結果として) はMP4に保存された録画は回復不能になります。 複数の音声トラックを録画する場合はMKVの利用を検討して録画の終了後にMP4に再多重化してください。(ファイル -> 録画の再多重化)" +FinalScene.Title="シーンを削除する" +FinalScene.Text="1つ以上のシーンが必要です。" + diff --git a/UI/data/locale/ko-KR.ini b/UI/data/locale/ko-KR.ini index 9c62f0e..badd892 100644 --- a/UI/data/locale/ko-KR.ini +++ b/UI/data/locale/ko-KR.ini @@ -31,6 +31,9 @@ DroppedFrames="손실된 프레임 %1 (%2%)" PreviewProjector="전체화면 프로젝터 (미리보기)" SceneProjector="전체화면 프로젝터 (장면)" SourceProjector="전체화면 프로젝터 (소스)" +PreviewWindow="창 프로젝터 (미리보기)" +SceneWindow="창 프로젝터 (장면)" +SourceWindow="창 프로젝터 (소스)" Clear="단축키 해제" Revert="되돌리기" Show="보이기" @@ -56,6 +59,92 @@ Deprecated="사용하지 않음" ReplayBuffer="리플레이 버퍼" Import="가져오기" Export="내보내기" +Copy="복사" +Paste="붙여넣기" +PasteReference="붙여넣기 (참조)" +PasteDuplicate="붙여넣기 (중복)" +RemuxRecordings="녹화본 재다중화" +Next="다음" +Back="이전" + +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="구성 마법사 (베타)" +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="참고: 캔버스 (기본) 해상도는 방송 혹은 녹화하려는 해상도와 반드시 같을 필요는 없습니다. 실제 방송/녹화 해상도는 Pc 사양을 낮추거나 비트레이트 제한에 맞추기 위해 이 캔버스 해상도를 기준으로 축소할 수 있습니다." +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="하드웨어 인코딩은 CPU 사용량을 대폭 줄일 수 있지만, 소프트웨어 인코덩과 동등한 품질을 달성하려면 더 많은 비트레이트가 필요합니다." +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="새 판올림이 준비되었습니다:" @@ -375,6 +464,7 @@ 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="프로젝터" @@ -489,18 +579,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY 연도, 네 자리\n%YY 연도, 마지막 두 자리 (00-99)\n%MM 월 십진법 (01-12)\n%DD 일, 선행 0 포함 (01-31)\n%hh 시 24 시간 형식 (00-23)\n%mm Minute (00-59)\n%ss 초 (00-61)\n%% A % sign\n%a 요일 축약\n%A 요일\n%b 월 축약\n%B 월\n%d 일, 선행 0 포함 (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 UTC에서 ISO 8601 기준만큼 조정 혹은 시간대\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.Adapter="비디오 어댑터" +Basic.Settings.Video.BaseResolution="기본 (캔버스) 해상도" +Basic.Settings.Video.ScaledResolution="출력 (조정된) 해상도" +Basic.Settings.Video.DownscaleFilter="축소 필터" Basic.Settings.Video.DisableAeroWindows="에어로 비활성화 (윈도우 전용)" -Basic.Settings.Video.FPS="초당 프레임수(FPS):" +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.Numerator="분자" +Basic.Settings.Video.Denominator="분모" +Basic.Settings.Video.Renderer="렌더러" Basic.Settings.Video.InvalidResolution="잘못된 값이 입력되었습니다. 반드시 [폭]x[높이]의 형식이어야 합니다. (예 1920x1080)" Basic.Settings.Video.CurrentlyActive="비디오가 현재 출력되고 있습니다. 비디오 설정을 변경하려면 관련 작업을 중단해야 합니다." Basic.Settings.Video.DisableAero="에어로 테마 끄기" @@ -538,6 +628,7 @@ 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="재접속 시 잘려나간 지점 보관 (지연시간 증가)" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="최소 하나의 트랙을 선택해야 합니 OutputWarnings.MultiTrackRecording="경고: 일부 형식(예를 들어 FLV)은 녹화 하나에 여러 개의 트랙을 지원하지 않습니다" OutputWarnings.MP4Recording="경고: MP4로 녹화를 하면 파일이 마무리가 되지 않았을 때 (예를 들어 컴퓨터가 급작스럽게 꺼지거나 블루 스크린 오류가 일어나는 경우) 복구할 수 없습니다. 여러 개의 오디오 트랙을 녹음하고 싶다면 MKV 확장자로 녹화 한 뒤 재다중화 작업을 통해 mp4로 전환하십시오. (파일->재다중화 녹화)" +FinalScene.Title="장면 삭제" +FinalScene.Text="적어도 하나의 장면은 존재해야 합니다." + diff --git a/UI/data/locale/lt-LT.ini b/UI/data/locale/lt-LT.ini index ad0fa96..b9282eb 100644 --- a/UI/data/locale/lt-LT.ini +++ b/UI/data/locale/lt-LT.ini @@ -50,6 +50,12 @@ Top="Iš viršaus" Bottom="Iš apačios" + + + + + + QuickTransitions.SwapScenes="Sukeisti Peržiūros/Išvesties scenas po Perėjimo" QuickTransitions.SwapScenesTT="Sukeičia peržiūros ir išvesties scenas po perėjimo įvykdymo (jei originali išvesties scena vis dar egzistuoja).\nTai neatšauks jokių pakeitimų kurie galima buvo atlikti originalioje išvesties scenoje." QuickTransitions.DuplicateScene="Dubliuoti Sceną" @@ -279,5 +285,6 @@ Basic.MainMenu.Edit.Order.MoveToBottom="Perkelti į apačią" + diff --git a/UI/data/locale/ms-MY.ini b/UI/data/locale/ms-MY.ini index 82da14e..4cfd606 100644 --- a/UI/data/locale/ms-MY.ini +++ b/UI/data/locale/ms-MY.ini @@ -54,6 +54,12 @@ Minutes="Minit" Seconds="Saat" + + + + + + QuickTransitions.SwapScenes="Tukar Pratonton/Pengeluaran Adegan Selepas Peralihan" QuickTransitions.SwapScenesTT="Menukarkan pratonton dan pengeluaran adegan-adegan selepas peralihan(jika pengeluaran adegan mash wujud).\nIni tidak akan mengundurkan sebarang perubahan yang mungkin telah dilakukan pada pengeluaran adegan yang asal." QuickTransitions.DuplicateScene="Klonkan Adegan" @@ -419,3 +425,4 @@ Push-to-talk="Tekan-untuk-cakap" + diff --git a/UI/data/locale/nb-NO.ini b/UI/data/locale/nb-NO.ini index c60250b..a4fdc59 100644 --- a/UI/data/locale/nb-NO.ini +++ b/UI/data/locale/nb-NO.ini @@ -56,6 +56,20 @@ Deprecated="Foreldet" ReplayBuffer="Omspill Buffer" Import="Importer" Export="Eksporter" +Copy="Kopier" +Paste="Lim inn" +PasteReference="Lim inn (Referanse)" +PasteDuplicate="Lim inn (Duplikat)" +RemuxRecordings="Remux Opptak" +Next="Neste" +Back="Tilbake" + +AlreadyRunning.Title="OBS kjører allerede" + + + + + Updater.Title="Ny oppdatering tilgjengelig" Updater.Text="Det finnes en ny oppdatering:" @@ -63,8 +77,11 @@ Updater.UpdateNow="Oppdater nå" Updater.RemindMeLater="Påminn meg senere" Updater.Skip="Hopp over versjon" Updater.Running.Title="Programmet er aktiv" +Updater.Running.Text="Utganger er aktive, deaktiver alle aktive utganger før du forsøker å oppdatere" Updater.NoUpdatesAvailable.Title="Ingen oppdateringer er tilgjengelig" Updater.NoUpdatesAvailable.Text="Ingen oppdateringer er tilgjengelig" +Updater.FailedToLaunch="Kunne ikke starte oppdaterer" +Updater.GameCaptureActive.Title="Spillopptak aktivt" QuickTransitions.SwapScenes="Bytt forhåndsvisnings-/utgangsscener etter overgang" QuickTransitions.SwapScenesTT="Bytter forhåndsvisnings- og utgangsscenen etter overgang, hvis den originale utgangsscenen fortsatt eksisterer.\nDette vil ikke tilbakestille endringer på den originale utgangsscenen." @@ -108,6 +125,7 @@ ConfirmRemove.TextMultiple="Er du sikker du ønsker å fjerne %1 filer?" Output.StartStreamFailed="Kan ikke starte streaming" Output.StartRecordingFailed="Kan ikke starte innspillingen" +Output.StartReplayFailed="Kunne ikke starte replay bufferen" Output.ConnectFail.Title="Tilkobling misklytes" Output.ConnectFail.BadPath="Ugyldig filbane eller tilkoblings-URL. Vennligst bekreft at instillingene dine er riktige." @@ -185,6 +203,10 @@ Deinterlacing.Yadif2x="Dobbelyadif" Deinterlacing.TopFieldFirst="Øverste felt først" Deinterlacing.BottomFieldFirst="Nederste felt først" +VolControl.SliderUnmuted="Volumskyveknappen for '%1': %2" +VolControl.SliderMuted="Volumskyveknappen for '%1': %2 (dempet)" +VolControl.Mute="Demp '%1'" +VolControl.Properties="Egenskaper for '%1'" Basic.Main.AddSceneDlg.Title="Ny Scene" Basic.Main.AddSceneDlg.Text="Vennligst gi et navn til scenen." @@ -309,9 +331,12 @@ Basic.MainMenu.Edit.RedoAction="&Gjør om $1" Basic.MainMenu.Edit.LockPreview="Lås Forhåndsvisning" Basic.MainMenu.Edit.Scale="Forhåndsvisning & Skalering" Basic.MainMenu.Edit.Scale.Window="Tilpass til vindu" +Basic.MainMenu.Edit.Scale.Canvas="Lerret (%1x%2)" +Basic.MainMenu.Edit.Scale.Output="Utgang (%1x%2)" Basic.MainMenu.Edit.Transform="&Transformer" Basic.MainMenu.Edit.Transform.EditTransform="&Redigér transformering..." Basic.MainMenu.Edit.Transform.CopyTransform="Kopiere transformering" +Basic.MainMenu.Edit.Transform.PasteTransform="Lim inn transformering" Basic.MainMenu.Edit.Transform.ResetTransform="&Angre transformering" Basic.MainMenu.Edit.Transform.Rotate90CW="Rotér 90 grader med klokka" Basic.MainMenu.Edit.Transform.Rotate90CCW="Rotér 90 grader mot klokka" @@ -330,6 +355,7 @@ Basic.MainMenu.Edit.AdvAudio="&Avanserte lydinstillinger" Basic.MainMenu.View="&Vis" Basic.MainMenu.View.Toolbars="%Verktøylinje" +Basic.MainMenu.View.Toolbars.Listboxes="&Listebokser" Basic.MainMenu.View.SceneTransitions="Sceneoverganger" Basic.MainMenu.View.StatusBar="Statuslinje" @@ -360,10 +386,12 @@ Basic.Settings.Confirm="Du har endringer som ikke er lagret. Vil du lagre?" Basic.Settings.General="Generelt" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Språk" +Basic.Settings.General.EnableAutoUpdates="Automatisk se etter oppdateringer ved oppstart" Basic.Settings.General.WarnBeforeStartingStream="Vis bekreftelsesdialogboks når du starter strømming" Basic.Settings.General.WarnBeforeStoppingStream="Vis bekreftelsesdialogboks når stanser strømming" Basic.Settings.General.Projectors="Projektorer" Basic.Settings.General.HideProjectorCursor="Skjul musepekeren over projektorer" +Basic.Settings.General.ProjectorAlwaysOnTop="Alltid vis projektorer øverst" Basic.Settings.General.Snapping="Festing ved kildejustering" Basic.Settings.General.ScreenSnapping="Fest kilder til kanten av skjermen" Basic.Settings.General.CenterSnapping="Fest kilder til vannrett og loddrett midtpunkt" @@ -371,6 +399,8 @@ Basic.Settings.General.SourceSnapping="Fest kilder til andre kilder" Basic.Settings.General.SnapDistance="Festingfølsomhet" Basic.Settings.General.RecordWhenStreaming="Spill inn automatisk ved strømming" Basic.Settings.General.KeepRecordingWhenStreamStops="Fortsett innspilling etter strømming" +Basic.Settings.General.ReplayBufferWhileStreaming="Automatisk start replay bufferen når du strømmer" +Basic.Settings.General.KeepReplayBufferStreamStops="Hold replay bufferen når du stopper strømmen" Basic.Settings.General.SysTrayWhenStarted="Minimer til systemstatusfelt ved oppstart" Basic.Settings.Stream="Strøm" @@ -459,6 +489,7 @@ Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Videokoderinstillinger (om no Basic.Settings.Output.Adv.FFmpeg.AEncoder="Lydkoder" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Lydkoderinstillinger (om noen)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Mukserinstillinger (om noen)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Vis alle kodeker (selv om potensielt ikke-kompatibel)" 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" @@ -470,13 +501,13 @@ Basic.Settings.Video.BaseResolution="Grunnoppløsning (lerret):" Basic.Settings.Video.ScaledResolution="Utgangsoppløsning (skalert):" Basic.Settings.Video.DownscaleFilter="Nedskaleringsfilter:" Basic.Settings.Video.DisableAeroWindows="Deaktiver Aero (kun Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="FPS samleverdi" Basic.Settings.Video.FPSInteger="FPS heltallverdi" Basic.Settings.Video.FPSFraction="FPS brøkverdi" -Basic.Settings.Video.Numerator="Teller:" -Basic.Settings.Video.Denominator="Nevner:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Teller" +Basic.Settings.Video.Denominator="Nevner" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Ugyldig oppløsningsverdi. Må være [bredde]x[høyde] (f.eks. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Bildeutgang er aktiv. Vennligst stans alle utganger for å endre bildeinstillingene." Basic.Settings.Video.DisableAero="Skru av Aero" @@ -512,12 +543,14 @@ Basic.Settings.Advanced.Video.ColorSpace="YUV fargerom" Basic.Settings.Advanced.Video.ColorRange="YUV fargespekter" Basic.Settings.Advanced.Video.ColorRange.Partial="Delvis" Basic.Settings.Advanced.Video.ColorRange.Full="Full" +Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Standard" Basic.Settings.Advanced.StreamDelay="Strømforsinkelse" Basic.Settings.Advanced.StreamDelay.Duration="Varighet (sekunder)" Basic.Settings.Advanced.StreamDelay.Preserve="Bevar avkuttingspunktet (øk forsinkelse) ved tilbakekobling" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Anslått minnebruk: %1 MB" Basic.Settings.Advanced.Network="Nettverk" Basic.Settings.Advanced.Network.BindToIP="Bind til IP" +Basic.Settings.Advanced.Network.EnableNewSocketLoop="Aktiver ny nettverk kode" Basic.AdvAudio="Avanserte lydinstillinger" Basic.AdvAudio.Name="Navn" @@ -525,6 +558,10 @@ Basic.AdvAudio.Volume="Volum (%)" Basic.AdvAudio.Mono="Nedmiks til mono" Basic.AdvAudio.Panning="Panorering" Basic.AdvAudio.SyncOffset="Synkronerings forskyvning (ms)" +Basic.AdvAudio.Monitoring="Hør på kilde" +Basic.AdvAudio.Monitoring.None="Ikke hør kilde" +Basic.AdvAudio.Monitoring.MonitorOnly="Kun hør kilde (Ikke output kilde)" +Basic.AdvAudio.Monitoring.Both="Hør og output kilde" Basic.AdvAudio.AudioTracks="Spor" Basic.Settings.Hotkeys="Hurtigtaster" @@ -584,3 +621,4 @@ SceneItemHide="Gjem '%1'" OutputWarnings.NoTracksSelected="Du må velge minst ett spor" OutputWarnings.MultiTrackRecording="Advarsel: enkelte formater (som FLV) støtter ikke flere spor per opptak" + diff --git a/UI/data/locale/nl-NL.ini b/UI/data/locale/nl-NL.ini index 169d48c..f5e512f 100644 --- a/UI/data/locale/nl-NL.ini +++ b/UI/data/locale/nl-NL.ini @@ -5,7 +5,7 @@ Region="Nederland" OK="OK" Apply="Toepassen" Cancel="Annuleren" -Close="Afsluiten" +Close="Sluiten" Save="Opslaan" Discard="Verwerpen" Disable="Uitschakelen" @@ -20,7 +20,7 @@ Properties="Eigenschappen" MoveUp="Omhoog Schuiven" MoveDown="Omlaag Schuiven" Settings="Instellingen" -Display="Monitor" +Display="Beeldscherm" Name="Naam" Exit="Afsluiten" Mixer="Mixer" @@ -31,6 +31,9 @@ DroppedFrames="Gedropte Frames %1 (%2%)" PreviewProjector="Full-screen Projector (Preview)" SceneProjector="Full-screen Projector (Scène)" SourceProjector="Full-screen Projector (Bron)" +PreviewWindow="Projectorvenster (Preview)" +SceneWindow="Projectorvenster (Scène)" +SourceWindow="Projectorvenster (Bron)" Clear="Wissen" Revert="Herstellen" Show="Weergeven" @@ -56,6 +59,92 @@ Deprecated="Verouderd" ReplayBuffer="Replay Buffer" Import="Importeer" Export="Exporteer" +Copy="Kopiëren" +Paste="plakken" +PasteReference="Plakken (referentie)" +PasteDuplicate="Plakken (dupliceren)" +RemuxRecordings="Remux opnames" +Next="Volgende" +Back="Vorige" + +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." +AlreadyRunning.LaunchAnyway="Toch Starten" + +Copy.Filters="Kopieer Filters" +Paste.Filters="Plak filters" + +BandwidthTest.Region="Regio" +BandwidthTest.Region.US="Verenigde Staten" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Azië" +BandwidthTest.Region.Other="Overig" + +Basic.FirstStartup.RunWizard="Wil je de automatische configuratieassistent gebruiken? Je kan ook de instellingen handmatig aanpassen door op Instellingen te klikken in het hoofdscherm." +Basic.FirstStartup.RunWizard.BetaWarning="(Let op: de automatische configuratieassistent is momenteel in een beta-fase)" +Basic.FirstStartup.RunWizard.NoClicked="Mocht je van gedachten veranderen, dan kun je de automatische configuratieassistent opnieuw starten vanuit het Help menu." + +Basic.AutoConfig="Automatische configuratieassistent" +Basic.AutoConfig.Beta="Automatische configuratieassistent (Beta)" +Basic.AutoConfig.ApplySettings="Instellingen toepassen" +Basic.AutoConfig.StartPage="Gebruiksinformatie" +Basic.AutoConfig.StartPage.SubTitle="Waar wil je het programma voor gebruiken?" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimaliseren voor streamen, lokaal opnemen is minder belangrijk" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimaliseren voor lokaal opnemen, ik ga niet streamen" +Basic.AutoConfig.VideoPage="Video-instellingen" +Basic.AutoConfig.VideoPage.SubTitle="Geef aan welke gewenste video-instellingen je wil gebruiken" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Gebruik huidige (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Beeldscherm %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Gebruik huidige (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 of 30, maar geef de voorkeur aan 60 wanneer mogelijk" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 of 30, maar geef de voorkeur aan een hoge resolutie" +Basic.AutoConfig.VideoPage.CanvasExplanation="Let op: de basisresolutie (canvas) hoeft niet gelijk te zijn als de resolutie waarmee je streamt of opneemt. Je stream/opnameresolutie kan omlaag bijgeschaald worden vanaf de basisresolutie om de belasting op de computer of de benodigde bitrate te verlagen." +Basic.AutoConfig.StreamPage="Stream-informatie" +Basic.AutoConfig.StreamPage.SubTitle="Voer alstublieft uw stream-informatie in" +Basic.AutoConfig.StreamPage.Service="Dienst" +Basic.AutoConfig.StreamPage.Service.ShowAll="Toon alles..." +Basic.AutoConfig.StreamPage.Server="Server" +Basic.AutoConfig.StreamPage.StreamKey="Stream key" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Schat bitrate met bandbreedte test (kan een paar minuten duren)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Geef de voorkeur aan hardware-encoding" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardware-encoding elimineert het meeste CPU-gebruik, maar heeft wellicht een hogere bitrate nodig om hetzelfde kwaliteitsniveau te bereiken." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Streamwaarschuwing" +Basic.AutoConfig.StreamPage.StreamWarning.Text="De bandbreedte-test is bezig met het streamen van willekeurige videogegevens zonder audio naar uw kanaal. Indien mogelijk is het aanbevolen om tijdelijk het opslaan van video's van streams uit te schakelen en de stream privé te maken tot de test is voltooid. Doorgaan?" +Basic.AutoConfig.TestPage="Eindresultaat" +Basic.AutoConfig.TestPage.SubTitle.Testing="Het programma voert nu een reeks testen uit om de meest ideale instellingen in te schatten" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testen is afgerond" +Basic.AutoConfig.TestPage.TestingBandwidth="Bandbreedte-test aan het uitvoeren, dit kan enkele minuten duren..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Verbinden met: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Kon met geen enkele server verbinden, controleer je internetverbinding en probeer het opnieuw." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Bandbreedte testen voor: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Stream encoder testen, dit kan even duren..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Opname encoder testen, dit kan even duren..." +Basic.AutoConfig.TestPage.TestingRes="Beeldschermresoluties testen, dit kan enkele minuten duren..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Kon de encoder niet starten" +Basic.AutoConfig.TestPage.TestingRes.Resolution="%1x%2 %3 FPS testen..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streamingencoder" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Opname-encoder" +Basic.AutoConfig.TestPage.Result.Header="Het programma heeft vastgesteld dat deze geschatte instellingen het meest ideaal zijn voor jou:" +Basic.AutoConfig.TestPage.Result.Footer="Om deze instellingen te gebruiken, klik op Instellingen Toepassen. Om de configuratieassistent aan te passen, klik op Terug. Om de instellingen handmatig te bepalen, klik op Annuleren en open de instellingen." + +Basic.Stats="Statistieken" +Basic.Stats.CPUUsage="Processorgebruik" +Basic.Stats.HDDSpaceAvailable="Harde-schijfruimte beschikbaar" +Basic.Stats.MemoryUsage="Geheugengebruik" +Basic.Stats.AverageTimeToRender="Gemiddelde tijd om een frame te genereren" +Basic.Stats.SkippedFrames="Overgeslagen frames als gevolg van encodervertraging" +Basic.Stats.MissedFrames="Frames gemist als gevolg van rendervertraging" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Lokale opname" +Basic.Stats.Status="Status" +Basic.Stats.Status.Recording="Opname bezig" +Basic.Stats.Status.Live="LIVE" +Basic.Stats.Status.Reconnecting="Opnieuw verbinden" +Basic.Stats.Status.Inactive="Niet actief" +Basic.Stats.DroppedFrames="Gedropte Frames (Netwerk)" +Basic.Stats.MegabytesSent="Totale Gegevensuitvoer" +Basic.Stats.Bitrate="Bitrate" Updater.Title="Update beschikbaar" Updater.Text="Er is een update beschikbaar:" @@ -375,6 +464,7 @@ Basic.Settings.General="Algemeen" Basic.Settings.General.Theme="Thema" Basic.Settings.General.Language="Taal" Basic.Settings.General.EnableAutoUpdates="Automatisch controleren op updates tijdens het opstarten" +Basic.Settings.General.OpenStatsOnStartup="Open statistieken bij het opstarten" Basic.Settings.General.WarnBeforeStartingStream="Laat bevestigingsvenster zien bij het starten van streams" Basic.Settings.General.WarnBeforeStoppingStream="Laat bevestiginsvenster zien bij het stoppen van streams" Basic.Settings.General.Projectors="Projectoren" @@ -481,24 +571,26 @@ Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Video Encoderinstellingen (in Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audio Encoder" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Audio Encoderinstellingen (indien gewenst)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxerinstellingen (indien aanwezig)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Tijd tussen keyframes (frames)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Toon alle codecs (ongeacht compatibiliteit)" 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 Jaar, vier cijfers\n%YY Jaar, laatste twee cijfers (00-99)\n%MM Maand, cijfer (0-12)\n%DD Dag van de maand, nul aangevuld (01-31)\n%hh Uur in 24h formaat (00-23)\n%mm Minuut (00-59)\n%ss Seconde (00-61)\n%% Een % teken\n%a Dag van de week afgekort\n%A Dag van de week volledig\n%b Maand, afgekorte naam\n%B Maand, volledige naam\n%d Dag van de maand, nul aangevuld (01-31)\n%H Uur in 24h formaat (00-23)\n%I Uur in 12h formaat (01-12)\n%m Maand, cijfer (01-12)\n%M Minuut (00-59)\n%p AM of PM\n%S Second e(00-61)\n%y Jaar, laatste twee cijfers (00-99)\n%Y Jaar\n%z ISO 8601 afstand van UTC of tijdzone\n naam of afkorting\n%Z naam of afkorting van tijdzone\n" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Videoadapter:" -Basic.Settings.Video.BaseResolution="Basisresolutie (Canvas):" -Basic.Settings.Video.ScaledResolution="Uitvoerresolutie (Geschaald):" -Basic.Settings.Video.DownscaleFilter="Resolutieverlagingsfilter:" +Basic.Settings.Video.Adapter="Videoadapter" +Basic.Settings.Video.BaseResolution="Basisresolutie (Canvas)" +Basic.Settings.Video.ScaledResolution="Uitvoerresolutie (Geschaald)" +Basic.Settings.Video.DownscaleFilter="Resolutieverlagingsfilter" Basic.Settings.Video.DisableAeroWindows="Aero Uitschakelen (alleen Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Gebruikelijke FPS-waardes" Basic.Settings.Video.FPSInteger="Geheel-getal FPS-waarde" Basic.Settings.Video.FPSFraction="Breuk FPS-waarde" -Basic.Settings.Video.Numerator="Teller:" -Basic.Settings.Video.Denominator="Noemer:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Teller" +Basic.Settings.Video.Denominator="Noemer" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Ongeldige resolutiewaarde. Moet [breedte] x [hoogte] zijn (bijv. 1920 x 1080)" Basic.Settings.Video.CurrentlyActive="Video-uitvoer is momenteel actief. Schakel a.u.b. alle uitvoeren uit om video-instellingen aan te passen." Basic.Settings.Video.DisableAero="Aero uitschakelen" @@ -536,12 +628,15 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Partial" Basic.Settings.Advanced.Video.ColorRange.Full="Full" Basic.Settings.Advanced.Audio.MonitoringDevice="Audio monitoring apparaat" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Standaard" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Schakel Windows audio ducking uit" Basic.Settings.Advanced.StreamDelay="Streamvertraging" Basic.Settings.Advanced.StreamDelay.Duration="Duur (seconden)" Basic.Settings.Advanced.StreamDelay.Preserve="Hervat op het eindpunt (verhoog vertraging) bij opnieuw verbinden" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Geschat Geheugengebruik: %1 MB" 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.AdvAudio="Geavanceerde Audioinstellingen" Basic.AdvAudio.Name="Naam" @@ -611,4 +706,8 @@ SceneItemHide="Verberg '%1'" OutputWarnings.NoTracksSelected="Selecteer a.u.b. ten minste een track" OutputWarnings.MultiTrackRecording="Waarschuwing: Sommige formaten (zoals FLV) bieden geen ondersteuning voor meerdere tracks per opname" +OutputWarnings.MP4Recording="Waarschuwing: Opnames opgeslagen als MP4 zijn niet te herstellen als de opame niet correct afgerond kan worden (bijvoorbeeld door BSOD's, stroomuitval). Als je wil opnemen met meerdere audiotracks gebruik dan bij voorkeur MKV en remux de opname naar mp4 (Bestand -> Remux opnames)" + +FinalScene.Title="Verwijder scène" +FinalScene.Text="Er moet tenminste één scène zijn." diff --git a/UI/data/locale/pl-PL.ini b/UI/data/locale/pl-PL.ini index 79eb1ba..05233b0 100644 --- a/UI/data/locale/pl-PL.ini +++ b/UI/data/locale/pl-PL.ini @@ -31,6 +31,9 @@ DroppedFrames="Zgubione klatki %1 (%2%)" PreviewProjector="Wyświetlanie na pełnym ekranie (podgląd)" SceneProjector="Wyświetlanie na pełnym ekranie (scena)" SourceProjector="Wyświetlanie na pełnym ekranie (źródło)" +PreviewWindow="Podgląd w oknie (podgląd)" +SceneWindow="Podgląd w oknie (scena)" +SourceWindow="Podgląd w oknie (źródło)" Clear="Wyczyść" Revert="Przywróć" Show="Pokaż" @@ -56,6 +59,92 @@ Deprecated="Wycofywane" ReplayBuffer="Buffer replayu" Import="Importuj" Export="Eksportuj" +Copy="Kopiuj" +Paste="Wklej" +PasteReference="Wklej (odniesienie)" +PasteDuplicate="Wklej (duplikat)" +RemuxRecordings="Przepakuj nagrania" +Next="Dalej" +Back="Wstecz" + +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." +AlreadyRunning.LaunchAnyway="Uruchom mimo to" + +Copy.Filters="Kopiuj filtry" +Paste.Filters="Wklej filtry" + +BandwidthTest.Region="Region" +BandwidthTest.Region.US="Stany Zjednoczone" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Azja" +BandwidthTest.Region.Other="Inny" + +Basic.FirstStartup.RunWizard="Czy chcesz uruchomić kreatora konfiguracji automatycznej? Można również ręcznie skonfigurować ustawienia, klikając przycisk Ustawienia w oknie głównym." +Basic.FirstStartup.RunWizard.BetaWarning="(Uwaga: Kreator konfiguracji automatycznej jest obecnie w wersji beta)" +Basic.FirstStartup.RunWizard.NoClicked="Jeżeli zmienisz zdanie, można uruchomić kreatora konfiguracji automatycznej każdej chwili ponownie z menu Narzędzia." + +Basic.AutoConfig="Kreator konfiguracji automatycznej" +Basic.AutoConfig.Beta="Kreator konfiguracji automatycznej (Beta)" +Basic.AutoConfig.ApplySettings="Zastosuj ustawienia" +Basic.AutoConfig.StartPage="Sposób użytkowania" +Basic.AutoConfig.StartPage.SubTitle="Określ w jaki sposób chcesz używać aplikacji" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optymalizuj pod kątem przesyłania strumieniowego, nagrywanie lokalne jest mniej istotne" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optymalizuj pod nagrywanie lokalne, nie będę strumieniować" +Basic.AutoConfig.VideoPage="Ustawienia wideo" +Basic.AutoConfig.VideoPage.SubTitle="Określ wybrane ustawienia wideo, których chcesz użyć" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Użyj bieżącej (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Monitor %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Użyj bieżącego (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 lub 30 ale preferuj 60" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 lub 30 ale preferuj większą rozdzielczość" +Basic.AutoConfig.VideoPage.CanvasExplanation="Rozdzielczość bazowa niekoniecznie musi być rozdzielczością wynikowego nagrania lub streamu. Ta druga może być efektem skalowania w dół w celu zmniejszenia wymagań poziomu przepływności lub dostępności zasobów potrzebnych do kompresji." +Basic.AutoConfig.StreamPage="Informacja o streamie" +Basic.AutoConfig.StreamPage.SubTitle="Podaj informacje o streamie" +Basic.AutoConfig.StreamPage.Service="Serwis" +Basic.AutoConfig.StreamPage.Service.ShowAll="Pokaż wszystkie..." +Basic.AutoConfig.StreamPage.Server="Serwer" +Basic.AutoConfig.StreamPage.StreamKey="Klucz strumienia" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Oszacuj bitrate przy pomocy testu prędkości łącza (może potrwać parę minut)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferuj enkodowanie sprzętowe" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Enkodowanie sprzętowe odciąża procesor ale może wymagać większej przepustowości dla osiągnięcia porównywalnej jakości." +Basic.AutoConfig.StreamPage.StreamWarning.Title=" Ostrzeżenie" +Basic.AutoConfig.StreamPage.StreamWarning.Text="W celu przetestowania streamu na Twój kanał zostaną wysyłane losowo generowane dane wideo bez dźwięku. Jeżeli to możliwe, zaleca się na czas testu tymczasowo wyłączyć zapisywanie streamu na serwerze i ustawienie streamu w tryb prywatny. Kontynuować?" +Basic.AutoConfig.TestPage="Wyniki końcowe" +Basic.AutoConfig.TestPage.SubTitle.Testing="Aplikacja przeprowadza obecnie serię testów w celu ustalenia najlepszych ustawień" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testowanie zakończone" +Basic.AutoConfig.TestPage.TestingBandwidth="Wykonywanie testu przepustowości, może to potrwać kilka minut..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Łączenie z: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Nie można połączyć z żadnym serwerem. Sprawdź połączenie internetowe i spróbuj ponownie." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Testowanie przepustowości dla: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Testowanie enkodera streamu, może to chwilę potrwać..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testowanie enkodera nagrywania, może to chwilę potrwać..." +Basic.AutoConfig.TestPage.TestingRes="Testowanie rozdzielczości, może to chwilę potrwać..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Uruchomienie enkodera nie powiodło się" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Testuję %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Enkoder streamu" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Enkoder nagrywania" +Basic.AutoConfig.TestPage.Result.Header="Aplikacja ustaliła poniższe ustawienia jako najbardziej idealne dla Ciebie:" +Basic.AutoConfig.TestPage.Result.Footer="Aby użyć tych ustawień, kliknij przycisk Zastosuj ustawienia. Aby ponownie skonfigurować kreatora i spróbować ponownie, kliknij przycisk Wstecz. Aby ręcznie skonfigurować ustawienia, kliknij przycisk Anuluj i otwórz Ustawienia." + +Basic.Stats="Statystyki" +Basic.Stats.CPUUsage="Użycie procesora" +Basic.Stats.HDDSpaceAvailable="Wolne miejsce na dysku" +Basic.Stats.MemoryUsage="Wykorzystanie pamięci" +Basic.Stats.AverageTimeToRender="Średni czas renderowania klatki" +Basic.Stats.SkippedFrames="Pominięte klatki z powodu opóźnień enkodera" +Basic.Stats.MissedFrames="Pominięte klatki z powodu opóźnień renderowania" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Nagrywanie" +Basic.Stats.Status="Status" +Basic.Stats.Status.Recording="Nagrywanie" +Basic.Stats.Status.Live="NA ŻYWO" +Basic.Stats.Status.Reconnecting="Wznawianie połączenia" +Basic.Stats.Status.Inactive="Nieaktywne" +Basic.Stats.DroppedFrames="Zgubione klatki (sieć)" +Basic.Stats.MegabytesSent="Całkowite dane wyjściowe" +Basic.Stats.Bitrate="Przepływność (bitrate)" Updater.Title="Dostępna jest nowa aktualizacja" Updater.Text="Dostępna jest nowa aktualizacja:" @@ -375,6 +464,7 @@ Basic.Settings.General="Główne" Basic.Settings.General.Theme="Motyw" Basic.Settings.General.Language="Język" Basic.Settings.General.EnableAutoUpdates="Automatycznie sprawdzaj dostępność aktualizacji" +Basic.Settings.General.OpenStatsOnStartup="Otwórz statystyki przy starcie aplikacji" Basic.Settings.General.WarnBeforeStartingStream="Pokaż komunikat potwierdzenia uruchomienia streamowania" Basic.Settings.General.WarnBeforeStoppingStream="Pokaż komunikat potwierdzenia zatrzymania streamowania" Basic.Settings.General.Projectors="Projektory" @@ -489,18 +579,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY Rok, cztery cyfry\n%YY Rok, ostatnie dwie cyfry (00-99)\n%MM Miesiąc, liczba dziesiętna, dwie cyfry (01-12)\n%DD Dzień, liczba dziesiętna, dwie cyfry (01-31)\n%hh Godzina, format 24-godzinny, dwie cyfry (00-23)\n%mm Minuta (00-59)\n%ss Sekunda (00-61)\n%% Znak %\n%a Dzień tygodnia, skrót\n%A Dzień tygodnia, pełna nazwa\n%b Nazwa miesiąca, skrót\n%B Nazwa miesiąca, pełna nazwa\n%d Dzień miesiąca, dwie cyfry (01-31)\n%H Godzina, format 24-godzinny, dwie cyfry (00-23)\n%I Godzina, format 12-godzinny, dwie cyfry (01-12)\n%m Miesiąc, dwie cyfry (01-12)\n%M Minuta (00-59)\n%p oznaczenie przed lub po południu\n%S Sekunda (00-61)\n%y Rok, ostatnie dwie cyfry (00-99)\n%Y Rok\n%z ISO 8601, przesunięcie od czasu UTC\n nazwa lub skrót\n%Z Nazwa lub skrót strefy czasowej\n" Basic.Settings.Video="Obraz" -Basic.Settings.Video.Adapter="Karta graficzna:" -Basic.Settings.Video.BaseResolution="Rozdzielczość bazowa (obraz):" -Basic.Settings.Video.ScaledResolution="Rozdzielczość wynikowa (skalowana):" -Basic.Settings.Video.DownscaleFilter="Filtr skalujący:" +Basic.Settings.Video.Adapter="Karta graficzna" +Basic.Settings.Video.BaseResolution="Rozdzielczość bazowa (obraz)" +Basic.Settings.Video.ScaledResolution="Rozdzielczość wynikowa (skalowana)" +Basic.Settings.Video.DownscaleFilter="Filtr skalujący" Basic.Settings.Video.DisableAeroWindows="Wyłącz Aero (tylko Windows)" -Basic.Settings.Video.FPS="FPS (klatki na sekundę):" +Basic.Settings.Video.FPS="FPS (klatki na sekundę)" Basic.Settings.Video.FPSCommon="Typowe wartości FPS" Basic.Settings.Video.FPSInteger="Całkowite wartości FPS" Basic.Settings.Video.FPSFraction="Ułamkowe wartości FPS" -Basic.Settings.Video.Numerator="Licznik:" -Basic.Settings.Video.Denominator="Mianownik:" -Basic.Settings.Video.Renderer="Renderowanie:" +Basic.Settings.Video.Numerator="Licznik" +Basic.Settings.Video.Denominator="Mianownik" +Basic.Settings.Video.Renderer="Renderowanie" Basic.Settings.Video.InvalidResolution="Nieprawidłowa rozdzielczość. Wartość powinna mieć format [szerokość]x[wysokość] (np. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Wyjście wideo jest aktywne. Należy wyłączyć wszelkie wyjścia, aby zmienić ustawienia wideo." Basic.Settings.Video.DisableAero="Wyłącz Aero" @@ -538,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Częściowy" Basic.Settings.Advanced.Video.ColorRange.Full="Pełny" Basic.Settings.Advanced.Audio.MonitoringDevice="Monitorowane urządzenie audio" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Domyślne" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Wyłącz systemowe obniżanie głośności innych źródeł dźwięków" Basic.Settings.Advanced.StreamDelay="Opóźnienie streamu" Basic.Settings.Advanced.StreamDelay.Duration="Czas trwania (s)" Basic.Settings.Advanced.StreamDelay.Preserve="Zachowuj punkt przerwania (zwiększ opóźnienie) podczas ponownego łączenia" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Musisz wybrać przynajmniej jedną ścieżkę" OutputWarnings.MultiTrackRecording="Ostrzeżenie: Pewne formaty plików (np. FLV) nie obsługują wielu ścieżek dźwiękowych" OutputWarnings.MP4Recording="Ostrzeżenie: Nagrania zapisanego w formacie mp4 nie będzie można odzyskać, jeśli plik nie zostanie zakończony poprawnie (np. w wyniku BSOD, braku prądu, itp.). Jeśli chcesz nagrać wiele ścieżek audio należy rozważyć użycie formatu mkv i remux nagrania do mp4 po zakończeniu (Plik -> Przepakuj nagrania)." +FinalScene.Title="Usuń scenę" +FinalScene.Text="Musi być co najmniej jedna scena." + diff --git a/UI/data/locale/pt-BR.ini b/UI/data/locale/pt-BR.ini index 4fb7cce..f73d41c 100644 --- a/UI/data/locale/pt-BR.ini +++ b/UI/data/locale/pt-BR.ini @@ -31,6 +31,9 @@ DroppedFrames="Quadros Perdidos %1 (%2%)" PreviewProjector="Projetor em tela cheia(pré-visualização)" SceneProjector="Projetor em tela cheia(cena)" SourceProjector="Projetor em tela cheia(fonte)" +PreviewWindow="Projetor em janela (Pré-visualização)" +SceneWindow="Projetor em janela (Cena)" +SourceWindow="Projetor em janela (Fonte)" Clear="Limpar" Revert="Desfazer" Show="Exibir" @@ -54,8 +57,107 @@ Minutes="Minutos" Seconds="Segundos" Deprecated="Obsoleto" ReplayBuffer="Buffer do Replay" +Import="Importar" Export="Exportar" +Copy="Copiar" +Paste="Colar" +PasteReference="Colar (Referência)" +PasteDuplicate="Colar (Duplicar)" +RemuxRecordings="Remixar Gravações" +Next="Avançar" +Back="Voltar" +AlreadyRunning.Title="OBS já está em execução" +AlreadyRunning.Text="OBS já está em execução! A menos que você tenha a intenção de fazer isso, por favor, feche todas as instâncias existentes do OBS antes de tentar executar uma nova. Se você tiver definido para minimizar o OBS na bandeja do sistema, verifique se ainda está lá em execução." +AlreadyRunning.LaunchAnyway="Executar mesmo assim" + +Copy.Filters="Copiar Filtros" +Paste.Filters="Colar Filtros" + +BandwidthTest.Region="Região" +BandwidthTest.Region.US="Estados Unidos" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Ásia" +BandwidthTest.Region.Other="Outro" + +Basic.FirstStartup.RunWizard="Você gostaria de executar o assistente de configuração? Você também pode definir manualmente suas configurações clicando no botão \"Configurações\" na janela principal." +Basic.FirstStartup.RunWizard.BetaWarning="(Nota: O assistente de configuração está atualmente em beta)" +Basic.FirstStartup.RunWizard.NoClicked="Se você mudar de ideia, você pode executar o assistente de configuração a qualquer momento no menu \"Ferramentas\"." + +Basic.AutoConfig="Assistente de Configuração" +Basic.AutoConfig.Beta="Assistente de Configuração (Beta)" +Basic.AutoConfig.ApplySettings="Aplicar Configurações" +Basic.AutoConfig.StartPage="Informações de Uso" +Basic.AutoConfig.StartPage.SubTitle="Marque para que você deseja usar o programa" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Otimizar para transmissão, gravação é opcional" +Basic.AutoConfig.StartPage.PrioritizeRecording="Otimizar somente para gravação, eu não farei transmissão" +Basic.AutoConfig.VideoPage="Configurações de Vídeo" +Basic.AutoConfig.VideoPage.SubTitle="Marque as configurações desejadas que você gostaria de usar" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Usar Atual (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Monitor %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Usar Atual (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 ou 30, mas prefiro 60 quando possível" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 ou 30, mas prefiro alta resolução" +Basic.AutoConfig.VideoPage.CanvasExplanation="Nota: A resolução da tela (base) não é necessariamente a mesma resolução que você transmitirá ou gravará. Sua resolução final da transmissão/gravação pode ser redimencionada da resolução da tela para reduzir o uso de recursos ou bitrate necessário." +Basic.AutoConfig.StreamPage="Informações da Transmissão" +Basic.AutoConfig.StreamPage.SubTitle="Por favor, digite suas informações para a transmissão" +Basic.AutoConfig.StreamPage.Service="Serviço" +Basic.AutoConfig.StreamPage.Service.ShowAll="Mostrar todos..." +Basic.AutoConfig.StreamPage.Server="Servidor" +Basic.AutoConfig.StreamPage.StreamKey="Chave da Transmissão" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Estimar taxa de bits com teste de largura de banda (pode levar uns minutos)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferir codificação por hardware" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Codificação por Hardware elimina a maioria do uso de CPU, mas pode exigir uma taxa de bits maior para obter o mesmo nível de qualidade." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Aviso de Transmissão" +Basic.AutoConfig.StreamPage.StreamWarning.Text="O teste de largura de banda está prestes a transmitir imagens aleatórias e sem áudio para o seu canal. Se for possível, é recomendável desativar temporariamente a gravação da transmissão e definir a transmissão como privada até que o teste seja concluído. Continuar?" +Basic.AutoConfig.TestPage="Resultados Finais" +Basic.AutoConfig.TestPage.SubTitle.Testing="O programa está executando um conjunto de testes para estimar as configurações ideais" +Basic.AutoConfig.TestPage.SubTitle.Complete="Teste completo" +Basic.AutoConfig.TestPage.TestingBandwidth="Executando teste de largura de banda, isso pode levar alguns minutos..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Conectando a: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Falha ao se conectar a qualquer servidor, verifique sua conexão com a Internet e tente novamente." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Teste de largura de banda para: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Testando codificador de transmissão, isto pode demorar um minuto..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testando codificador de gravação, isto pode demorar um minuto..." +Basic.AutoConfig.TestPage.TestingRes="Testando resoluções, isto pode levar alguns minutos..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Falha ao iniciar o codificador" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Testando %1x%2 %3 QPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Codificador da Transmissão" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Codificador da Gravação" +Basic.AutoConfig.TestPage.Result.Header="O programa determinou que estas configurações estimadas são as mais ideais para você:" +Basic.AutoConfig.TestPage.Result.Footer="Para usar essas configurações, clique em \"Aplicar Configurações\". Para reconfigurar o assistente e tentar novamente, clique em \"Voltar\". Para definir manualmente, clique em \"Cancelar\" e abra as \"Configurações\"." + +Basic.Stats="Estatísticas" +Basic.Stats.CPUUsage="Uso de CPU" +Basic.Stats.HDDSpaceAvailable="Espaço em disco disponível" +Basic.Stats.MemoryUsage="Uso de Memória" +Basic.Stats.AverageTimeToRender="Tempo médio para renderizar quadro" +Basic.Stats.SkippedFrames="Quadros ignorados devido a demora na codificação" +Basic.Stats.MissedFrames="Quadros perdidos devido a demora na renderização" +Basic.Stats.Output.Stream="Transmissão" +Basic.Stats.Output.Recording="Gravação" +Basic.Stats.Status="Status" +Basic.Stats.Status.Recording="Gravando" +Basic.Stats.Status.Live="AO VIVO" +Basic.Stats.Status.Reconnecting="Reconectando" +Basic.Stats.Status.Inactive="Inativa" +Basic.Stats.DroppedFrames="Quadros Perdidos (Rede)" +Basic.Stats.MegabytesSent="Saída Total de Dados" +Basic.Stats.Bitrate="Taxa de bits" + +Updater.Title="Nova atualização disponível" +Updater.Text="Há uma nova atualização disponível:" +Updater.UpdateNow="Atualizar agora" +Updater.RemindMeLater="Lembre-me depois" +Updater.Skip="Pular Versão" +Updater.Running.Title="Programa atualmente em execução" +Updater.Running.Text="Saídas estão atualmente ativas, por favor desligue quaisquer saídas ativas antes de tentar atualizar" +Updater.NoUpdatesAvailable.Title="Nenhuma atualização disponível" +Updater.NoUpdatesAvailable.Text="Não há atualizações disponíveis" +Updater.FailedToLaunch="Falha ao iniciar o atualizador" +Updater.GameCaptureActive.Title="Captura de jogo ativa" +Updater.GameCaptureActive.Text="A biblioteca de captura de jogos está em uso. Feche todos os jogos / programas que estão sendo capturados (ou reinicie o computador) e tente novamente." QuickTransitions.SwapScenes="Trocar Cenas de Prévia/Saída após a Transição" QuickTransitions.SwapScenesTT="Troca a preview e a saída após transicionar (se a a cena original de saída ainda exisitr).\nIsto não irá desfazer nenhuma mudança que foi feita na cena original da saída." @@ -97,6 +199,10 @@ ConfirmRemove.Title="Confirmar a Remoção" ConfirmRemove.Text="Tem certeza que deseja remover '$1'?" ConfirmRemove.TextMultiple="Você tem certeza que quer remover esses %1 itens?" +Output.StartStreamFailed="Falha ao iniciar a transmissão" +Output.StartRecordingFailed="Falha ao iniciar a gravação" +Output.StartReplayFailed="Falha ao iniciar o buffer de repetição" +Output.StartFailedGeneric="Falha ao iniciar a saída. Favor verificar o log para informação detalhada do erro.\n\nAviso: Se estiver usando os codificadores NVENC ou AMD, tenha certeza de que seus drivers estão atualizados." Output.ConnectFail.Title="Falha ao conectar" Output.ConnectFail.BadPath="Caminho inválido ou URL inválida. Por favor verifique se as configurações estão válidas." @@ -174,6 +280,10 @@ Deinterlacing.Yadif2x="Yadif 2x" Deinterlacing.TopFieldFirst="Campo Superior Primeiro" Deinterlacing.BottomFieldFirst="Campo Inferior Primeiro" +VolControl.SliderUnmuted="Barra de volume para '%1': %2" +VolControl.SliderMuted="Barra de volume para '%1': %2 (atualmente silenciado)" +VolControl.Mute="Silenciar '%1'" +VolControl.Properties="Propriedades de '%1'" Basic.Main.AddSceneDlg.Title="Adicionar Cena" Basic.Main.AddSceneDlg.Text="Por favor, digite o nome da cena" @@ -283,7 +393,7 @@ Basic.MainMenu.File="&Arquivo" Basic.MainMenu.File.Export="&Exportar" Basic.MainMenu.File.Import="&Importar" Basic.MainMenu.File.ShowRecordings="Mostrar &Gravações" -Basic.MainMenu.File.Remux="Re&mux gravações" +Basic.MainMenu.File.Remux="Trocar Container (&M)" Basic.MainMenu.File.Settings="&Configurações" Basic.MainMenu.File.ShowSettingsFolder="Mostrar pasta de configurações" Basic.MainMenu.File.ShowProfileFolder="Mostrar pasta de perfil" @@ -353,8 +463,11 @@ Basic.Settings.Confirm="Você tem alterações não salvas. Salvar as alteraç Basic.Settings.General="Geral" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" +Basic.Settings.General.EnableAutoUpdates="Buscar novas atualizações ao iniciar o programa" +Basic.Settings.General.OpenStatsOnStartup="Abrir janela de estatísticas na inicialização" Basic.Settings.General.WarnBeforeStartingStream="Mostrar diálogo de confirmação quando iniciar transmissões" Basic.Settings.General.WarnBeforeStoppingStream="Mostrar diálogo de confirmação quando terminar transmissões" +Basic.Settings.General.Projectors="Projetores" Basic.Settings.General.HideProjectorCursor="Ocultar o cursor sobre projetores" Basic.Settings.General.ProjectorAlwaysOnTop="Colocar projetores sempre no topo" Basic.Settings.General.Snapping="Alinhamentos com encaixe na Cena" @@ -364,7 +477,11 @@ Basic.Settings.General.SourceSnapping="Encaixar fontes com outras fontes" Basic.Settings.General.SnapDistance="Sensibilidade de Encaixamento" Basic.Settings.General.RecordWhenStreaming="Gravar automaticamente quando estiver transmitindo" Basic.Settings.General.KeepRecordingWhenStreamStops="Continuar gravando quando a transmissão parar" +Basic.Settings.General.ReplayBufferWhileStreaming="Iniciar automaticamente o buffer de repetição durante a transmissão" +Basic.Settings.General.KeepReplayBufferStreamStops="Manter o buffer de repetição ativo quando a transmissão parar" +Basic.Settings.General.SysTray="Bandeja do sistema" Basic.Settings.General.SysTrayWhenStarted="Minimizar para a bandeja do sistema quando iniciar" +Basic.Settings.General.SystemTrayHideMinimize="Sempre minimizar para a bandeja (ignorar barra de tarefas)" Basic.Settings.General.SaveProjectors="Salvar projetores ao sair" Basic.Settings.Stream="Stream" @@ -454,6 +571,8 @@ Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Configurações do codificado Basic.Settings.Output.Adv.FFmpeg.AEncoder="Codificador de áudio" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Configurações do codificador de áudio(se houver)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Configurações do Muxer (se houver)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Intervalo de Keyframes (frames)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Mostrar todos os codecs (incluir potencialmente incompatíveis)" 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 %H-%M-%S\n%A %d-%Y-%m %Y-%m-%d %H-%M-%S\n%Y-%b-123_2_ 16_321 %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" @@ -507,13 +626,17 @@ Basic.Settings.Advanced.Video.ColorSpace="Espaço de cor YUV" Basic.Settings.Advanced.Video.ColorRange="Gama de cores YUV" Basic.Settings.Advanced.Video.ColorRange.Partial="Limitado" Basic.Settings.Advanced.Video.ColorRange.Full="Completo" +Basic.Settings.Advanced.Audio.MonitoringDevice="Dispositivo de Monitoramento de Áudio" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Padrão" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Desativar a Oscilação de Áudio do Windows" Basic.Settings.Advanced.StreamDelay="Atraso da transmissão" Basic.Settings.Advanced.StreamDelay.Duration="Duração (segundos)" Basic.Settings.Advanced.StreamDelay.Preserve="Preservar o ponto de corte (aumento de atraso) quando reconectar" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Uso de memória estimado: %1 MB" 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.AdvAudio="Propriedades de áudio avançadas" Basic.AdvAudio.Name="Nome" @@ -521,6 +644,10 @@ Basic.AdvAudio.Volume="Volume (%)" Basic.AdvAudio.Mono="Downmix para Mono" Basic.AdvAudio.Panning="Balanceamento" Basic.AdvAudio.SyncOffset="Atraso de sincronização (ms)" +Basic.AdvAudio.Monitoring="Monitoramento de Áudio" +Basic.AdvAudio.Monitoring.None="Não monitorar" +Basic.AdvAudio.Monitoring.MonitorOnly="Apenas monitorar (saída muda)" +Basic.AdvAudio.Monitoring.Both="Monitorar e enviar áudio" Basic.AdvAudio.AudioTracks="Faixas" Basic.Settings.Hotkeys="Teclas de atalho" @@ -579,4 +706,8 @@ SceneItemHide="Ocultar '%1'" OutputWarnings.NoTracksSelected="Você deve selecionar pelo menos uma faixa" OutputWarnings.MultiTrackRecording="Aviso: Alguns formatos (como FLV) não suportam várias faixas por gravação" +OutputWarnings.MP4Recording="Atenção: Gravações salvas em arquivos MP4 se tornarão irrecuperáveis se o arquivo não puder ser finalizado (ex: tela azul, queda de energia, etc.). Caso queira gravar várias faixas de áudio, considere usar MKV e usar a ferramenta de remux no arquivo resultante da gravação (Arquivo->Remux gravações)" + +FinalScene.Title="Excluir cena" +FinalScene.Text="É preciso haver pelo menos uma cena." diff --git a/UI/data/locale/pt-PT.ini b/UI/data/locale/pt-PT.ini index 24feb7d..2825877 100644 --- a/UI/data/locale/pt-PT.ini +++ b/UI/data/locale/pt-PT.ini @@ -54,6 +54,12 @@ Minutes="Minutos" Seconds="Segundos" + + + + + + QuickTransitions.SwapScenes="Trocar pré-visualização/saída de cenas Depois de uma Transição" QuickTransitions.DuplicateScene="Duplicar cena" QuickTransitions.EditProperties="Fontes duplicadas" @@ -409,18 +415,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss Basic.Settings.Video="Vídeo" -Basic.Settings.Video.Adapter="Adaptador de Vídeo:" -Basic.Settings.Video.BaseResolution="Resolução de base (tela):" -Basic.Settings.Video.ScaledResolution="Resolução de saída (escalado):" -Basic.Settings.Video.DownscaleFilter="Filtro de Escalamento:" +Basic.Settings.Video.Adapter="Adaptador de Vídeo" +Basic.Settings.Video.BaseResolution="Resolução de base (tela)" +Basic.Settings.Video.ScaledResolution="Resolução de saída (escalado)" +Basic.Settings.Video.DownscaleFilter="Filtro de Escalamento" Basic.Settings.Video.DisableAeroWindows="Desactivar Aero (apenas no Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Valor de FPS Comum" Basic.Settings.Video.FPSInteger="Valor de FPS Íntegro" Basic.Settings.Video.FPSFraction="Valor de FPS Fracional" -Basic.Settings.Video.Numerator="Numerador:" -Basic.Settings.Video.Denominator="Demoninador:" -Basic.Settings.Video.Renderer="Renderizador:" +Basic.Settings.Video.Numerator="Numerador" +Basic.Settings.Video.Denominator="Demoninador" +Basic.Settings.Video.Renderer="Renderizador" Basic.Settings.Video.InvalidResolution="Resolução Inválida, Tem de ser [largura]x[altura] (ex. 1920x1080)" Basic.Settings.Video.CurrentlyActive="A Saída de Vídeo encontra-se activa. Por favor desligue a saída de vídeo para mudar as definições de vídeo." Basic.Settings.Video.DisableAero="Desativar Aero" @@ -528,3 +534,4 @@ SceneItemHide="Ocultar '%1'" OutputWarnings.NoTracksSelected="Tem de selecionar pelo menos uma faixa" OutputWarnings.MultiTrackRecording="Aviso: Alguns formatos (como FLV) não suportam várias faixas por gravação" + diff --git a/UI/data/locale/ro-RO.ini b/UI/data/locale/ro-RO.ini index d39fc9b..90982d9 100644 --- a/UI/data/locale/ro-RO.ini +++ b/UI/data/locale/ro-RO.ini @@ -56,6 +56,12 @@ Import="Importă" Export="Exportă" + + + + + + QuickTransitions.SwapScenes="Comută între previzualizare/scenele de ieșire după tranziționare" QuickTransitions.SwapScenesTT="Schimba previzualizarea si scenele de output dupa tranzitionare (în cazul în care încă există outputul scenei originale). \nAceasta nu va anula nicio modificăre care au fost făcute la outputul scenei originale." QuickTransitions.DuplicateScene="Duplică scena" @@ -417,18 +423,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY An, patru cifre\n%YY An, ultimele 2 cifre (00-99)\n%MM Luna ca numar decimal (01-12)\n%DD Ziua lunii, prefixat cu 0 (01-31)\n%hh Ora in format 24h (00-23)\n%mm Minut (00-59)\n%ss Secunda (00-61)\n%% Un % semn\n%a Numele zilei abreviat\n%A Numele zilei full\n%b Numele lunii abreviat\n%B Numele lunii full\n%d Ziua lunii, prefixat cu 0 (01-31)\n%H Ora in format 24h (00-23)\n%I Ora in format 12h (01-12)\n%m Luna ca numar decimal (01-12)\n%M Minut (00-59)\n%p Desemnari AM or PM\n%S Secunda (00-61)\n%y An, ultimele 2 cifre (00-99)\n%Y An\n%z ISO 8601 compensare UTC or fus orar\n nume sau abreviere\n%Z Numele fusului orar sau abreviere\n" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Adaptor video:" -Basic.Settings.Video.BaseResolution="Rezoluție (canvas) de bază:" -Basic.Settings.Video.ScaledResolution="Rezoluție (scalată) la ieșire:" -Basic.Settings.Video.DownscaleFilter="Filtru pentru descalare:" +Basic.Settings.Video.Adapter="Adaptor video" +Basic.Settings.Video.BaseResolution="Rezoluție (canvas) de bază" +Basic.Settings.Video.ScaledResolution="Rezoluție (scalată) la ieșire" +Basic.Settings.Video.DownscaleFilter="Filtru pentru descalare" Basic.Settings.Video.DisableAeroWindows="Dezactivează Aero (Numai Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Valori FPS comune" Basic.Settings.Video.FPSInteger="Valoare cu număr întreg pentru FPS" Basic.Settings.Video.FPSFraction="Valoare FPS fracționată" -Basic.Settings.Video.Numerator="Numărător:" -Basic.Settings.Video.Denominator="Numitor:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Numărător" +Basic.Settings.Video.Denominator="Numitor" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Valoare rezoluţie invalidă. Trebuie să fie [latime]x[înălţime] (de exemplu, 1920x1080)" Basic.Settings.Video.CurrentlyActive="Ieşirea video este în prezent activă. Vă rugăm să opriţi orice ieşiri pentru a schimba setările video." Basic.Settings.Video.DisableAero="Dezactivează Aero" @@ -536,3 +542,4 @@ SceneItemHide="Ascunde „%1”" OutputWarnings.NoTracksSelected="Trebuie să selectezi cel puțin o pistă" OutputWarnings.MultiTrackRecording="Atenție: Anumite formate (precum FLV) nu suportă multiple piste per înregistrare" + diff --git a/UI/data/locale/ru-RU.ini b/UI/data/locale/ru-RU.ini index 2a184b1..10766b3 100644 --- a/UI/data/locale/ru-RU.ini +++ b/UI/data/locale/ru-RU.ini @@ -31,6 +31,9 @@ DroppedFrames="Пропуск кадров %1 (%2%)" PreviewProjector="Полноэкранный проектор (предпросмотр)" SceneProjector="Полноэкранный проектор (сцена)" SourceProjector="Полноэкранный проектор (источник)" +PreviewWindow="Оконный проектор (Предпросмотр)" +SceneWindow="Оконный проектор (Сцена)" +SourceWindow="Оконный проектор (Источник)" Clear="Очистить" Revert="Вернуть" Show="Показать" @@ -56,6 +59,92 @@ Deprecated="Устаревшее" ReplayBuffer="Буфер повтора" Import="Импорт" Export="Экспорт" +Copy="Копировать" +Paste="Вставить" +PasteReference="Вставить (Ссылка)" +PasteDuplicate="Вставить (Дубликат)" +RemuxRecordings="Ремультиплексирование записей" +Next="Далее" +Back="Назад" + +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="Мастер автоматической настройки (Бета)" +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="Использование ЦП" +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="Доступно новое обновление:" @@ -375,6 +464,7 @@ 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="Проекторы" @@ -405,7 +495,7 @@ 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.Adv="Расширенный" Basic.Settings.Output.Mode.FFmpeg="Вывод FFmpeg" Basic.Settings.Output.UseReplayBuffer="Включить Буфер повтора" Basic.Settings.Output.ReplayBuffer.SecondsMax="Максимальное время повтора (секунд)" @@ -489,18 +579,18 @@ FilenameFormatting.completer="%DD-%MM-%CCYY %hh-%mm-%ss\n%DD-%MM-%YY %hh-%mm-%ss 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="Видеоадаптер:" -Basic.Settings.Video.BaseResolution="Базовое (основа) разрешение:" -Basic.Settings.Video.ScaledResolution="Выходное (масштабированное) разрешение:" -Basic.Settings.Video.DownscaleFilter="Фильтр масштабирования:" +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.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.Numerator="Числитель" +Basic.Settings.Video.Denominator="Знаменатель" +Basic.Settings.Video.Renderer="Рендер" Basic.Settings.Video.InvalidResolution="Неверное разрешение. Должно быть [Ширина]x[Высота] (т.е. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Видео выход в данный момент активен. Пожалуйста, отключите все выходы, чтобы изменить настройки видео." Basic.Settings.Video.DisableAero="Отключить Aero" @@ -538,6 +628,7 @@ 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="Сохранить точку отсечки (увеличить задержку) при переподключении" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Вы должны выбрать хотя бы OutputWarnings.MultiTrackRecording="Предупреждение: Некоторые форматы (такие как FLV) не поддерживают множественные звуковые дорожки" OutputWarnings.MP4Recording="Внимание: Записи, сохраненные в MP4 будут нечитаемы, если файл не будет завершен (например, в результате BSOD'а, потери напряжения в сети и т.д.). Если вы хотите записывать несколько аудио дорожек, рассмотрите использование MKV, и последующее ремультиплексирование в MP4 после завершения записи (Файл -> Ремультиплексирование записей)" +FinalScene.Title="Удалить сцену" +FinalScene.Text="Здесь должна быть по крайней мере одна сцена." + diff --git a/UI/data/locale/sk-SK.ini b/UI/data/locale/sk-SK.ini index 4b292f0..9624d94 100644 --- a/UI/data/locale/sk-SK.ini +++ b/UI/data/locale/sk-SK.ini @@ -47,6 +47,12 @@ Minutes="Minúty" Seconds="Sekundy" + + + + + + QuickTransitions.DuplicateScene="Duplikovať scénu" Basic.TransitionDuration="Trvanie" @@ -243,14 +249,14 @@ Basic.Settings.Output.Adv.Recording.Type="Typ" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Video adaptér:" +Basic.Settings.Video.Adapter="Video adaptér" Basic.Settings.Video.DisableAeroWindows="Vypnúť Aero (len Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Bežné hodnoty FPS" Basic.Settings.Video.FPSInteger="Celočíselná hodnota FPS" Basic.Settings.Video.FPSFraction="Zlomková hodnota FPS" -Basic.Settings.Video.Numerator="Čitateľ:" -Basic.Settings.Video.Denominator="Menovateľ:" +Basic.Settings.Video.Numerator="Čitateľ" +Basic.Settings.Video.Denominator="Menovateľ" Basic.Settings.Video.InvalidResolution="Neplatné rozlíšenie. Správne je [šírka]x[výška] (napr. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Výstup videa je práve aktívny. Prosím, vypnite všetky výstupy na zmenu nastavení videa." @@ -269,3 +275,4 @@ Basic.Settings.Audio.Channels="Kanály" + diff --git a/UI/data/locale/sl-SI.ini b/UI/data/locale/sl-SI.ini index ac0a831..c1d7a5a 100644 --- a/UI/data/locale/sl-SI.ini +++ b/UI/data/locale/sl-SI.ini @@ -32,6 +32,12 @@ DroppedFrames="Izpuščene sličice %1 (%2 %)" + + + + + + NameExists.Title="Ime že obstaja" NameExists.Text="Ime je že v uporabi." @@ -209,16 +215,16 @@ Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Vse datoteke" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Grafična kartica:" -Basic.Settings.Video.DownscaleFilter="Pomanjševalni filter:" +Basic.Settings.Video.Adapter="Grafična kartica" +Basic.Settings.Video.DownscaleFilter="Pomanjševalni filter" Basic.Settings.Video.DisableAeroWindows="Onemogoči Aero (samo Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Pogoste FPS vrednosti" Basic.Settings.Video.FPSInteger="Cela števila FPS" Basic.Settings.Video.FPSFraction="Decimalne FPS vrednosti" -Basic.Settings.Video.Numerator="Števec:" -Basic.Settings.Video.Denominator="Imenovalec:" -Basic.Settings.Video.Renderer="Rendanje:" +Basic.Settings.Video.Numerator="Števec" +Basic.Settings.Video.Denominator="Imenovalec" +Basic.Settings.Video.Renderer="Rendanje" Basic.Settings.Video.InvalidResolution="Neveljavna vrednost ločljivost. Biti mora [širina] x [višina] (npr. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Video izhod je trenutno aktiven. Prosimo, izklopite kakšen koli izhod za spreminjanje nastavitev." @@ -244,3 +250,4 @@ Basic.Settings.Advanced.Video.ColorRange.Full="Celotno" + diff --git a/UI/data/locale/sr-CS.ini b/UI/data/locale/sr-CS.ini index cc86301..210f416 100644 --- a/UI/data/locale/sr-CS.ini +++ b/UI/data/locale/sr-CS.ini @@ -55,6 +55,12 @@ Seconds="Sekundi" Deprecated="Prevaziđeno" + + + + + + QuickTransitions.SwapScenes="Zameni scene pregleda/izlaza nakon prelaza" QuickTransitions.SwapScenesTT="Zamenjuje scene pregleda i izlaza nakon prelaza (ako originalna scena izlaza još uvek postoji).\nOvo neće poništiti promene koje su načinjene nad originalnom scenom izlaza." QuickTransitions.DuplicateScene="Dupliraj scenu" @@ -434,18 +440,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY Godina, četiri cifre\n%YY Godina, poslednje dve cifre (00-99)\n%MM Mesec kao decimalni broj (01-12)\n%DD Dan u mesecu, sa nulom ispred (01-31)\n%hh Sat u 24-časovnom zapisu (00-23)\n%mm Minut (00-59)\n%ss Sekunda (00-61)\n%% Znak procenta\n%a Skraćeno ime dana u nedelji\n%A Puno ime dana u nedelji\n%b Skraćeno ime meseca\n%B Puno ime meseca\n%d Dan u mesecu, sa nulom ispred (01-31)\n%H Sat u 24-časovnom zapisu (00-23)\n%I Sat u 12-časovnom zapisu (01-12)\n%m Mesec kao decimalni broj (01-12)\n%M Minut (00-59)\n%p Oznaka za pre ili posle podne\n%S Sekunda (00-61)\n%y Godina, poslednje dve cifre (00-99)\n%Y Godina\n%z ISO 8601 odstupanje od UTC ili ime\n vremenske zone ili skraćenica\n%Z Ime vremenske zone ili skraćenica\n" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Video adapter:" -Basic.Settings.Video.BaseResolution="Osnovna (površinska) rezolucija:" -Basic.Settings.Video.ScaledResolution="Izlazna (skalirana) rezolucija:" -Basic.Settings.Video.DownscaleFilter="Filter za skaliranje:" +Basic.Settings.Video.Adapter="Video adapter" +Basic.Settings.Video.BaseResolution="Osnovna (površinska) rezolucija" +Basic.Settings.Video.ScaledResolution="Izlazna (skalirana) rezolucija" +Basic.Settings.Video.DownscaleFilter="Filter za skaliranje" Basic.Settings.Video.DisableAeroWindows="Isključi Aero (samo za Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Uobičakene FPS vrednosti" Basic.Settings.Video.FPSInteger="Brojevna FPS vrednost" Basic.Settings.Video.FPSFraction="Brojevna FPS vrednost sa decimalama" -Basic.Settings.Video.Numerator="Brojilac:" -Basic.Settings.Video.Denominator="Imenilac:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Brojilac" +Basic.Settings.Video.Denominator="Imenilac" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Neispravna vrednost rezolucije. Mora biti [width]x[height] (npr. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Video izlaz je trenutno aktivan. Molim isključite sve izlaze da promenite video podešavanja." Basic.Settings.Video.DisableAero="Onemogući Aero" @@ -553,3 +559,4 @@ SceneItemHide="Sakrij '%1'" 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 f76cb37..edf1a05 100644 --- a/UI/data/locale/sr-SP.ini +++ b/UI/data/locale/sr-SP.ini @@ -55,6 +55,12 @@ Seconds="Секунди" Deprecated="Превазиђено" + + + + + + QuickTransitions.SwapScenes="Замени сцене прегледа/излаза након прелаза" QuickTransitions.SwapScenesTT="Замењује сцене прегледа и излаза након прелаза (ако оригинална сцена још увек постоји).\nОво неће поништити промене које су начињене над оригиналном сценом излаза." QuickTransitions.DuplicateScene="Дуплирај сцену" @@ -434,18 +440,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss 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 Ознака за пре или после подне\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.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.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.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" @@ -553,3 +559,4 @@ SceneItemHide="Сакриј '%1'" OutputWarnings.NoTracksSelected="Морате одабрати макар једну траку" OutputWarnings.MultiTrackRecording="Упозорење: Одређени формати (као што је FLV) не подржавају више трака по снимку" + diff --git a/UI/data/locale/sv-SE.ini b/UI/data/locale/sv-SE.ini index c18a4ae..6873cac 100644 --- a/UI/data/locale/sv-SE.ini +++ b/UI/data/locale/sv-SE.ini @@ -31,6 +31,9 @@ DroppedFrames="Tappade bildrutor %1 (%2%)" PreviewProjector="Fullskärmsprojektor (förhandsvisning)" SceneProjector="Fullskärmsprojektor (scen)" SourceProjector="Fullskärmsprojektor (källa)" +PreviewWindow="Fönsterprojektor (förhandsvisning)" +SceneWindow="Fönsterprojektor (scen)" +SourceWindow="Fönsterprojektor (källa)" Clear="Rensa" Revert="Återgå" Show="Visa" @@ -56,6 +59,90 @@ Deprecated="Föråldrat" ReplayBuffer="Reprisbuffert" Import="Importera" Export="Exportera" +Copy="Kopiera" +Paste="Klistra in" +PasteReference="Klistra in (referens)" +PasteDuplicate="Klistra in (duplicera)" +RemuxRecordings="Remuxa inspelningar" +Next="Nästa" +Back="Tillbaka" + +AlreadyRunning.Title="OBS körs redan" +AlreadyRunning.LaunchAnyway="Kör ändå" + +Copy.Filters="Kopiera filter" +Paste.Filters="Klistra in filter" + +BandwidthTest.Region="Region" +BandwidthTest.Region.US="USA" +BandwidthTest.Region.EU="Europa" +BandwidthTest.Region.Asia="Asien" +BandwidthTest.Region.Other="Övrigt" + +Basic.FirstStartup.RunWizard="Skulle du vilja köra den automatiska konfigurationsguiden? Du kan konfigurera dina inställningar manuellt genom att klicka på knappen Inställningar i huvudfönstret." +Basic.FirstStartup.RunWizard.BetaWarning="(OBS: Den automatiska konfigurationsguiden är för nuvarande i betastadiet)" +Basic.FirstStartup.RunWizard.NoClicked="Om du ändrar dig kan du köra den automatiska konfigurationsguiden när som helst från verktygsmenyn." + +Basic.AutoConfig="Automatisk konfigurationsguide" +Basic.AutoConfig.Beta="Automatisk konfigurationsguide (beta)" +Basic.AutoConfig.ApplySettings="Verkställ inställningar" +Basic.AutoConfig.StartPage="Användningsinformation" +Basic.AutoConfig.StartPage.SubTitle="Ange vad du vill använda programmet för" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Optimera för strömning, inspelning sekundärt" +Basic.AutoConfig.StartPage.PrioritizeRecording="Optimera endast för inspelning, jag kommer inte att strömma" +Basic.AutoConfig.VideoPage="Videoinställningar" +Basic.AutoConfig.VideoPage.SubTitle="Ange önskade videoinställningar som du vill använda" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Använd nuvarande (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Bildskärm %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Använd nuvarande (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Antingen 60 eller 30, men föredra 60 när det är möjligt" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Antingen 60 eller 30, men föredra hög upplösning" +Basic.AutoConfig.StreamPage="Ströminformation" +Basic.AutoConfig.StreamPage.SubTitle="Ange din ströminformation" +Basic.AutoConfig.StreamPage.Service="Tjänst" +Basic.AutoConfig.StreamPage.Service.ShowAll="Visa alla..." +Basic.AutoConfig.StreamPage.Server="Server" +Basic.AutoConfig.StreamPage.StreamKey="Strömnyckel" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Länk)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Uppskatta bithastighetens med ett bandbreddstest (kan ta några minuter)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Föredra hårdvarukodning" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hårdvarukodning eliminerar den mesta processoranvändningen, men kan kräva mer bithastighet för att uppnå samma kvalitetsnivå." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Strömvarning" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Bandbreddstestet kommer att strömma slumpad videodata utan ljud till din kanal. Om det fungerar, är det rekommenderat det att tillfälligt inaktivera att spara videor av strömmar och ändra strömmen till privat tills testet är färdigt. Fortsätta?" +Basic.AutoConfig.TestPage="Slutgiltiga resultat" +Basic.AutoConfig.TestPage.SubTitle.Testing="Programmet utför nu en grupp tester för att uppskatta de mest idealiska inställningarna" +Basic.AutoConfig.TestPage.SubTitle.Complete="Testet slutfört" +Basic.AutoConfig.TestPage.TestingBandwidth="Utför bandbreddstest, detta kan ta några minuter..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Ansluter till: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Misslyckades att ansluta till någon server, kontrollera din Internetanslutning och försök igen." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Testar bandbredden för: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Testar strömkodare, detta kan ta några minuter..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Testar inspelningskodare, detta kan ta några minuter..." +Basic.AutoConfig.TestPage.TestingRes="Testar upplösningar, detta kan ta några minuter..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Misslyckades att starta kodaren" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Testar %1x%2 i %3 bilder per sekund..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Strömningskodare" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Inspelningskodare" +Basic.AutoConfig.TestPage.Result.Header="Programmet har bestämt att dessa uppskattade inställningar är de mest idealiska för dig:" +Basic.AutoConfig.TestPage.Result.Footer="För att använda dessa inställningar, klicka på Verkställ inställningar. För att omkonfigurera guiden och försöka igen, klicka på Tillbaka. För att konfigurera inställningarna manuellt, klicka på Avbryt och öppna Inställningar." + +Basic.Stats="Statistik" +Basic.Stats.CPUUsage="CPU-användning" +Basic.Stats.HDDSpaceAvailable="Tillgängligt hårddiskutrymme" +Basic.Stats.MemoryUsage="Minnesanvändning" +Basic.Stats.AverageTimeToRender="Medeltid för att rendera bildruta" +Basic.Stats.SkippedFrames="Bildrutor som hoppades över p.g.a. kodningslagg" +Basic.Stats.MissedFrames="Bildrutor som saknas p.g.a. renderingslagg" +Basic.Stats.Output.Stream="Ström" +Basic.Stats.Output.Recording="Inspelning" +Basic.Stats.Status="Status" +Basic.Stats.Status.Recording="Spelar in" +Basic.Stats.Status.Live="SÄNDER" +Basic.Stats.Status.Reconnecting="Återansluter" +Basic.Stats.Status.Inactive="Inaktiv" +Basic.Stats.DroppedFrames="Tappade bildrutor (nätverk)" +Basic.Stats.MegabytesSent="Total datautmatning" +Basic.Stats.Bitrate="Bithastighet" Updater.Title="Ny uppdatering tillgänglig" Updater.Text="Det finns en ny uppdatering tillgänglig:" @@ -375,6 +462,7 @@ Basic.Settings.General="Allmänt" Basic.Settings.General.Theme=" Tema" Basic.Settings.General.Language="Språk" Basic.Settings.General.EnableAutoUpdates="Sök efter uppdateringar automatiskt vid start" +Basic.Settings.General.OpenStatsOnStartup="Öppna statistikfönstret vid uppstart" Basic.Settings.General.WarnBeforeStartingStream="Visa bekräftelsedialog när ström startas" Basic.Settings.General.WarnBeforeStoppingStream="Visa bekräftelsedialog när ström stoppas" Basic.Settings.General.Projectors="Projektorer" @@ -492,15 +580,15 @@ Basic.Settings.Video="Video" Basic.Settings.Video.Adapter="Grafikkort:" Basic.Settings.Video.BaseResolution="Grundupplösning (kanvas):" Basic.Settings.Video.ScaledResolution="Utdataupplösning (skalad):" -Basic.Settings.Video.DownscaleFilter="Nedskalningsfilter:" +Basic.Settings.Video.DownscaleFilter="Nedskalningsfilter" Basic.Settings.Video.DisableAeroWindows="Inaktivera Aero (endast Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Vanliga bildhastighetsvärden" Basic.Settings.Video.FPSInteger="Heltals-bildhastighetsvärde" Basic.Settings.Video.FPSFraction="Decimaltals-bildhastighetsvärde" -Basic.Settings.Video.Numerator="Täljare:" -Basic.Settings.Video.Denominator="Nämnare:" -Basic.Settings.Video.Renderer="Renderare:" +Basic.Settings.Video.Numerator="Täljare" +Basic.Settings.Video.Denominator="Nämnare" +Basic.Settings.Video.Renderer="Renderare" Basic.Settings.Video.InvalidResolution="Ogiltig upplösning. Måste anges som [bredd]x[höjd] (t.ex 1920x1080)" Basic.Settings.Video.CurrentlyActive="Videoutmatning är aktiv. Stoppa alla utmatningar för att kunna ändra videoinställningar." Basic.Settings.Video.DisableAero="Inaktivera Aero" @@ -538,6 +626,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Partiell" Basic.Settings.Advanced.Video.ColorRange.Full="Full" Basic.Settings.Advanced.Audio.MonitoringDevice="Ljuduppspelningsenhet" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Standard" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Inaktivera audio ducking i Windows" Basic.Settings.Advanced.StreamDelay="Strömfördröjning" Basic.Settings.Advanced.StreamDelay.Duration="Varaktighet (sekunder)" Basic.Settings.Advanced.StreamDelay.Preserve="Behåll stoppunkten (öka fördröjningen) vid återanslutning" @@ -616,3 +705,6 @@ OutputWarnings.NoTracksSelected="Du måste välja minst ett spår" OutputWarnings.MultiTrackRecording="Varning: En del format (t.ex. FLV) stöder inte flera spår för varje inspelning" OutputWarnings.MP4Recording="Varning: Inspelningar som sparas som MP4 kommer inte att kunna återställas om filen inte kan slutföras (d.v.s. om en datorkrasch, strömavbrott, etc. skulle inträffa). Om du vill spela in flera ljudspår, överväg att använda MKV och remuxa inspelningen till mp4 när den är färdig (Arkiv->Remuxa inspelningar)" +FinalScene.Title="Radera scen" +FinalScene.Text="Det måste finnas minst en scen." + diff --git a/UI/data/locale/ta-IN.ini b/UI/data/locale/ta-IN.ini index 8a8cce8..28db817 100644 --- a/UI/data/locale/ta-IN.ini +++ b/UI/data/locale/ta-IN.ini @@ -87,6 +87,13 @@ New="புதிய" + + + + + + + diff --git a/UI/data/locale/th-TH.ini b/UI/data/locale/th-TH.ini index 0d149d4..599ea64 100644 --- a/UI/data/locale/th-TH.ini +++ b/UI/data/locale/th-TH.ini @@ -44,6 +44,12 @@ Enable="เปิดใช้งาน" + + + + + + ConfirmRemove.Title="ยืนยันการลบ" ConfirmRemove.Text="คุณแน่ใจแล้วหรือที่จะลบ '$1'?" @@ -124,7 +130,7 @@ Basic.Settings.Stream.StreamType="รูปแบบการสตรีม" Basic.Settings.Video="วีดีโอ" Basic.Settings.Video.DisableAeroWindows="ปิดการใช้งาน Aero (เฉพาะ Windows เท่านั้น)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Audio="เสียง" @@ -139,3 +145,4 @@ Basic.Settings.Audio="เสียง" + diff --git a/UI/data/locale/tr-TR.ini b/UI/data/locale/tr-TR.ini index c33cad1..14a07b6 100644 --- a/UI/data/locale/tr-TR.ini +++ b/UI/data/locale/tr-TR.ini @@ -28,9 +28,12 @@ Browse="Gözat" Mono="Mono" Stereo="Stereo" DroppedFrames="Kaybedilen Kareler %1 (%2%)" -PreviewProjector="Tam Ekran Yansıtması (Önizleme)" -SceneProjector="Tam Ekran Yansıtması (Sahne)" -SourceProjector="Tam Ekran Yansıtması (Kaynak)" +PreviewProjector="Tam Ekran Projektör (Önizleme)" +SceneProjector="Tam Ekran Projektör (Sahne)" +SourceProjector="Tam Ekran Projektör (Kaynak)" +PreviewWindow="Pencereli Projektör (Önizleme)" +SceneWindow="Pencereli Projektör (Sahne)" +SourceWindow="Pencereli Projektör (Kaynak)" Clear="Temizle" Revert="Eski Haline Döndür" Show="Göster" @@ -39,7 +42,7 @@ Untitled="İsimsiz" New="Yeni" Duplicate="Çoğalt" Enable="Etkinleştir" -DisableOSXVSync="OSX V-Sync Devre Dışı Bırak" +DisableOSXVSync="OSX V-Sync'i Devre Dışı Bırak" ResetOSXVSyncOnExit="OSX V-Sync'i Çıkışta Sıfırla" HighResourceUsage="Kodlama aşırı yüklendi! Video ayarlarını kapatmayı veya daha hızlı bir kodlama ön ayarını kullanmayı düşünün." Transition="Geçiş" @@ -56,6 +59,92 @@ Deprecated="Kullanım dışı" ReplayBuffer="Tekrar Oynatma Arabelleği" Import="İçe Aktar" Export="Dışa Aktar" +Copy="Kopyala" +Paste="Yapıştır" +PasteReference="Yapıştır (Referans)" +PasteDuplicate="Yapıştır (Çoğalt)" +RemuxRecordings="Remux Kayıtları" +Next="İleri" +Back="Geri" + +AlreadyRunning.Title="OBS zaten çalışıyor" +AlreadyRunning.Text="OBS zaten çalışıyor! Bunu yapmak istemediyseniz, lütfen yeni bir örneği çalıştırmayı denemeden önce varolan tüm OBS örneklerini kapatın. OBS'yi sistem tablasına küçülmesi için ayarladıysanız, lütfen hala çalışıp çalışmadığını görmek için orayı kontrol edin." +AlreadyRunning.LaunchAnyway="Yine de Başlat" + +Copy.Filters="Filtreleri Kopyala" +Paste.Filters="Filtreleri Yapıştır" + +BandwidthTest.Region="Bölge" +BandwidthTest.Region.US="Amerika Birleşik Devletleri" +BandwidthTest.Region.EU="Avrupa" +BandwidthTest.Region.Asia="Asya" +BandwidthTest.Region.Other="Diğer" + +Basic.FirstStartup.RunWizard="Otomatik yapılandırma sihirbazını çalıştırmak istiyor musunuz? Ana penceredeki Ayarlar düğmesine tıklayarak ayarlarınızı elle de yapılandırabilirsiniz." +Basic.FirstStartup.RunWizard.BetaWarning="(Not: Otomatik yapılandırma sihirbazı şu anda beta'da)" +Basic.FirstStartup.RunWizard.NoClicked="Fikrinizi değiştirirseniz, otomatik yapılandırma sihirbazını istediğiniz zaman Araçlar menüsünden yeniden çalıştırabilirsiniz." + +Basic.AutoConfig="Otomatik Yapılandırma Sihirbazı" +Basic.AutoConfig.Beta="Otomatik Yapılandırma Sihirbazı (Beta)" +Basic.AutoConfig.ApplySettings="Ayarları Uygula" +Basic.AutoConfig.StartPage="Kullanım Bilgileri" +Basic.AutoConfig.StartPage.SubTitle="Programı ne için kullanmak istediğinizi belirtin" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Yayın için en iyi hale getir, kayıt ikinci sırada" +Basic.AutoConfig.StartPage.PrioritizeRecording="Sadece kayıt için en iyi hale getir, yayın yapmayacağım" +Basic.AutoConfig.VideoPage="Video Ayarları" +Basic.AutoConfig.VideoPage.SubTitle="Kullanmak istediğiniz video ayarlarını belirtin" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Şimdikini Kullan (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Ekran %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Şimdikini Kullan (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 ya da 30, ama olabilirse 60'ı tercih et" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 ya da 30, ama yüksek çözünürlüğü tercih et" +Basic.AutoConfig.VideoPage.CanvasExplanation="Not: Tuval (taban) çözünürlüğü yayın veya kayıt yapacağınız çözünürlük ile aynı olmak zorunda değildir. Gerçek yayın/kayıt çözünürlüğünüz kaynak kullanımı ve bit hızı gereksinimlerini düşürmek için tuval çözünürlüğünüzden aşağıya boyutlandırılabilir." +Basic.AutoConfig.StreamPage="Yayın Bilgisi" +Basic.AutoConfig.StreamPage.SubTitle="Lütfen yayın bilginizi girin" +Basic.AutoConfig.StreamPage.Service="Hizmet" +Basic.AutoConfig.StreamPage.Service.ShowAll="Tümünü Göster..." +Basic.AutoConfig.StreamPage.Server="Sunucu" +Basic.AutoConfig.StreamPage.StreamKey="Yayın Anahtarı" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Bağlantı)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Bant genişliği testi ile bit hızını tahmin et (birkaç dakika alabilir)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Donanım kodlamayı tercih et" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Donanım Kodlama çoğu CPU kullanımını ortadan kaldırır, ancak aynı kalite düzeyini elde etmek için daha fazla bit hızı gerektirebilir." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Yayın uyarısı" +Basic.AutoConfig.StreamPage.StreamWarning.Text="Bant genişliği testi kanalınıza ses olmayan rastlantısallaştırılmış video veri yayını yapmak üzere. Yapabiliyorsanız, test bitene kadar geçici bir süre yayınların videosunu kaydetmeyi kapatmanız ve yayını gizli olarak ayarlamanız önerilir. Devam edilsin mi?" +Basic.AutoConfig.TestPage="Sonuçlar" +Basic.AutoConfig.TestPage.SubTitle.Testing="Program en uygun ayarları tahmin etmek için şimdi bir dizi test yürütüyor" +Basic.AutoConfig.TestPage.SubTitle.Complete="Test etme tamamlandı" +Basic.AutoConfig.TestPage.TestingBandwidth="Bant genişliği testi yapılıyor, bu birkaç dakika alabilir..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Bağlanılıyor: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Herhangi bir sunucuya bağlanamıyor, lütfen internet bağlantınızı kontrol edin ve tekrar deneyin." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Bant genişliği test ediliyor: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Yayın kodlayıcı test ediliyor, bu bir dakika alabilir..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Kayıt kodlayıcı test ediliyor, bu bir dakika alabilir..." +Basic.AutoConfig.TestPage.TestingRes="Çözünürlükler test ediliyor, bu birkaç dakika alabilir..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Kodlayıcı başlatılamadı" +Basic.AutoConfig.TestPage.TestingRes.Resolution="%1x%2 %3 FPS test ediliyor..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Yayın Kodlayıcı" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Kayıt Kodlayıcı" +Basic.AutoConfig.TestPage.Result.Header="Program tahmin edilen bu ayarların sizin için en uygunu olduğunu belirledi:" +Basic.AutoConfig.TestPage.Result.Footer="Bu ayarları kullanmak için, Ayarları Uygula'ya tıklayın. Sihirbazı yeniden yapılandırmak ve denemek için Geri'ye tıklayın. Ayarları kendiniz elle yapılandırmak için İptal'e tıklayın ve Ayarlar'ı açın." + +Basic.Stats="İstatistikler" +Basic.Stats.CPUUsage="İşlemci Kullanımı" +Basic.Stats.HDDSpaceAvailable="Mevcut HDD alanı" +Basic.Stats.MemoryUsage="Bellek Kullanımı" +Basic.Stats.AverageTimeToRender="Kare işleme için ortalama süre" +Basic.Stats.SkippedFrames="Kodlama gecikmesi nedeniyle atlanan kareler" +Basic.Stats.MissedFrames="İşleme gecikmesi nedeniyle kaçan kareler" +Basic.Stats.Output.Stream="Yayın" +Basic.Stats.Output.Recording="Kayıt" +Basic.Stats.Status="Durum" +Basic.Stats.Status.Recording="Kaydediliyor" +Basic.Stats.Status.Live="CANLI" +Basic.Stats.Status.Reconnecting="Yeniden bağlanıyor" +Basic.Stats.Status.Inactive="Etkin değil" +Basic.Stats.DroppedFrames="Atlanan Kareler (Ağ)" +Basic.Stats.MegabytesSent="Toplam Veri Çıkışı" +Basic.Stats.Bitrate="Bit Hızı" Updater.Title="Yeni güncelleme mevcut" Updater.Text="Mevcut yeni bir güncelleme var:" @@ -110,17 +199,19 @@ 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?" -Output.StartStreamFailed="Yayın işlemi başarısız oldu" -Output.StartRecordingFailed="Kayıt işlemi başarısız oldu" -Output.StartReplayFailed="Tekrar oynatma arabelleği başarısız oldu" +Output.StartStreamFailed="Yayın başlatılamadı" +Output.StartRecordingFailed="Kayıt başlatılamadı" +Output.StartReplayFailed="Tekrar oynatma arabelleği başlatılamadı" +Output.StartFailedGeneric="Çıkışı başlatma başarısız oldu. Detaylar için lütfen günlüğe bakın: \n\nNVENC veya AMD kodlayıcılarını kullanıyorsanız, video sürücülerinin güncel olduğundan emin olun." Output.ConnectFail.Title="Bağlantı kurulamadı" Output.ConnectFail.BadPath="Bağlantı adresiniz geçersiz. Ayarlarınızı kontrol edin ve geçerli bir adres giriniz." Output.ConnectFail.ConnectFailed="Sunucuya bağlanılamadı" +Output.ConnectFail.InvalidStream="Belirtilen kanal veya yayın anahtarına erişilemedi, lütfen yayın anahtarınızı iyi kontrol edin. Eğer doğruysa, sunucuya bağlanırken sorun oluyor olabilir." Output.ConnectFail.Error="Sunucuya bağlanmaya çalışırken beklenmeyen bir hata oluştu. Daha fazla bilgi için günlük dosyasına bakınız." Output.ConnectFail.Disconnected="Sunucu bağlantısı kesildi." -Output.RecordFail.Title="Kayıt işlemi başarısız oldu" +Output.RecordFail.Title="Kayıt başlatılamadı" Output.RecordFail.Unsupported="Çıkış biçimi ya desteklenmiyor ya da birden fazla sesi desteklemiyor. Lütfen ayarlarınızı kontrol edip tekrar deneyin." Output.RecordNoSpace.Title="Yetersiz disk alanı" Output.RecordNoSpace.Msg="Kayıt'aa devam etmek yeterli disk alanı yok." @@ -373,6 +464,7 @@ Basic.Settings.General="Genel" Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Dil" Basic.Settings.General.EnableAutoUpdates="Başlangıçta güncellemeleri otomatik olarak kontrol et" +Basic.Settings.General.OpenStatsOnStartup="Başlangıçta istatistikler iletişim kutusunu aç" Basic.Settings.General.WarnBeforeStartingStream="Yayın başlatırken onay iletişim kutusunu göster" Basic.Settings.General.WarnBeforeStoppingStream="Yayın durduğunda onay iletişim kutusunu göster" Basic.Settings.General.Projectors="Projektörler" @@ -419,8 +511,13 @@ Basic.Settings.Output.Simple.RecordingQuality.Stream="Canlı Yayın ile Aynı" Basic.Settings.Output.Simple.RecordingQuality.Small="Yüksek Kalite, Normal Dosya Boyutu" Basic.Settings.Output.Simple.RecordingQuality.HQ="Aynı Kaliteye Yakın, Büyük Dosya Boyutu" Basic.Settings.Output.Simple.RecordingQuality.Lossless="Kayıpsız Kalite, Çok Büyük Dosya Boyutu" +Basic.Settings.Output.Simple.Warn.VideoBitrate="Uyarı: Yayın video bit hızı %1 olarak ayarlanacak, bu şu anki yayın hizmeti için üst sınırdır. Eğer %1 değerinin üstüne çıkmak istediğinizden eminseniz, gelişmiş kodlayıcı seçeneklerini etkinleştirin ve \"Yayın hizmetini bit hızı sınırlarına zorla\" işaretini kaldırın." +Basic.Settings.Output.Simple.Warn.AudioBitrate="Uyarı: Yayın audio bit hızı %1 olarak ayarlanacak, bu şu anki yayın hizmeti için üst sınırdır. Eğer %1 değerinin üstüne çıkmak istediğinizden eminseniz, gelişmiş kodlayıcı seçeneklerini etkinleştirin ve \"Yayın hizmetini bit hızı sınırlarına zorla\" işaretini kaldırın." +Basic.Settings.Output.Simple.Warn.Encoder="Uyarı: Bir yazılım kodlayıcı ile yayın kalitesinden farklı kayıt yapmak eğer aynı anda hem kayıt hem de yayın yapıyorsanız ilave CPU kullanımı gerektirecektir." +Basic.Settings.Output.Simple.Warn.Lossless="Uyarı: Kayıpsız kalite muazzam büyük dosya boyutları oluşturur! Kayıpsız kalite, yüksek çözünürlüklerde ve kare hızlarında, dakikada 7 gigabyte'a kadar disk alanı kullanabilir. Kayıpsız, kullanılabilir disk alanınız çok büyük değilse, uzun kayıtlar için tavsiye edilmez." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Kayıpsız kalitede kullanmak istediğinizden emin misiniz?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Kayıpsız kalite uyarısı!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="Uyarı: Aynı anda yayın ve kayıt yaparken çoklu ayrı QSV kodlayıcı kullanamazsınız. Eğer aynı anda yayın ve kayıt yapmak istiyorsanız, lütfen kayıt kodlayıcısını yada yayın kodlayıcısını değiştirin." Basic.Settings.Output.Simple.Encoder.Software="Yazılım (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Donanım (QSV)" Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Donanım (AMD)" @@ -468,7 +565,7 @@ Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Varsayılan Biçim" Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Kapsayıcı Biçimi Açıklaması" Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Dosya yolu veya URL'den tahmini Video/Ses Kodeksi" Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Varsayılan Kodlayıcı" -Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Kodlayıcı Devredışı" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Kodlayıcıyı Devre Dışı Bırak" Basic.Settings.Output.Adv.FFmpeg.VEncoder="Video Kodlayıcı" Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Video Kodlayıcı Ayarları (var ise)" Basic.Settings.Output.Adv.FFmpeg.AEncoder="Ses Kodlayıcı" @@ -482,18 +579,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY Yıl, dört hane\n%YY Yıl, son iki hane (00-99)\n%MM Ay rakamla (01-12)\n%DD Ayın günü, önü-sıfırlı (01-31)\n%hh Saat 24s formatlı (00-23)\n%mm Dakika (00-59)\n%ss Saniye (00-61)\n%% A % sign\n%a Kısaltılmış gün adı\n%A Tam gün adı\n%b Kısaltılmış ay adı\n%B Tam ay adı\n%d Ayın günü, önü-sıfırlı (01-31)\n%H Saat 24s formatlı (00-23)\n%I Saat 12s formatlı (01-12)\n%m Ay rakamla (01-12)\n%M Dakika (00-59)\n%p AM veya PM belirteci\n%S Saniye (00-61)\n%y Yıl, son iki hane (00-99)\n%Y Yıl\n%z UTC'ye göre ISO 8601 farkı veya zaman dilimi\n adı veya kısaltması\n%Z Zaman dilimi adı veya kısaltması\n" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Video Bağdaştırıcı:" -Basic.Settings.Video.BaseResolution="Temel (Tuval) Çözünürlüğü:" -Basic.Settings.Video.ScaledResolution="Çıkış (Boyutlandırılmış) Çözünürlüğü:" -Basic.Settings.Video.DownscaleFilter="Boyut Azaltma Filtresi:" +Basic.Settings.Video.Adapter="Video Bağdaştırıcı" +Basic.Settings.Video.BaseResolution="Temel (Tuval) Çözünürlüğü" +Basic.Settings.Video.ScaledResolution="Çıkış (Boyutlandırılmış) Çözünürlüğü" +Basic.Settings.Video.DownscaleFilter="Boyut Azaltma Filtresi" Basic.Settings.Video.DisableAeroWindows="Aero'yu Devre Dışı Bırak (yalnızca Windows)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Ortak FPS Değerleri" Basic.Settings.Video.FPSInteger="Tamsayı FPS değeri" Basic.Settings.Video.FPSFraction="Kesirli FPS değeri" -Basic.Settings.Video.Numerator="Pay:" -Basic.Settings.Video.Denominator="Payda:" -Basic.Settings.Video.Renderer="Yorumlayıcı:" +Basic.Settings.Video.Numerator="Pay" +Basic.Settings.Video.Denominator="Payda" +Basic.Settings.Video.Renderer="İşleyici" Basic.Settings.Video.InvalidResolution="Geçersiz çözünürlük değeri. [Genişlik]x[Yükseklik] şeklinde olmalıdır. (örnek 1920x1080)" Basic.Settings.Video.CurrentlyActive="Video çıkışı şu anda etkin durumda. Video ayarlarını değiştirmek için lütfen bütün çıkışları kapalı duruma getirin." Basic.Settings.Video.DisableAero="Aero'yu Devre Dışı Bırak" @@ -531,6 +628,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Kısmi" Basic.Settings.Advanced.Video.ColorRange.Full="Tam" Basic.Settings.Advanced.Audio.MonitoringDevice="Ses İzleme Aygıtı" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Varsayılan" +Basic.Settings.Advanced.Audio.DisableAudioDucking="Windows ses alçaltmasını devre dışı bırak" Basic.Settings.Advanced.StreamDelay="Yayın Gecikmesi" Basic.Settings.Advanced.StreamDelay.Duration="Süre (saniye)" Basic.Settings.Advanced.StreamDelay.Preserve="Tatbik ederken kesim noktasını (gecikme artışı) koru" @@ -610,3 +708,6 @@ OutputWarnings.NoTracksSelected="En az bir ses parçası seçmelisiniz" OutputWarnings.MultiTrackRecording="Uyarı: Bazı biçimler (FLV gibi) kayıt başına birden fazla parçayı desteklemez" OutputWarnings.MP4Recording="Uyarı: MP4'e kaydedilen kayıtlar eğer dosya sonlandırılamazsa kurtarılamaz (örneğin: Mavi Hata Ekranı, güç kesintisi v.b.). Eğer çoklu ses izi kaydetmek istiyorsanız MKV kullanmayı düşünün ve bittikten sonra sonra kayıtı mp4'e remuxlayabilirsiniz (Dosya->Remux Kayıtları)" +FinalScene.Title="Sahneyi Sil" +FinalScene.Text="En az bir sahne olması gerekiyor." + diff --git a/UI/data/locale/uk-UA.ini b/UI/data/locale/uk-UA.ini index 073f921..9770894 100644 --- a/UI/data/locale/uk-UA.ini +++ b/UI/data/locale/uk-UA.ini @@ -31,6 +31,9 @@ DroppedFrames="Пропущено кадрів %1 (%2%)" PreviewProjector="Повноекранний Проектор (вікно Перегляду)" SceneProjector="Повноекранний Проектор (Сцена)" SourceProjector="Повноекранний Проектор (Джерело)" +PreviewWindow="Віконний Проектор (вікно Перегляду)" +SceneWindow="Віконний Проектор (Сцена)" +SourceWindow="Віконний Проектор (Джерело)" Clear="Очистити" Revert="Відмінити" Show="Показати" @@ -56,8 +59,94 @@ Deprecated="Припинено підтримку" ReplayBuffer="Буфер Повторів" Import="Імпорт" Export="Експорт" +Copy="Копіювати" +Paste="Вставити" +PasteReference="Встивити (як вже існуюче)" +PasteDuplicate="Вставити (як повну копію)" +RemuxRecordings="Ремультиплексація Записів" +Next="Далі" +Back="Назад" -Updater.Title="Доступне оновлення" +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="Майстер з автонастроювання (бета-версія)" +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 Кадр/сек..." +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="Використання ЦП" +Basic.Stats.HDDSpaceAvailable="HDD, вільне місце" +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="Нагадати пізніше" @@ -375,6 +464,7 @@ 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="Проектор" @@ -489,18 +579,18 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss 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 ДП або ПП позначення\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.Adapter="Відеокарта" +Basic.Settings.Video.BaseResolution="Роздільна здатність (Полотно)" +Basic.Settings.Video.ScaledResolution="Роздільна здатність (Вивід)" +Basic.Settings.Video.DownscaleFilter="Фільтр масштабування" Basic.Settings.Video.DisableAeroWindows="Вимкнути Aero (лише для Windows)" -Basic.Settings.Video.FPS="Кадрів в секунду:" +Basic.Settings.Video.FPS="Кадрів в секунду" Basic.Settings.Video.FPSCommon="Кадр/сек, загальні значення" Basic.Settings.Video.FPSInteger="Кадр/сек, цілі значення" Basic.Settings.Video.FPSFraction="Кадр/сек, дробові значення" -Basic.Settings.Video.Numerator="Чисельник:" -Basic.Settings.Video.Denominator="Знаменник:" -Basic.Settings.Video.Renderer="Візуалізація:" +Basic.Settings.Video.Numerator="Чисельник" +Basic.Settings.Video.Denominator="Знаменник" +Basic.Settings.Video.Renderer="Візуалізація" Basic.Settings.Video.InvalidResolution="Неприпустиме значення роздільної здатності. Повинно бути [ширина]x[висота] (тобто, 1920x1080)" Basic.Settings.Video.CurrentlyActive="Вивід відео в даний час активний. Будь ласка, зупинить весь вивід відео, щоб змінити настройки відео." Basic.Settings.Video.DisableAero="Вимкнути Aero" @@ -538,6 +628,7 @@ 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="Зберегати точку роз'єднання (збільшує затримку) під час встановлення нового зв'язку" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="Ви повинні вибрати хоча б OutputWarnings.MultiTrackRecording="Попередження: Певні формати (наприклад, FLV) не підтримують кілька треків на запис" OutputWarnings.MP4Recording="Попередження: Запис в MP4 може стати невиправно пошкодженим, якщо файл не буде завершено (наприклад, в результаті BSOD, втрати живлення і т.п.). Якщо ви хочете, мати запис декількох звукових доріжок спробуйте використати MKV та зробіть ремультиплексацію запису до mp4 після того, як запис буде закінчено (Файл->Ремультиплексація Записів)" +FinalScene.Title="Видалення сцени" +FinalScene.Text="Повинна бути принаймні одна сцена." + diff --git a/UI/data/locale/vi-VN.ini b/UI/data/locale/vi-VN.ini index dcd63c8..179866c 100644 --- a/UI/data/locale/vi-VN.ini +++ b/UI/data/locale/vi-VN.ini @@ -52,11 +52,111 @@ Reset="Thiết lập lại" Hours="Giờ" Minutes="Phút" Seconds="Giây" +Deprecated="Dừng cập nhật" +ReplayBuffer="Replay Buffer" +Import="Nhập" +Export="Xuất" +Copy="Chép" +Paste="Dán" +PasteReference="Dán (Tham khảo)" +PasteDuplicate="Dán (Bản sao)" +RemuxRecordings="Remux video" +Next="Tiếp tục" +Back="Quay lại" +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." +AlreadyRunning.LaunchAnyway="Khởi động luôn" + +Copy.Filters="Sao chép các bộ lọc" +Paste.Filters="Dán các bộ lọc" + +BandwidthTest.Region="Khu vực" +BandwidthTest.Region.US="Hoa Kỳ" +BandwidthTest.Region.EU="Châu Âu" +BandwidthTest.Region.Asia="Châu Á" +BandwidthTest.Region.Other="Khác" + +Basic.FirstStartup.RunWizard="Bạn có muốn chạy trình cấu hình tự động? Bạn có thể cấu hình bằng tay cài đặt của bạn bằng cách nhấn vào nút Cài đặt trong cửa sổ chính." +Basic.FirstStartup.RunWizard.BetaWarning="(Lưu ý: Trình cấu hình tự động đang trong phiên bản beta)" +Basic.FirstStartup.RunWizard.NoClicked="Nếu bạn thay đổi, bạn có thể chạy trình cấu hình tự động bất kỳ lúc nào từ menu Công cụ." + +Basic.AutoConfig="Trình cấu hình tự động" +Basic.AutoConfig.Beta="Trình cấu hình tự động (Beta)" +Basic.AutoConfig.ApplySettings="Áp dụng các thiết lập" +Basic.AutoConfig.StartPage="Thông tin sử dụng" +Basic.AutoConfig.StartPage.SubTitle="Xác định bạn muốn sử dụng chương trình cho mục đích gì" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Tối ưu hóa cho streaming, quay phim là thứ cấp" +Basic.AutoConfig.StartPage.PrioritizeRecording="Tối ưu hóa chỉ cho quay phim, tôi sẽ không streaming" +Basic.AutoConfig.VideoPage="Cài đặt video" +Basic.AutoConfig.VideoPage.SubTitle="Chỉ rõ các thiết lập video bạn muốn sử dụng" +Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Sử dụng hiện tại (%1x%2)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Màn hình %1 (%2x%3)" +Basic.AutoConfig.VideoPage.FPS.UseCurrent="Sử dụng hiện tại (%1)" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 hoặc 30, nhưng ưu tiên chọn 60 khi có thể" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 hoặc 30, nhưng ưu tiên độ phân giải cao" +Basic.AutoConfig.StreamPage="Thông tin stream" +Basic.AutoConfig.StreamPage.SubTitle="Vui lòng nhập thông tin máy chủ stream của bạn" +Basic.AutoConfig.StreamPage.Service="Dịch vụ" +Basic.AutoConfig.StreamPage.Service.ShowAll="Hiện tất cả..." +Basic.AutoConfig.StreamPage.Server="Máy chủ" +Basic.AutoConfig.StreamPage.StreamKey="Khóa stream" +Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Liên kết)" +Basic.AutoConfig.StreamPage.PerformBandwidthTest="Ước tính bitrate với thử nghiệm băng thông (có thể mất vài phút)" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Ưu tiên mã hóa bằng phần cứng" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Mã hóa bằng phần cứng giúp loại bỏ hầu hết sử dụng CPU nhưng có thể yêu cầu thêm bitrate để có được chất lượng tương đương." +Basic.AutoConfig.StreamPage.StreamWarning.Title="Cảnh báo stream" +Basic.AutoConfig.TestPage="Kết quả cuối cùng" +Basic.AutoConfig.TestPage.SubTitle.Complete="Thử nghiệm hoàn tất" +Basic.AutoConfig.TestPage.TestingBandwidth="Đang kiểm tra băng thông, điều này có thể mất vài phút..." +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Kết nối tới: %1..." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Không thể kết nối tới tất cả các máy chủ. Vui lòng kiểm tra kết nối và thử lại." +Basic.AutoConfig.TestPage.TestingBandwidth.Server="Thử nghiệm băng thông cho: %1" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Thử mã hóa stream, điều này có thể mất vài phút..." +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Thử mã hóa quay video, điều này có thể mất vài phút..." +Basic.AutoConfig.TestPage.TestingRes="Kiểm tra độ phân giải, điều này có thể mất vài phút..." +Basic.AutoConfig.TestPage.TestingRes.Fail="Thất bại trong việc bắt đầu trình mã hóa" +Basic.AutoConfig.TestPage.TestingRes.Resolution="Thử nghiệm %1x%2 %3 FPS..." +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Mã hóa streaming" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Mã hóa quay video" +Basic.AutoConfig.TestPage.Result.Header="Chương trình đã xác định được cài đặt lý tưởng nhất dành cho bạn:" +Basic.AutoConfig.TestPage.Result.Footer="Để sử dụng các thiết lập này, nhấp vào Áp dụng cài đặt. Để cấu hình lại và thử lại, bấm Lùi lại. Để cấu hình thiết lập bằng tay, bấm Hủy bỏ và mở Cài đặt." + +Basic.Stats="Thống kê" +Basic.Stats.CPUUsage="Sử dụng CPU" +Basic.Stats.HDDSpaceAvailable="Không gian ổ cứng còn trống" +Basic.Stats.MemoryUsage="Bộ nhớ đã sử dụng" +Basic.Stats.AverageTimeToRender="Thời gian trung bình để vẽ hình" +Basic.Stats.SkippedFrames="Khung hình bị bỏ qua do mã hóa bị lag" +Basic.Stats.MissedFrames="Khung hình bị mất do mã hóa bị lag" +Basic.Stats.Output.Stream="Stream" +Basic.Stats.Output.Recording="Quay video" +Basic.Stats.Status="Trạng thái" +Basic.Stats.Status.Recording="Đang quay" +Basic.Stats.Status.Live="TRỰC TIẾP" +Basic.Stats.Status.Reconnecting="Đang kết nối lại" +Basic.Stats.Status.Inactive="Không hoạt động" +Basic.Stats.DroppedFrames="Khung hình bị rớt (Mạng)" +Basic.Stats.MegabytesSent="Tổng dung lượng" +Basic.Stats.Bitrate="Bitrate" + +Updater.Title="Có bản cập nhật mới" +Updater.Text="Có sẵn một bản cập nhật mới:" +Updater.UpdateNow="Cập nhật ngay" +Updater.RemindMeLater="Hỏi lại sau" +Updater.Skip="Bỏ qua phiên bản" +Updater.Running.Title="Chương trình đang kích hoạt" +Updater.Running.Text="Đầu ra đang được kích hoạt, vui lòng tắt bất kỳ đầu ra nào đang hoạt động trước khi bấm Cập nhật" +Updater.NoUpdatesAvailable.Title="Không có cập nhật mới" +Updater.NoUpdatesAvailable.Text="Không có bản cập nhật mới" +Updater.FailedToLaunch="Thất bại khi mở trình cập nhật" +Updater.GameCaptureActive.Title="Quay video đang hoạt động" +Updater.GameCaptureActive.Text="Thư viện móc quay game hiện đang hoạt động. Xin vui lòng đóng tất cả các game/chương trình hiện đang quay (hoặc khởi động lại Windows) và thử lại." QuickTransitions.SwapScenes="Hoán đổi cảnh Xem trước/Đầu ra sau khi Chuyển cảnh" QuickTransitions.SwapScenesTT="Hoán đổi cảnh xem trước và cảnh đầu ra sau khi chuyển cảnh (nếu cảnh đầu ra gốc vẫn tồn tại).\nMọi thay đổi với cảnh đầu ra gốc sẽ không hoàn tác." QuickTransitions.DuplicateScene="Tạo bản sao cảnh" +QuickTransitions.EditProperties="Bản sao nguồn" QuickTransitions.HotkeyName="C. cảnh nhanh: %1" Basic.AddTransition="Thêm cấu hình chuyển cảnh" @@ -89,6 +189,10 @@ ConfirmRemove.Title="Xác nhận loại bỏ" ConfirmRemove.Text="Bạn có chắc bạn muốn loại bỏ '$1' không?" ConfirmRemove.TextMultiple="Bạn có chắc bạn muốn xóa %1 nội dung không?" +Output.StartStreamFailed="Không thể bắt đầu streaming" +Output.StartRecordingFailed="Không thể bắt đầu quay video" +Output.StartReplayFailed="Không thể khởi động replay buffer" +Output.StartFailedGeneric="Bắt đầu đầu ra đã thất bại. Vui lòng kiểm tra các bản ghi để biết thêm chi tiết.\n\nGhi chú: nếu bạn đang sử dụng bộ mã hóa NVENC hoặc AMD, hãy chắc chắn rằng các trình điều khiển GPU được cập nhật phiên bản mới nhất." Output.ConnectFail.Title="Không thể kết nối" Output.ConnectFail.BadPath="URL không hợp lệ của đường dẫn hoặc kết nối. Xin vui lòng kiểm tra cài đặt của bạn để xác nhận rằng họ là hợp lệ." @@ -103,6 +207,8 @@ Output.RecordNoSpace.Title="Không gian đĩa không đủ" Output.RecordNoSpace.Msg="Không còn đủ không gian đĩa để tiếp tục ghi âm." Output.RecordError.Title="Lỗi ghi âm" Output.RecordError.Msg="Một lỗi không xác định xảy ra trong khi ghi âm." +Output.ReplayBuffer.NoHotkey.Title="Không có hotkey!" +Output.ReplayBuffer.NoHotkey.Msg="Không hotkey lưu replay buffer. Vui lòng đặt hotkey \"Lưu\" để sử dụng để lưu video." Output.BadPath.Title="Đường dẫn tệp xấu" Output.BadPath.Text="Đường dẫn đầu ra của cấu hình tệp không hợp lệ. Xin vui lòng kiểm tra cài đặt của bạn để xác nhận rằng một đường dẫn hợp lệ tập tin đã được thiết lập." @@ -162,6 +268,8 @@ Deinterlacing.Linear2x="Linear 2x" Deinterlacing.Yadif="Yadif" Deinterlacing.Yadif2x="Yadif 2x" +VolControl.Mute="Tắt tiếng '%1'" +VolControl.Properties="Thuộc tính '%1'" Basic.Main.AddSceneDlg.Title="Thêm cảnh" Basic.Main.AddSceneDlg.Text="Vui lòng nhập tên của cảnh" @@ -173,10 +281,10 @@ Basic.Main.AddSceneCollection.Text="Vui lòng nhập tên của bộ sưu tập Basic.Main.RenameSceneCollection.Title="Đổi tên cảnh bộ sưu tập" -AddProfile.Title="Thêm hồ sơ" +AddProfile.Title="Thêm cấu hình" AddProfile.Text="Vui lòng nhập tên cấu hình" -RenameProfile.Title="Đổi tên hồ sơ" +RenameProfile.Title="Đổi tên cấu hình" Basic.Main.PreviewDisabled="Xem trước hiện đang vô hiệu hoá" @@ -257,9 +365,12 @@ Basic.Main.Scenes="Cảnh" Basic.Main.Sources="Nguồn" Basic.Main.Connecting="Đang kết nối..." Basic.Main.StartRecording="Bắt đầu ghi" +Basic.Main.StartReplayBuffer="Bắt đầu Replay Buffer" Basic.Main.StartStreaming="Bắt đầu Streaming" Basic.Main.StopRecording="Dừng ghi" Basic.Main.StoppingRecording="Dừng ghi video..." +Basic.Main.StopReplayBuffer="Dừng Replay Buffer" +Basic.Main.StoppingReplayBuffer="Đang dừng Replay Buffer..." Basic.Main.StopStreaming="Ngừng Streaming" Basic.Main.StoppingStreaming="Dừng stream..." Basic.Main.ForceStopStreaming="Ngừng Streaming (huỷ chậm trễ)" @@ -271,7 +382,7 @@ Basic.MainMenu.File.ShowRecordings="Hiển thị &Bản ghi" Basic.MainMenu.File.Remux="Re&mux bản ghi" Basic.MainMenu.File.Settings="&Cài đặt" Basic.MainMenu.File.ShowSettingsFolder="Hiển thị thư mục cài đặt" -Basic.MainMenu.File.ShowProfileFolder="Hiển thị thư mục hồ sơ" +Basic.MainMenu.File.ShowProfileFolder="Hiển thị thư mục cấu hình" Basic.MainMenu.AlwaysOnTop="&Luôn trên đầu trang" Basic.MainMenu.File.Exit="&Thoát" @@ -281,6 +392,9 @@ Basic.MainMenu.Edit.Redo="&Làm lại" Basic.MainMenu.Edit.UndoAction="&Hoàn tác $1" Basic.MainMenu.Edit.RedoAction="&Làm lại $1" Basic.MainMenu.Edit.LockPreview="&Khóa xem trước" +Basic.MainMenu.Edit.Scale="Xem trước & co dãn" +Basic.MainMenu.Edit.Scale.Window="Co dãn cửa sổ" +Basic.MainMenu.Edit.Scale.Output="Đầu ra (%1x%2)" Basic.MainMenu.Edit.Transform="&Biến đổi" Basic.MainMenu.Edit.Transform.EditTransform="&Chỉnh sửa biến đổi..." Basic.MainMenu.Edit.Transform.ResetTransform="&Đặt lại biến đổi" @@ -305,7 +419,13 @@ Basic.MainMenu.View.Toolbars.Listboxes="&Listboxes" Basic.MainMenu.View.StatusBar="&Thanh trạng thái" Basic.MainMenu.SceneCollection="& Bộ sưu tập cảnh" -Basic.MainMenu.Profile="&Hồ sơ" +Basic.MainMenu.Profile="&Cấu hình" +Basic.MainMenu.Profile.Import="Nhập cấu hình" +Basic.MainMenu.Profile.Export="Xuất cấu hình" +Basic.MainMenu.SceneCollection.Import="Nhập bộ sưu tập cảnh" +Basic.MainMenu.SceneCollection.Export="Xuất bộ sưu tập cảnh" +Basic.MainMenu.Profile.Exists="Cấu hình này đã tồn tại" +Basic.MainMenu.SceneCollection.Exists="Bộ sưu tập cảnh đã tồn tại" Basic.MainMenu.Tools="&Công cụ" @@ -325,8 +445,19 @@ Basic.Settings.Confirm="Bạn đã lưu thay đổi. Lưu thay đổi?" Basic.Settings.General="Chung" Basic.Settings.General.Theme="Theme" Basic.Settings.General.Language="Ngôn ngữ" +Basic.Settings.General.EnableAutoUpdates="Tự động kiểm tra cập nhật khi khởi động" +Basic.Settings.General.OpenStatsOnStartup="Mở hộp thoại thống kê khi khởi động" +Basic.Settings.General.WarnBeforeStartingStream="Hiển thị hộp thoại xác nhận khi bắt đầu stream" +Basic.Settings.General.WarnBeforeStoppingStream="Hiển thị hộp thoại xác nhận khi dừng lại stream" Basic.Settings.General.HideProjectorCursor="Ẩn con trỏ chuột trên màn chiếu" Basic.Settings.General.ProjectorAlwaysOnTop="Làm cho màn chiếu luôn luôn trên đầu" +Basic.Settings.General.RecordWhenStreaming="Tự động quay video khi stream" +Basic.Settings.General.KeepRecordingWhenStreamStops="Quay tiếp khi dừng stream" +Basic.Settings.General.ReplayBufferWhileStreaming="Tự động bắt đầu replay buffer khi stream" +Basic.Settings.General.KeepReplayBufferStreamStops="Để replay buffer tiếp tục chạy khi dừng stream" +Basic.Settings.General.SysTray="Khay hệ thống" +Basic.Settings.General.SysTrayWhenStarted="Thu nhỏ về khay hệ thống khi bắt đầu" +Basic.Settings.General.SystemTrayHideMinimize="Luôn luôn thu nhỏ về khay hệ thống thay vì thanh tác vụ" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Kiểu Stream" @@ -340,6 +471,13 @@ Basic.Settings.Output.Mode="Đầu ra chế độ" Basic.Settings.Output.Mode.Simple="Dễ sử dụng" Basic.Settings.Output.Mode.Adv="Nâng cao" Basic.Settings.Output.Mode.FFmpeg="FFmpeg đầu ra" +Basic.Settings.Output.UseReplayBuffer="Bật Replay Buffer" +Basic.Settings.Output.ReplayBuffer.SecondsMax="Thời gian phát lại tối đa (Giây)" +Basic.Settings.Output.ReplayBuffer.MegabytesMax="Bộ nhớ tối đa (Megabyte)" +Basic.Settings.Output.ReplayBuffer.Estimate="Ước tính bộ nhớ sử dụng: %1 MB" +Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Không thể ước tính sử dụng bộ nhớ. Xin vui lòng thiết lập giới hạn bộ nhớ tối đa." +Basic.Settings.Output.ReplayBuffer.HotkeyMessage="(Lưu ý: Hãy chắc chắn thiết lập một hotkey cho replay buffer trong phần hotkey)" +Basic.Settings.Output.ReplayBuffer.Suffix="Hậu tố" Basic.Settings.Output.Simple.SavePath="Đường dẫn ghi âm" Basic.Settings.Output.Simple.RecordingQuality="Chất lượng ghi âm" Basic.Settings.Output.Simple.RecordingQuality.Stream="Giống như dòng" @@ -350,6 +488,7 @@ Basic.Settings.Output.Simple.Warn.Encoder="Chú ý: Ghi âm với một phần m Basic.Settings.Output.Simple.Warn.Lossless="Cảnh báo: Lossless chất lượng tạo ra kích thước tập tin rất lớn! Lossless chất lượng có thể sử dụng upward of 7 GB không gian đĩa cho một phút ở độ phân giải cao và framerates. Lossless không nên dùng cho bản ghi âm dài trừ khi bạn có một số lượng rất lớn của không gian đĩa sẵn dùng." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Bạn có chắc bạn muốn sử dụng lossless chất lượng?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless chất lượng cảnh báo!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="Chú ý: Bạn không thể sử dụng nhiều mã hóa QSV khi streaming và ghi âm cùng lúc. Nếu bạn muốn stream và quay cùng lúc, xin vui lòng thay đổi mã hóa quay video hoặc mã hóa stream." Basic.Settings.Output.Simple.Encoder.Software="Phần mềm (x 264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Phần cứng (QSV)" Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Phần cứng (AMD)" @@ -401,22 +540,24 @@ Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Thiết đặt bộ mã hóa Basic.Settings.Output.Adv.FFmpeg.AEncoder="Mã hóa âm thanh" Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Thiết đặt bộ mã hóa video (nếu có)" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxer cài đặt (nếu có)" +Basic.Settings.Output.Adv.FFmpeg.GOPSize="Thời gian đặt Keyframe (giây)" +Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Hiển thị tất cả các bộ mã hóa (ngay cả khi không tương thích)" Basic.Settings.Video="Video" -Basic.Settings.Video.Adapter="Video Adapter:" -Basic.Settings.Video.BaseResolution="Cơ sở (vải) độ phân giải:" -Basic.Settings.Video.ScaledResolution="Độ phân giải (Kích thước) đầu ra:" -Basic.Settings.Video.DownscaleFilter="Downscale lọc:" +Basic.Settings.Video.Adapter="Video Adapter" +Basic.Settings.Video.BaseResolution="Cơ sở (vải) độ phân giải" +Basic.Settings.Video.ScaledResolution="Độ phân giải (Kích thước) đầu ra" +Basic.Settings.Video.DownscaleFilter="Downscale lọc" Basic.Settings.Video.DisableAeroWindows="Vô hiệu hóa hàng không (Windows only)" -Basic.Settings.Video.FPS="FPS:" +Basic.Settings.Video.FPS="FPS" Basic.Settings.Video.FPSCommon="Giá trị phổ biến khung hình/giây" Basic.Settings.Video.FPSInteger="Giá trị số nguyên FPS" Basic.Settings.Video.FPSFraction="Phân đoạn FPS giá trị" -Basic.Settings.Video.Numerator="Tử số:" -Basic.Settings.Video.Denominator="Mẫu số:" -Basic.Settings.Video.Renderer="Renderer:" +Basic.Settings.Video.Numerator="Tử số" +Basic.Settings.Video.Denominator="Mẫu số" +Basic.Settings.Video.Renderer="Renderer" Basic.Settings.Video.InvalidResolution="Giá trị độ phân giải không hợp lệ. Phải là [width]x[height] (tức là năm 1920 x 1080)" Basic.Settings.Video.CurrentlyActive="Đầu ra video là hiện đang hoạt động. Hãy tắt vào bất kỳ kết quả đầu ra để thay đổi thiết đặt video." Basic.Settings.Video.DisableAero="Vô hiệu hóa Aero" @@ -452,12 +593,16 @@ Basic.Settings.Advanced.Video.ColorSpace="Không gian màu YUV" Basic.Settings.Advanced.Video.ColorRange="Phạm vi màu YUV" Basic.Settings.Advanced.Video.ColorRange.Partial="Một phần" Basic.Settings.Advanced.Video.ColorRange.Full="Đầy đủ" +Basic.Settings.Advanced.Audio.MonitoringDevice="Thiết bị giám sát âm thanh" +Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Mặc định" Basic.Settings.Advanced.StreamDelay="Stream trễ" Basic.Settings.Advanced.StreamDelay.Duration="Thời gian (giây)" Basic.Settings.Advanced.StreamDelay.Preserve="Giữ điểm cắt (tăng chậm trễ) khi kết nối lại" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Ước tính bộ nhớ sử dụng: %1 MB" Basic.Settings.Advanced.Network="Mạng" Basic.Settings.Advanced.Network.BindToIP="Liên kết với IP" +Basic.Settings.Advanced.Network.EnableNewSocketLoop="Sử dụng mã mạng mới" +Basic.Settings.Advanced.Network.EnableLowLatencyMode="Chế độ độ trễ thấp" Basic.AdvAudio="Thuộc tính âm thanh nâng cao" Basic.AdvAudio.Name="Tên" @@ -465,6 +610,10 @@ Basic.AdvAudio.Volume="Khối lượng (%)" Basic.AdvAudio.Mono="Downmix để Mono" Basic.AdvAudio.Panning="Panning" Basic.AdvAudio.SyncOffset="Bù đắp đồng bộ (ms)" +Basic.AdvAudio.Monitoring="Giám sát âm thanh" +Basic.AdvAudio.Monitoring.None="Tắt giám sát" +Basic.AdvAudio.Monitoring.MonitorOnly="Chỉ giám sát (không âm thanh ra)" +Basic.AdvAudio.Monitoring.Both="Giám sát và ra âm thanh" Basic.AdvAudio.AudioTracks="Bài hát" Basic.Settings.Hotkeys="Phím nóng" @@ -523,4 +672,8 @@ SceneItemHide="Ẩn '%1'" OutputWarnings.NoTracksSelected="Bạn phải chọn ít nhất một track" OutputWarnings.MultiTrackRecording="Chú ý: Một số định dạng (chẳng hạn như FLV) không hỗ trợ nhiều track cho mỗi video" +OutputWarnings.MP4Recording="Chú ý: các bản ghi âm được lưu ở dạng MP4 sẽ không thể phục hồi nếu các tập tin không thể được hoàn thành (ví dụ như bị màn hình xanh, mất điện, vv.). Nếu bạn muốn ghi nhiều âm thanh thì xem xét sử dụng MKV và remux video sang mp4 sau khi hoàn tất (Tập tin -> Remux video)" + +FinalScene.Title="Xóa cảnh" +FinalScene.Text="Cần có ít nhất một cảnh." diff --git a/UI/data/locale/zh-CN.ini b/UI/data/locale/zh-CN.ini index bc07812..8d3f118 100644 --- a/UI/data/locale/zh-CN.ini +++ b/UI/data/locale/zh-CN.ini @@ -31,6 +31,9 @@ DroppedFrames="丢帧 %1 (%2%)" PreviewProjector="全屏投影仪(预览)" SceneProjector="全屏投影仪 (现场)" SourceProjector="全屏投影仪(源)" +PreviewWindow="开窗式投影机 (预览)" +SceneWindow="开窗式投影机 (场景)" +SourceWindow="开窗式投影机 (源)" Clear="清除" Revert="还原" Show="显示" @@ -56,6 +59,92 @@ Deprecated="不推荐使用" ReplayBuffer="重拨缓存" Import="导入" Export="导出" +Copy="复制" +Paste="粘贴" +PasteReference="粘贴(引用)" +PasteDuplicate="粘贴(重复)" +RemuxRecordings="转封装录像" +Next="下一个" +Back="后退" + +AlreadyRunning.Title="OBS 已在运行" +AlreadyRunning.Text="OBS 已经在运行! 除非你想要这样做, 请在你运行一个新的 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="(注意: 自动配置向导目前处于 beta 阶段)" +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="注意: canvas(base) 分辨率并非与你将要推流或者录像的分辨率相同. 你实际的推流/录像的分辨率可能会从 canvas 的分辨率缩放来减少分辨率使用或者比特率需要." +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="硬件编码降低了大多数的 CPU 使用率, 但可能需要更多的比特率, 来获得同等的质量." +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="有可用的新版本:" @@ -291,12 +380,12 @@ Basic.Main.Sources="来源" Basic.Main.Connecting="连接中..." Basic.Main.StartRecording="开始录制" Basic.Main.StartReplayBuffer="开始回放缓存" -Basic.Main.StartStreaming="开始串流" +Basic.Main.StartStreaming="开始推流" Basic.Main.StopRecording="停止录制" Basic.Main.StoppingRecording="停止录制..." Basic.Main.StopReplayBuffer="停止回放缓存" Basic.Main.StoppingReplayBuffer="正在停止回放缓存..." -Basic.Main.StopStreaming="停止串流" +Basic.Main.StopStreaming="停止推流" Basic.Main.StoppingStreaming="停止推流..." Basic.Main.ForceStopStreaming="停止流 (放弃延迟)" @@ -375,6 +464,7 @@ 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="投影仪" @@ -394,7 +484,7 @@ Basic.Settings.General.SysTrayWhenStarted="开始时最小化到系统托盘" Basic.Settings.General.SystemTrayHideMinimize="总是最小化到系统托盘, 而不是任务栏" Basic.Settings.General.SaveProjectors="退出时保存投影仪" -Basic.Settings.Stream="串流" +Basic.Settings.Stream="流" Basic.Settings.Stream.StreamType="串流类型" Basic.Settings.Output="输出" @@ -489,9 +579,9 @@ FilenameFormatting.completer="%CCYY-%MM-%DD %hh-%mm-%ss\n%YY-%MM-%DD %hh-%mm-%ss FilenameFormatting.TT="%CCYY 年, 4位\n%YY 年, 后两位 (00-99)\n%MM 月份 (01-12)\n%DD 日, 用0补充 (01-31)\n%hh 24小时制小时 (00-23)\n%mm 分钟 (00-59)\n%ss 秒 (00-61)\n%% A % sign\n%a 缩写工作日名称\n%A 完整工作日名称\n%b 缩写月份名称\n%B 完整月份名称\n%d 日, 用0补充 (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 offset from UTC 或者 时区\n 名称或缩写\n%Z 时区名称或缩写\n" Basic.Settings.Video="视频" -Basic.Settings.Video.Adapter="视频适配器:" -Basic.Settings.Video.BaseResolution="基础 (Canvas) 分辨率:" -Basic.Settings.Video.ScaledResolution="输出 (缩放) 分辨率:" +Basic.Settings.Video.Adapter="视频适配器" +Basic.Settings.Video.BaseResolution="基础 (Canvas) 分辨率" +Basic.Settings.Video.ScaledResolution="输出 (缩放) 分辨率" Basic.Settings.Video.DownscaleFilter="缩放过滤器" Basic.Settings.Video.DisableAeroWindows="禁用 Aero (仅限 Windows)" Basic.Settings.Video.FPS="每秒帧数:" @@ -538,6 +628,7 @@ 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="重新连接时保持截止点 (增加延迟)" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="您必须选择至少一个轨道" OutputWarnings.MultiTrackRecording="警告: 某些格式 (如 FLV) 不支持每个录像多个轨道" OutputWarnings.MP4Recording="警告︰ 录制保存到 MP4 将无法恢复,如果该文件不能完成 (例如由于蓝屏死机,掉电等)。如果您想要记录多个音频轨道考虑使用 MKV 然后在它完成后 remux 录制到 mp4 (文件 -> Remux 录制文件)" +FinalScene.Title="删除场景" +FinalScene.Text="至少要有一个场景." + diff --git a/UI/data/locale/zh-TW.ini b/UI/data/locale/zh-TW.ini index 674fa19..ea482a8 100644 --- a/UI/data/locale/zh-TW.ini +++ b/UI/data/locale/zh-TW.ini @@ -31,6 +31,9 @@ DroppedFrames="影格遺失: %1 (%2%)" PreviewProjector="全螢幕投影(預覽)" SceneProjector="全螢幕投影(場景)" SourceProjector="全螢幕投影(來源)" +PreviewWindow="視窗化投影(預覽)" +SceneWindow="視窗化投影(場景)" +SourceWindow="視窗化投影(來源)" Clear="清除" Revert="復原" Show="顯示" @@ -56,6 +59,92 @@ Deprecated="不再維護" ReplayBuffer="重播緩衝" Import="匯入" Export="匯出" +Copy="複製" +Paste="貼上" +PasteReference="貼上 (參考)" +PasteDuplicate="貼上 (重複)" +RemuxRecordings="重新封裝錄影" +Next="下一步" +Back="返回" + +AlreadyRunning.Title="OBS 已在執行中" +AlreadyRunning.Text="OBS 已在執行中!除非這是您的意圖,請在執行新的 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="(注意: 自動設定精靈目前處於 Beta 階段)" +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="硬體編碼去除了大多數的 CPU 使用率,但可能需要更多的位元率以獲得同等的品質。" +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="有新更新︰" @@ -358,7 +447,7 @@ Basic.MainMenu.SceneCollection.Exists="已有該場景群組" Basic.MainMenu.Tools="工具(&T)" -Basic.MainMenu.Help="幫助 (&H)" +Basic.MainMenu.Help="說明 (&H)" Basic.MainMenu.Help.Website="前往 OBS 網站 (&W)" Basic.MainMenu.Help.Logs="Log 檔案 (&L)" Basic.MainMenu.Help.Logs.ShowLogs="顯示 Log (&S)" @@ -375,6 +464,7 @@ 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="投影" @@ -490,7 +580,7 @@ FilenameFormatting.TT="%CCYY 年, 四位數\n%YY 年, 末兩位數 (00-99)\n%M Basic.Settings.Video="影像" Basic.Settings.Video.Adapter="顯示卡:" -Basic.Settings.Video.BaseResolution="來源(全畫面)解析度:" +Basic.Settings.Video.BaseResolution="來源(畫布)解析度:" Basic.Settings.Video.ScaledResolution="輸出(縮放)解析度:" Basic.Settings.Video.DownscaleFilter="壓縮方式:" Basic.Settings.Video.DisableAeroWindows="暫時關閉 Aero 特效(僅限 Windows)" @@ -538,6 +628,7 @@ 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="重新連線時維持截止點 (增加延遲)" @@ -617,3 +708,6 @@ OutputWarnings.NoTracksSelected="您必須至少選擇一個軌道" OutputWarnings.MultiTrackRecording="警告:某些格式 (例如 FLV) 不支援多個軌道錄製" OutputWarnings.MP4Recording="警告︰ 如果檔案無法完成,儲存成 MP4 的紀錄將無法復原 (例如由於 BSOD,斷電等)。如果想要記錄多個音軌請考慮儲存成 MKV 並在完成後重新封裝成 mp4(檔案 -> 重新封裝)" +FinalScene.Title="刪除場景" +FinalScene.Text="至少要有一個場景。" + diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index 28f7c28..adcfea1 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -508,3 +508,18 @@ QLabel#errorLabel { color: rgb(192, 0, 0); font-weight: bold; } + +* [themeID="warning"] { + color: rgb(192, 128, 0); + font-weight: bold; +} + +* [themeID="error"] { + color: rgb(192, 0, 0); + font-weight: bold; +} + +* [themeID="good"] { + color: rgb(0, 192, 0); + font-weight: bold; +} diff --git a/UI/data/themes/Dark/cogwheel.png b/UI/data/themes/Dark/cogwheel.png index 84d9234..62ecce9 100644 Binary files a/UI/data/themes/Dark/cogwheel.png and b/UI/data/themes/Dark/cogwheel.png differ diff --git a/UI/data/themes/Dark/down_arrow.png b/UI/data/themes/Dark/down_arrow.png index 1904bdb..39d31da 100644 Binary files a/UI/data/themes/Dark/down_arrow.png and b/UI/data/themes/Dark/down_arrow.png differ diff --git a/UI/data/themes/Dark/minus.png b/UI/data/themes/Dark/minus.png index 68c0ac5..26e35b4 100644 Binary files a/UI/data/themes/Dark/minus.png and b/UI/data/themes/Dark/minus.png differ diff --git a/UI/data/themes/Dark/mute.png b/UI/data/themes/Dark/mute.png index 97506a9..682f91e 100644 Binary files a/UI/data/themes/Dark/mute.png and b/UI/data/themes/Dark/mute.png differ diff --git a/UI/data/themes/Dark/plus.png b/UI/data/themes/Dark/plus.png index 547cfa9..69325d1 100644 Binary files a/UI/data/themes/Dark/plus.png and b/UI/data/themes/Dark/plus.png differ diff --git a/UI/data/themes/Dark/unmute.png b/UI/data/themes/Dark/unmute.png index dbf8041..ae73cd2 100644 Binary files a/UI/data/themes/Dark/unmute.png and b/UI/data/themes/Dark/unmute.png differ diff --git a/UI/data/themes/Dark/up_arrow.png b/UI/data/themes/Dark/up_arrow.png index 36ff915..89a5d47 100644 Binary files a/UI/data/themes/Dark/up_arrow.png and b/UI/data/themes/Dark/up_arrow.png differ diff --git a/UI/data/themes/Dark/updown.png b/UI/data/themes/Dark/updown.png index 08cfeb6..69cadbb 100644 Binary files a/UI/data/themes/Dark/updown.png and b/UI/data/themes/Dark/updown.png differ diff --git a/UI/data/themes/Default.qss b/UI/data/themes/Default.qss index e2aefc9..b411e0d 100644 --- a/UI/data/themes/Default.qss +++ b/UI/data/themes/Default.qss @@ -73,3 +73,18 @@ QLabel#errorLabel { color: rgb(192, 0, 0); font-weight: bold; } + +* [themeID="warning"] { + color: rgb(192, 128, 0); + font-weight: bold; +} + +* [themeID="error"] { + color: rgb(192, 0, 0); + font-weight: bold; +} + +* [themeID="good"] { + color: rgb(0, 128, 0); + font-weight: bold; +} diff --git a/UI/forms/AutoConfigFinishPage.ui b/UI/forms/AutoConfigFinishPage.ui new file mode 100644 index 0000000..ae3ddc7 --- /dev/null +++ b/UI/forms/AutoConfigFinishPage.ui @@ -0,0 +1,41 @@ + + + AutoConfigFinishPage + + + + 0 + 0 + 400 + 300 + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/UI/forms/AutoConfigStartPage.ui b/UI/forms/AutoConfigStartPage.ui new file mode 100644 index 0000000..e873043 --- /dev/null +++ b/UI/forms/AutoConfigStartPage.ui @@ -0,0 +1,51 @@ + + + AutoConfigStartPage + + + + 0 + 0 + 400 + 300 + + + + + + + + + + Basic.AutoConfig.StartPage.PrioritizeStreaming + + + true + + + + + + + Basic.AutoConfig.StartPage.PrioritizeRecording + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/UI/forms/AutoConfigStreamPage.ui b/UI/forms/AutoConfigStreamPage.ui new file mode 100644 index 0000000..594f99e --- /dev/null +++ b/UI/forms/AutoConfigStreamPage.ui @@ -0,0 +1,255 @@ + + + AutoConfigStreamPage + + + + 0 + 0 + 566 + 335 + + + + + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + Basic.AutoConfig.StreamPage.Service + + + service + + + + + + + + + + Basic.AutoConfig.StreamPage.StreamKey + + + true + + + key + + + + + + + + + + + + + + + QLineEdit::Password + + + + + + + Show + + + + + + + + + Basic.AutoConfig.StreamPage.PerformBandwidthTest + + + true + + + + + + + Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip + + + Basic.AutoConfig.StreamPage.PreferHardwareEncoding + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 90 + 20 + + + + + + + + Basic.AutoConfig.StreamPage.Server + + + + + + + Basic.Settings.Stream.StreamType + + + + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + BandwidthTest.Region + + + + + + BandwidthTest.Region.Asia + + + + + + + BandwidthTest.Region.US + + + + + + + BandwidthTest.Region.EU + + + + + + + BandwidthTest.Region.Other + + + + + + + + + + + + + 500 + + + 10000 + + + 2500 + + + + + + + Basic.Settings.Output.VideoBitrate + + + bitrate + + + + + + + streamType + service + server + customServer + key + show + preferHardware + doBandwidthTest + regionUS + regionEU + regionAsia + regionOther + + + + diff --git a/UI/forms/AutoConfigTestPage.ui b/UI/forms/AutoConfigTestPage.ui new file mode 100644 index 0000000..95c2d44 --- /dev/null +++ b/UI/forms/AutoConfigTestPage.ui @@ -0,0 +1,141 @@ + + + AutoConfigTestPage + + + + 0 + 0 + 400 + 300 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 2 + + + + + + + + + + + + + + + + + + + + + 24 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + Basic.AutoConfig.TestPage.Result.Header + + + true + + + + + + + Basic.AutoConfig.TestPage.Result.Footer + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + + + + diff --git a/UI/forms/AutoConfigVideoPage.ui b/UI/forms/AutoConfigVideoPage.ui new file mode 100644 index 0000000..b941cc8 --- /dev/null +++ b/UI/forms/AutoConfigVideoPage.ui @@ -0,0 +1,96 @@ + + + AutoConfigVideoPage + + + + 0 + 0 + 470 + 300 + + + + + + + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Basic.Settings.Video.BaseResolution + + + canvasRes + + + + + + + + + + Basic.Settings.Video.FPS + + + fps + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 87 + 17 + + + + + + + + + + Basic.AutoConfig.VideoPage.CanvasExplanation + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 92aa8ed..dd61bde 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -892,6 +892,51 @@ + + + Copy + + + Ctrl+C + + + + + false + + + PasteReference + + + PasteReference + + + PasteReference + + + Ctrl+V + + + + + Copy.Filters + + + + + false + + + Paste.Filters + + + + + + + + + @@ -936,14 +981,15 @@ + + - - false - Basic.MainMenu.Tools + + @@ -1143,6 +1189,9 @@ Basic.MainMenu.Edit.Transform.EditTransform + + Ctrl+E + @@ -1409,6 +1458,26 @@ Basic.MainMenu.Edit.Scale.Output + + + PasteDuplicate + + + + + Basic.AutoConfig + + + + + Basic.AutoConfig.Beta + + + + + Basic.Stats + + diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 2d467aa..191f0bb 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -200,16 +200,6 @@ - - - - Basic.Settings.General.EnableAutoUpdates - - - true - - - @@ -226,6 +216,23 @@ + + + + Basic.Settings.General.EnableAutoUpdates + + + true + + + + + + + Basic.Settings.General.OpenStatsOnStartup + + + @@ -3501,8 +3508,8 @@ 0 0 - 803 - 738 + 593 + 761 @@ -3755,6 +3762,13 @@ + + + + Basic.Settings.Advanced.Audio.DisableAudioDucking + + + @@ -4277,8 +4291,8 @@ setCurrentIndex(int) - 159 - 30 + 310 + 29 241 @@ -4309,12 +4323,12 @@ setVisible(bool) - 240 - 46 + 250 + 39 - 240 - 44 + 250 + 39 @@ -4325,12 +4339,12 @@ setVisible(bool) - 240 - 46 + 250 + 39 - 160 - 44 + 250 + 39 @@ -4341,12 +4355,12 @@ setVisible(bool) - 240 - 46 + 250 + 39 - 240 - 43 + 250 + 39 @@ -4357,12 +4371,12 @@ setVisible(bool) - 240 - 46 + 250 + 39 - 160 - 43 + 250 + 39 @@ -4378,7 +4392,7 @@ 241 - 53 + 30 @@ -4389,12 +4403,12 @@ setEnabled(bool) - 168 - 67 + 259 + 60 - 250 - 67 + 228 + 50 @@ -4405,12 +4419,12 @@ setEnabled(bool) - 168 - 52 + 259 + 39 - 232 - 52 + 228 + 29 @@ -4425,8 +4439,8 @@ 56 - 232 - 56 + 228 + 50 @@ -4453,12 +4467,12 @@ setVisible(bool) - 240 - 46 + 250 + 39 - 240 - 45 + 250 + 39 @@ -4469,12 +4483,12 @@ setEnabled(bool) - 730 - 556 + 950 + 579 - 746 - 579 + 950 + 602 @@ -4485,12 +4499,12 @@ setEnabled(bool) - 830 - 556 + 950 + 579 - 826 - 602 + 950 + 625 @@ -4501,12 +4515,12 @@ setEnabled(bool) - 403 - 642 + 250 + 39 - 403 - 665 + 250 + 39 @@ -4517,12 +4531,12 @@ setEnabled(bool) - 733 - 317 + 950 + 340 347 - 343 + 366 @@ -4533,12 +4547,12 @@ setEnabled(bool) - 774 - 317 + 950 + 340 - 782 - 343 + 950 + 366 @@ -4549,12 +4563,12 @@ setEnabled(bool) - 820 - 317 + 950 + 340 - 837 - 366 + 950 + 389 @@ -4565,12 +4579,12 @@ setEnabled(bool) - 881 - 317 + 950 + 340 - 890 - 389 + 950 + 412 @@ -4581,12 +4595,12 @@ setEnabled(bool) - 928 - 317 + 950 + 340 - 915 - 412 + 950 + 435 @@ -4597,12 +4611,12 @@ setEnabled(bool) - 803 - 199 + 950 + 222 - 820 - 222 + 950 + 245 @@ -4613,12 +4627,12 @@ setEnabled(bool) - 747 - 245 + 950 + 268 - 753 - 268 + 950 + 291 @@ -4629,12 +4643,12 @@ setEnabled(bool) - 431 - 422 + 250 + 39 - 356 - 443 + 250 + 39 @@ -4645,12 +4659,12 @@ setEnabled(bool) - 465 - 420 + 250 + 39 - 463 - 447 + 250 + 39 @@ -4661,12 +4675,12 @@ setEnabled(bool) - 533 - 420 + 250 + 39 - 557 - 446 + 250 + 39 @@ -4677,12 +4691,12 @@ setEnabled(bool) - 504 - 420 + 250 + 39 - 494 - 465 + 250 + 39 diff --git a/UI/forms/OBSRemux.ui b/UI/forms/OBSRemux.ui index a75d640..4c74e56 100644 --- a/UI/forms/OBSRemux.ui +++ b/UI/forms/OBSRemux.ui @@ -11,37 +11,9 @@ - Dialog + RemuxRecordings - - - - 10 - 90 - 351 - 23 - - - - 24 - - - - - - 10 - 10 - 471 - 71 - - - - - QFormLayout::AllNonFixedFieldsGrow - - - 6 - + @@ -60,12 +32,6 @@ - - - 0 - 0 - - @@ -81,12 +47,6 @@ - - - 0 - 0 - - @@ -98,21 +58,25 @@ + + + + 24 + + + + + + + + + QDialogButtonBox::Ok|QDialogButtonBox::Close + + + + + - - - - - 370 - 90 - 111 - 23 - - - - Remux.Remux - - diff --git a/UI/forms/images/add.png b/UI/forms/images/add.png index 1ab02cb..5413c1e 100644 Binary files a/UI/forms/images/add.png and b/UI/forms/images/add.png differ diff --git a/UI/forms/images/configuration21_16.png b/UI/forms/images/configuration21_16.png index d165a7a..3ddd717 100644 Binary files a/UI/forms/images/configuration21_16.png and b/UI/forms/images/configuration21_16.png differ diff --git a/UI/forms/images/down.png b/UI/forms/images/down.png index 3953778..35a8c4b 100644 Binary files a/UI/forms/images/down.png and b/UI/forms/images/down.png differ diff --git a/UI/forms/images/editscene.png b/UI/forms/images/editscene.png index 99ff752..65b96e8 100644 Binary files a/UI/forms/images/editscene.png and b/UI/forms/images/editscene.png differ diff --git a/UI/forms/images/invisible_mask.png b/UI/forms/images/invisible_mask.png index 4f9ece7..479e13e 100644 Binary files a/UI/forms/images/invisible_mask.png and b/UI/forms/images/invisible_mask.png differ diff --git a/UI/forms/images/list_remove.png b/UI/forms/images/list_remove.png index a4e4d87..5858662 100644 Binary files a/UI/forms/images/list_remove.png and b/UI/forms/images/list_remove.png differ diff --git a/UI/forms/images/live.png b/UI/forms/images/live.png index 2be07b5..804f72c 100644 Binary files a/UI/forms/images/live.png and b/UI/forms/images/live.png differ diff --git a/UI/forms/images/mute.png b/UI/forms/images/mute.png index c658802..97cb10a 100644 Binary files a/UI/forms/images/mute.png and b/UI/forms/images/mute.png differ diff --git a/UI/forms/images/obs.png b/UI/forms/images/obs.png old mode 100755 new mode 100644 index e187877..dbae094 Binary files a/UI/forms/images/obs.png and b/UI/forms/images/obs.png differ diff --git a/UI/forms/images/properties.png b/UI/forms/images/properties.png index d165a7a..3ddd717 100644 Binary files a/UI/forms/images/properties.png and b/UI/forms/images/properties.png differ diff --git a/UI/forms/images/settings/advanced.png b/UI/forms/images/settings/advanced.png index 478e278..91593e0 100644 Binary files a/UI/forms/images/settings/advanced.png and b/UI/forms/images/settings/advanced.png differ diff --git a/UI/forms/images/settings/applications-system-2.png b/UI/forms/images/settings/applications-system-2.png index 34d5305..30f418d 100644 Binary files a/UI/forms/images/settings/applications-system-2.png and b/UI/forms/images/settings/applications-system-2.png differ diff --git a/UI/forms/images/settings/decibel_audio_player.png b/UI/forms/images/settings/decibel_audio_player.png index 415b04f..21d6858 100644 Binary files a/UI/forms/images/settings/decibel_audio_player.png and b/UI/forms/images/settings/decibel_audio_player.png differ diff --git a/UI/forms/images/settings/network-bluetooth.png b/UI/forms/images/settings/network-bluetooth.png index 052878c..764e766 100644 Binary files a/UI/forms/images/settings/network-bluetooth.png and b/UI/forms/images/settings/network-bluetooth.png differ diff --git a/UI/forms/images/settings/network.png b/UI/forms/images/settings/network.png index 736c74d..ada03d7 100644 Binary files a/UI/forms/images/settings/network.png and b/UI/forms/images/settings/network.png differ diff --git a/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png b/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png index 95a3aa8..2abb1b5 100644 Binary files a/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png and b/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png differ diff --git a/UI/forms/images/settings/preferences-system-network-3.png b/UI/forms/images/settings/preferences-system-network-3.png index c9f9963..3cfc3cb 100644 Binary files a/UI/forms/images/settings/preferences-system-network-3.png and b/UI/forms/images/settings/preferences-system-network-3.png differ diff --git a/UI/forms/images/settings/system-settings-3.png b/UI/forms/images/settings/system-settings-3.png index 6674eb2..afc55ff 100644 Binary files a/UI/forms/images/settings/system-settings-3.png and b/UI/forms/images/settings/system-settings-3.png differ diff --git a/UI/forms/images/settings/video-display-3.png b/UI/forms/images/settings/video-display-3.png index bbeb9e7..71e8379 100644 Binary files a/UI/forms/images/settings/video-display-3.png and b/UI/forms/images/settings/video-display-3.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 4f6a012..22a8b92 Binary files a/UI/forms/images/tray_active.png and b/UI/forms/images/tray_active.png differ diff --git a/UI/forms/images/unmute.png b/UI/forms/images/unmute.png index 5341b8a..e849e3e 100644 Binary files a/UI/forms/images/unmute.png and b/UI/forms/images/unmute.png differ diff --git a/UI/forms/images/up.png b/UI/forms/images/up.png index 4bed0cf..d881a91 100644 Binary files a/UI/forms/images/up.png and b/UI/forms/images/up.png differ diff --git a/UI/forms/images/visible_mask.png b/UI/forms/images/visible_mask.png index 79bcc7c..67e8326 100644 Binary files a/UI/forms/images/visible_mask.png and b/UI/forms/images/visible_mask.png differ diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 5586495..3ce774d 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -5,16 +5,10 @@ if(APPLE) include_directories(${COCOA}) endif() -if(WIN32 OR APPLE) - set(frontend-tools_HEADERS - auto-scene-switcher.hpp - ) - set(frontend-tools_SOURCES - auto-scene-switcher.cpp - ) - set(frontend-tools_UI - forms/auto-scene-switcher.ui - ) +if(UNIX) + find_package(X11 REQUIRED) + link_libraries(${X11_LIBRARIES}) + include_directories(${X11_INCLUDE_DIR}) endif() configure_file( @@ -24,16 +18,19 @@ configure_file( set(frontend-tools_HEADERS ${frontend-tools_HEADERS} "${CMAKE_BINARY_DIR}/config/frontend-tools-config.h" + auto-scene-switcher.hpp output-timer.hpp tool-helpers.hpp ) set(frontend-tools_SOURCES ${frontend-tools_SOURCES} + auto-scene-switcher.cpp frontend-tools.c output-timer.cpp ) set(frontend-tools_UI ${frontend-tools_UI} + forms/auto-scene-switcher.ui forms/output-timer.ui ) @@ -45,10 +42,14 @@ if(WIN32) set(frontend-tools_PLATFORM_SOURCES ${frontend-tools_PLATFORM_SOURCES} captions.cpp - captions-stream.cpp) + captions-handler.cpp + captions-mssapi.cpp + captions-mssapi-stream.cpp) set(frontend-tools_PLATFORM_HEADERS captions.hpp - captions-stream.hpp) + captions-handler.hpp + captions-mssapi.hpp + captions-mssapi-stream.hpp) set(frontend-tools_PLATFORM_UI forms/captions.ui) endif() @@ -60,6 +61,9 @@ elseif(APPLE) set(frontend-tools_PLATFORM_LIBS ${COCOA}) +else() + set(frontend-tools_PLATFORM_SOURCES + auto-scene-switcher-nix.cpp) endif() qt5_wrap_ui(frontend-tools_UI_HEADERS diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp new file mode 100644 index 0000000..0575196 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp @@ -0,0 +1,211 @@ +#include +#include +#include +#undef Bool +#undef CursorShape +#undef Expose +#undef KeyPress +#undef KeyRelease +#undef FocusIn +#undef FocusOut +#undef FontChange +#undef None +#undef Status +#undef Unsorted +#include +#include "auto-scene-switcher.hpp" + +using namespace std; + +static Display* xdisplay = 0; + +Display *disp() +{ + if (!xdisplay) + xdisplay = XOpenDisplay(NULL); + + return xdisplay; +} + +void cleanupDisplay() +{ + if (!xdisplay) + return; + + XCloseDisplay(xdisplay); + xdisplay = 0; +} + +static bool ewmhIsSupported() +{ + Display *display = disp(); + Atom netSupportingWmCheck = XInternAtom(display, + "_NET_SUPPORTING_WM_CHECK", true); + Atom actualType; + int format = 0; + unsigned long num = 0, bytes = 0; + unsigned char *data = NULL; + Window ewmh_window = 0; + + int status = XGetWindowProperty( + display, + DefaultRootWindow(display), + netSupportingWmCheck, + 0L, + 1L, + false, + XA_WINDOW, + &actualType, + &format, + &num, + &bytes, + &data); + + if (status == Success) { + if (num > 0) { + ewmh_window = ((Window*)data)[0]; + } + if (data) { + XFree(data); + data = NULL; + } + } + + if (ewmh_window) { + status = XGetWindowProperty( + display, + ewmh_window, + netSupportingWmCheck, + 0L, + 1L, + false, + XA_WINDOW, + &actualType, + &format, + &num, + &bytes, + &data); + if (status != Success || num == 0 || + ewmh_window != ((Window*)data)[0]) { + ewmh_window = 0; + } + if (status == Success && data) { + XFree(data); + } + } + + return ewmh_window != 0; +} + +static std::vector getTopLevelWindows() +{ + std::vector res; + + res.resize(0); + + if (!ewmhIsSupported()) { + return res; + } + + Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true); + Atom actualType; + int format; + unsigned long num, bytes; + Window* data = 0; + + for (int i = 0; i < ScreenCount(disp()); ++i) { + Window rootWin = RootWindow(disp(), i); + + int status = XGetWindowProperty( + disp(), + rootWin, + netClList, + 0L, + ~0L, + false, + AnyPropertyType, + &actualType, + &format, + &num, + &bytes, + (uint8_t**)&data); + + if (status != Success) { + continue; + } + + for (unsigned long i = 0; i < num; ++i) + res.emplace_back(data[i]); + + XFree(data); + } + + return res; +} + +static std::string GetWindowTitle(size_t i) +{ + Window w = getTopLevelWindows().at(i); + std::string windowTitle; + char* name; + + int status = XFetchName(disp(), w, &name); + if (status >= Success && name != nullptr) + { + std::string str(name); + windowTitle = str; + } + + XFree(name); + + return windowTitle; +} + +void GetWindowList(vector &windows) +{ + windows.resize(0); + + for (size_t i = 0; i < getTopLevelWindows().size(); ++i){ + if (GetWindowTitle(i) != "") + windows.emplace_back(GetWindowTitle(i)); + } +} + +void GetCurrentWindowTitle(string &title) +{ + if (!ewmhIsSupported()) { + return; + } + + Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true); + Atom actualType; + int format; + unsigned long num, bytes; + Window* data = 0; + char* name; + + Window rootWin = RootWindow(disp(), 0); + + XGetWindowProperty( + disp(), + rootWin, + active, + 0L, + ~0L, + false, + AnyPropertyType, + &actualType, + &format, + &num, + &bytes, + (uint8_t**)&data); + + int status = XFetchName(disp(), data[0], &name); + + if (status >= Success && name != nullptr) { + std::string str(name); + title = str; + } + + XFree(name); +} diff --git a/UI/frontend-plugins/frontend-tools/captions-handler.cpp b/UI/frontend-plugins/frontend-tools/captions-handler.cpp new file mode 100644 index 0000000..ee9a85c --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/captions-handler.cpp @@ -0,0 +1,54 @@ +#include "captions-handler.hpp" + +captions_handler::captions_handler( + captions_cb callback, + enum audio_format format, + uint32_t sample_rate) + : cb(callback) +{ + if (!reset_resampler(format, sample_rate)) + throw CAPTIONS_ERROR_GENERIC_FAIL; +} + +bool captions_handler::reset_resampler( + enum audio_format format, + uint32_t sample_rate) +try { + obs_audio_info ai; + if (!obs_get_audio_info(&ai)) + throw std::string("Failed to get OBS audio info"); + + resample_info src = { + ai.samples_per_sec, + AUDIO_FORMAT_FLOAT_PLANAR, + ai.speakers + }; + resample_info dst = { + sample_rate, + format, + SPEAKERS_MONO + }; + + if (!resampler.reset(dst, src)) + throw std::string("Failed to create audio resampler"); + + return true; + +} catch (std::string text) { + blog(LOG_WARNING, "%s: %s", __FUNCTION__, text.c_str()); + return false; +} + +void captions_handler::push_audio(const audio_data *audio) +{ + uint8_t *out[MAX_AV_PLANES]; + uint32_t frames; + uint64_t ts_offset; + bool success; + + success = audio_resampler_resample(resampler, + out, &frames, &ts_offset, + (const uint8_t *const *)audio->data, audio->frames); + if (success) + pcm_data(out[0], frames); +} diff --git a/UI/frontend-plugins/frontend-tools/captions-handler.hpp b/UI/frontend-plugins/frontend-tools/captions-handler.hpp new file mode 100644 index 0000000..a9f17cb --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/captions-handler.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include + +class resampler_obj { + audio_resampler_t *resampler = nullptr; + +public: + inline ~resampler_obj() + { + audio_resampler_destroy(resampler); + } + + inline bool reset(const resample_info &dst, const resample_info &src) + { + audio_resampler_destroy(resampler); + resampler = audio_resampler_create(&dst, &src); + return !!resampler; + } + + inline operator audio_resampler_t*() {return resampler;} +}; + +/* ------------------------------------------------------------------------- */ + +typedef std::function captions_cb; + +#define captions_error(s) std::string(obs_module_text("Captions.Error." ## s)) +#define CAPTIONS_ERROR_GENERIC_FAIL captions_error("GenericFail") + +/* ------------------------------------------------------------------------- */ + +class captions_handler { + captions_cb cb; + resampler_obj resampler; + +protected: + inline void callback(const std::string &text) + { + cb(text); + } + + virtual void pcm_data(const void *data, size_t frames)=0; + + /* always resamples to 1 channel */ + bool reset_resampler(enum audio_format format, uint32_t sample_rate); + +public: + /* throw std::string for errors shown to users */ + captions_handler( + captions_cb callback, + enum audio_format format, + uint32_t sample_rate); + virtual ~captions_handler() {} + + void push_audio(const audio_data *audio); +}; + +/* ------------------------------------------------------------------------- */ + +struct captions_handler_info { + std::string (*name)(void); + captions_handler *(*create)(captions_cb cb, const std::string &lang); +}; diff --git a/UI/frontend-plugins/frontend-tools/captions-stream.cpp b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp similarity index 91% rename from UI/frontend-plugins/frontend-tools/captions-stream.cpp rename to UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp index b983bad..f8b6425 100644 --- a/UI/frontend-plugins/frontend-tools/captions-stream.cpp +++ b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp @@ -1,4 +1,5 @@ -#include "captions-stream.hpp" +#include "captions-mssapi-stream.hpp" +#include "captions-mssapi.hpp" #include #include #include @@ -13,7 +14,8 @@ using namespace std; #define debugfunc(format, ...) #endif -CaptionStream::CaptionStream(DWORD samplerate_) : +CaptionStream::CaptionStream(DWORD samplerate_, mssapi_captions *handler_) : + handler(handler_), samplerate(samplerate_), event(CreateEvent(nullptr, false, false, nullptr)) { @@ -28,8 +30,6 @@ CaptionStream::CaptionStream(DWORD samplerate_) : format.nBlockAlign = 2; format.wBitsPerSample = 16; format.cbSize = sizeof(format); - - resampler.Reset(&format); } void CaptionStream::Stop() @@ -42,28 +42,16 @@ void CaptionStream::Stop() cv.notify_one(); } -void CaptionStream::PushAudio(const struct audio_data *data, bool muted) +void CaptionStream::PushAudio(const void *data, size_t frames) { - uint8_t *output[MAX_AV_PLANES] = {}; - uint32_t frames = data->frames; - uint64_t ts_offset; bool ready = false; - audio_resampler_resample(resampler, output, &frames, &ts_offset, - data->data, data->frames); - - if (output[0]) { - if (muted) - memset(output[0], 0, frames * sizeof(int16_t)); - - lock_guard lock(m); - circlebuf_push_back(buf, output[0], frames * sizeof(int16_t)); - write_pos += frames * sizeof(int16_t); - - if (wait_size && buf->size >= wait_size) - ready = true; - } + lock_guard lock(m); + circlebuf_push_back(buf, data, frames * sizeof(int16_t)); + write_pos += frames * sizeof(int16_t); + if (wait_size && buf->size >= wait_size) + ready = true; if (ready) cv.notify_one(); } @@ -316,7 +304,9 @@ STDMETHODIMP CaptionStream::SetFormat(REFGUID guid_ref, if (guid_ref == SPDFID_WaveFormatEx) { lock_guard lock(m); memcpy(&format, wfex, sizeof(format)); - resampler.Reset(wfex); + if (!handler->reset_resampler(AUDIO_FORMAT_16BIT, + wfex->nSamplesPerSec)) + return E_FAIL; /* 50 msec */ DWORD size = format.nSamplesPerSec / 20; diff --git a/UI/frontend-plugins/frontend-tools/captions-stream.hpp b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.hpp similarity index 75% rename from UI/frontend-plugins/frontend-tools/captions-stream.hpp rename to UI/frontend-plugins/frontend-tools/captions-mssapi-stream.hpp index 5461b7d..3c1ddb4 100644 --- a/UI/frontend-plugins/frontend-tools/captions-stream.hpp +++ b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.hpp @@ -1,10 +1,11 @@ +#pragma once + #include #include #include #include #include #include -#include #include #include @@ -18,37 +19,12 @@ public: inline circlebuf *operator->() {return &buf;} }; -class Resampler { - audio_resampler_t *resampler = nullptr; - -public: - inline void Reset(const WAVEFORMATEX *wfex) - { - const struct audio_output_info *aoi = - audio_output_get_info(obs_get_audio()); - - struct resample_info src; - src.samples_per_sec = aoi->samples_per_sec; - src.format = aoi->format; - src.speakers = aoi->speakers; - - struct resample_info dst; - dst.samples_per_sec = uint32_t(wfex->nSamplesPerSec); - dst.format = AUDIO_FORMAT_16BIT; - dst.speakers = (enum speaker_layout)wfex->nChannels; - - if (resampler) - audio_resampler_destroy(resampler); - resampler = audio_resampler_create(&dst, &src); - } - - inline ~Resampler() {audio_resampler_destroy(resampler);} - inline operator audio_resampler_t*() {return resampler;} -}; +class mssapi_captions; class CaptionStream : public ISpAudio { volatile long refs = 1; SPAUDIOBUFFERINFO buf_info = {}; + mssapi_captions *handler; ULONG notify_size = 0; SPAUDIOSTATE state; WinHandle event; @@ -58,7 +34,6 @@ class CaptionStream : public ISpAudio { std::mutex m; std::vector temp_buf; WAVEFORMATEX format = {}; - Resampler resampler; CircleBuf buf; ULONG wait_size = 0; @@ -67,10 +42,10 @@ class CaptionStream : public ISpAudio { ULONGLONG write_pos = 0; public: - CaptionStream(DWORD samplerate); + CaptionStream(DWORD samplerate, mssapi_captions *handler_); void Stop(); - void PushAudio(const struct audio_data *audio_data, bool muted); + void PushAudio(const void *data, size_t frames); // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid, void **ppv) override; diff --git a/UI/frontend-plugins/frontend-tools/captions-mssapi.cpp b/UI/frontend-plugins/frontend-tools/captions-mssapi.cpp new file mode 100644 index 0000000..e2cc8c4 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/captions-mssapi.cpp @@ -0,0 +1,179 @@ +#include "captions-mssapi.hpp" + +#define do_log(type, format, ...) blog(type, "[Captions] " format, \ + ##__VA_ARGS__) + +#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__) +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) + +mssapi_captions::mssapi_captions( + captions_cb callback, + const std::string &lang) try + : captions_handler(callback, AUDIO_FORMAT_16BIT, 16000) +{ + HRESULT hr; + + std::wstring wlang; + wlang.resize(lang.size()); + + for (size_t i = 0; i < lang.size(); i++) + wlang[i] = (wchar_t)lang[i]; + + LCID lang_id = LocaleNameToLCID(wlang.c_str(), 0); + + wchar_t lang_str[32]; + _snwprintf(lang_str, 31, L"language=%x", (int)lang_id); + + stop = CreateEvent(nullptr, false, false, nullptr); + if (!stop.Valid()) + throw "Failed to create event"; + + hr = SpFindBestToken(SPCAT_RECOGNIZERS, lang_str, nullptr, &token); + if (FAILED(hr)) + throw HRError("SpFindBestToken failed", hr); + + hr = CoCreateInstance(CLSID_SpInprocRecognizer, nullptr, CLSCTX_ALL, + __uuidof(ISpRecognizer), (void**)&recognizer); + if (FAILED(hr)) + throw HRError("CoCreateInstance for recognizer failed", hr); + + hr = recognizer->SetRecognizer(token); + if (FAILED(hr)) + throw HRError("SetRecognizer failed", hr); + + hr = recognizer->SetRecoState(SPRST_INACTIVE); + if (FAILED(hr)) + throw HRError("SetRecoState(SPRST_INACTIVE) failed", hr); + + hr = recognizer->CreateRecoContext(&context); + if (FAILED(hr)) + throw HRError("CreateRecoContext failed", hr); + + ULONGLONG interest = SPFEI(SPEI_RECOGNITION) | + SPFEI(SPEI_END_SR_STREAM); + hr = context->SetInterest(interest, interest); + if (FAILED(hr)) + throw HRError("SetInterest failed", hr); + + hr = context->SetNotifyWin32Event(); + if (FAILED(hr)) + throw HRError("SetNotifyWin32Event", hr); + + notify = context->GetNotifyEventHandle(); + if (notify == INVALID_HANDLE_VALUE) + throw HRError("GetNotifyEventHandle failed", E_NOINTERFACE); + + size_t sample_rate = audio_output_get_sample_rate(obs_get_audio()); + audio = new CaptionStream((DWORD)sample_rate, this); + audio->Release(); + + hr = recognizer->SetInput(audio, false); + if (FAILED(hr)) + throw HRError("SetInput failed", hr); + + hr = context->CreateGrammar(1, &grammar); + if (FAILED(hr)) + throw HRError("CreateGrammar failed", hr); + + hr = grammar->LoadDictation(nullptr, SPLO_STATIC); + if (FAILED(hr)) + throw HRError("LoadDictation failed", hr); + + try { + t = std::thread([this] () {main_thread();}); + } catch (...) { + throw "Failed to create thread"; + } + +} catch (const char *err) { + blog(LOG_WARNING, "%s: %s", __FUNCTION__, err); + throw CAPTIONS_ERROR_GENERIC_FAIL; + +} catch (HRError err) { + blog(LOG_WARNING, "%s: %s (%lX)", __FUNCTION__, err.str, err.hr); + throw CAPTIONS_ERROR_GENERIC_FAIL; +} + +mssapi_captions::~mssapi_captions() +{ + if (t.joinable()) { + SetEvent(stop); + t.join(); + } +} + +void mssapi_captions::main_thread() +try { + HRESULT hr; + + os_set_thread_name(__FUNCTION__); + + hr = grammar->SetDictationState(SPRS_ACTIVE); + if (FAILED(hr)) + throw HRError("SetDictationState failed", hr); + + hr = recognizer->SetRecoState(SPRST_ACTIVE); + if (FAILED(hr)) + throw HRError("SetRecoState(SPRST_ACTIVE) failed", hr); + + HANDLE events[] = {notify, stop}; + + started = true; + + for (;;) { + DWORD ret = WaitForMultipleObjects(2, events, false, INFINITE); + if (ret != WAIT_OBJECT_0) + break; + + CSpEvent event; + bool exit = false; + + while (event.GetFrom(context) == S_OK) { + if (event.eEventId == SPEI_RECOGNITION) { + ISpRecoResult *result = event.RecoResult(); + + CoTaskMemPtr text; + hr = result->GetText((ULONG)-1, (ULONG)-1, + true, &text, nullptr); + if (FAILED(hr)) + continue; + + char text_utf8[512]; + os_wcs_to_utf8(text, 0, text_utf8, 512); + + callback(text_utf8); + + blog(LOG_DEBUG, "\"%s\"", text_utf8); + + } else if (event.eEventId == SPEI_END_SR_STREAM) { + exit = true; + break; + } + } + + if (exit) + break; + } + + audio->Stop(); + +} catch (HRError err) { + blog(LOG_WARNING, "%s failed: %s (%lX)", __FUNCTION__, err.str, err.hr); +} + +void mssapi_captions::pcm_data(const void *data, size_t frames) +{ + if (started) + audio->PushAudio(data, frames); +} + +captions_handler_info mssapi_info = { + [] () -> std::string + { + return "Microsoft Speech-to-Text"; + }, + [] (captions_cb cb, const std::string &lang) -> captions_handler * + { + return new mssapi_captions(cb, lang); + } +}; diff --git a/UI/frontend-plugins/frontend-tools/captions-mssapi.hpp b/UI/frontend-plugins/frontend-tools/captions-mssapi.hpp new file mode 100644 index 0000000..a9a580f --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/captions-mssapi.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "captions-handler.hpp" +#include "captions-mssapi-stream.hpp" +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class mssapi_captions : public captions_handler { + friend class CaptionStream; + + ComPtr audio; + ComPtr token; + ComPtr grammar; + ComPtr recognizer; + ComPtr context; + + HANDLE notify; + WinHandle stop; + std::thread t; + bool started = false; + + void main_thread(); + +public: + mssapi_captions(captions_cb callback, const std::string &lang); + virtual ~mssapi_captions(); + virtual void pcm_data(const void *data, size_t frames) override; +}; diff --git a/UI/frontend-plugins/frontend-tools/captions.cpp b/UI/frontend-plugins/frontend-tools/captions.cpp index c26d5b9..d2da4d3 100644 --- a/UI/frontend-plugins/frontend-tools/captions.cpp +++ b/UI/frontend-plugins/frontend-tools/captions.cpp @@ -1,47 +1,54 @@ +#include + +#include #include -#include "captions-stream.hpp" #include "captions.hpp" +#include "captions-handler.hpp" #include "tool-helpers.hpp" -#include #include #include -#include +#include #include -#include -#include #include +#include +#include +#include #include #include #include +#include "captions-mssapi.hpp" + #define do_log(type, format, ...) blog(type, "[Captions] " format, \ ##__VA_ARGS__) -#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__) +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) using namespace std; -struct obs_captions { - thread th; - recursive_mutex m; - WinHandle stop_event; +#define DEFAULT_HANDLER "mssapi" +struct obs_captions { + string handler_id = DEFAULT_HANDLER; string source_name; OBSWeakSource source; - LANGID lang_id; + unique_ptr handler; + LANGID lang_id = GetUserDefaultUILanguage(); + + std::unordered_map handler_types; + + inline void register_handler(const char *id, + captions_handler_info &info) + { + handler_types.emplace(id, info); + } - void main_thread(); void start(); void stop(); - inline obs_captions() : - stop_event(CreateEvent(nullptr, false, false, nullptr)), - lang_id(GetUserDefaultUILanguage()) - { - } - + obs_captions(); inline ~obs_captions() {stop();} }; @@ -72,8 +79,6 @@ CaptionsDialog::CaptionsDialog(QWidget *parent) : { ui->setupUi(this); - lock_guard lock(captions->m); - auto cb = [this] (obs_source_t *source) { uint32_t caps = obs_source_get_output_flags(source); @@ -97,8 +102,19 @@ CaptionsDialog::CaptionsDialog(QWidget *parent) : return (*static_cast(data))(source);}, &cb); ui->source->blockSignals(false); + for (auto &ht : captions->handler_types) { + QString name = ht.second.name().c_str(); + QString id = ht.first.c_str(); + ui->provider->addItem(name, id); + } + + QString qhandler_id = captions->handler_id.c_str(); + int idx = ui->provider->findData(qhandler_id); + if (idx != -1) + ui->provider->setCurrentIndex(idx); + ui->enable->blockSignals(true); - ui->enable->setChecked(captions->th.joinable()); + ui->enable->setChecked(!!captions->handler); ui->enable->blockSignals(false); vector locales; @@ -129,13 +145,11 @@ CaptionsDialog::CaptionsDialog(QWidget *parent) : ui->language->setEnabled(false); } else if (!set_language) { - bool started = captions->th.joinable(); + bool started = !!captions->handler; if (started) captions->stop(); - captions->m.lock(); captions->lang_id = locales[0].id; - captions->m.unlock(); if (started) captions->start(); @@ -144,14 +158,12 @@ CaptionsDialog::CaptionsDialog(QWidget *parent) : void CaptionsDialog::on_source_currentIndexChanged(int) { - bool started = captions->th.joinable(); + bool started = !!captions->handler; if (started) captions->stop(); - captions->m.lock(); captions->source_name = ui->source->currentText().toUtf8().constData(); captions->source = GetWeakSourceByName(captions->source_name.c_str()); - captions->m.unlock(); if (started) captions->start(); @@ -159,21 +171,38 @@ void CaptionsDialog::on_source_currentIndexChanged(int) void CaptionsDialog::on_enable_clicked(bool checked) { - if (checked) + if (checked) { captions->start(); - else + if (!captions->handler) { + ui->enable->blockSignals(true); + ui->enable->setChecked(false); + ui->enable->blockSignals(false); + } + } else { captions->stop(); + } } void CaptionsDialog::on_language_currentIndexChanged(int) { - bool started = captions->th.joinable(); + bool started = !!captions->handler; if (started) captions->stop(); - captions->m.lock(); captions->lang_id = (LANGID)ui->language->currentData().toInt(); - captions->m.unlock(); + + if (started) + captions->start(); +} + +void CaptionsDialog::on_provider_currentIndexChanged(int idx) +{ + bool started = !!captions->handler; + if (started) + captions->stop(); + + captions->handler_id = + ui->provider->itemData(idx).toString().toUtf8().constData(); if (started) captions->start(); @@ -181,183 +210,83 @@ void CaptionsDialog::on_language_currentIndexChanged(int) /* ------------------------------------------------------------------------- */ -void obs_captions::main_thread() -try { - ComPtr audio; - ComPtr token; - ComPtr grammar; - ComPtr recognizer; - ComPtr context; - HRESULT hr; - - auto cb = [&] (const struct audio_data *audio_data, - bool muted) - { - audio->PushAudio(audio_data, muted); - }; - - using cb_t = decltype(cb); - - auto pre_cb = [] (void *param, obs_source_t*, - const struct audio_data *audio_data, bool muted) - { - return (*static_cast(param))(audio_data, muted); - }; - - os_set_thread_name(__FUNCTION__); - - CoInitialize(nullptr); - - wchar_t lang_str[32]; - _snwprintf(lang_str, 31, L"language=%x", (int)captions->lang_id); - - hr = SpFindBestToken(SPCAT_RECOGNIZERS, lang_str, nullptr, &token); - if (FAILED(hr)) - throw HRError("SpFindBestToken failed", hr); - - hr = CoCreateInstance(CLSID_SpInprocRecognizer, nullptr, CLSCTX_ALL, - __uuidof(ISpRecognizer), (void**)&recognizer); - if (FAILED(hr)) - throw HRError("CoCreateInstance for recognizer failed", hr); - - hr = recognizer->SetRecognizer(token); - if (FAILED(hr)) - throw HRError("SetRecognizer failed", hr); - - hr = recognizer->SetRecoState(SPRST_INACTIVE); - if (FAILED(hr)) - throw HRError("SetRecoState(SPRST_INACTIVE) failed", hr); - - hr = recognizer->CreateRecoContext(&context); - if (FAILED(hr)) - throw HRError("CreateRecoContext failed", hr); - - ULONGLONG interest = SPFEI(SPEI_RECOGNITION) | - SPFEI(SPEI_END_SR_STREAM); - hr = context->SetInterest(interest, interest); - if (FAILED(hr)) - throw HRError("SetInterest failed", hr); - - HANDLE notify; - - hr = context->SetNotifyWin32Event(); - if (FAILED(hr)) - throw HRError("SetNotifyWin32Event", hr); - - notify = context->GetNotifyEventHandle(); - if (notify == INVALID_HANDLE_VALUE) - throw HRError("GetNotifyEventHandle failed", E_NOINTERFACE); - - size_t sample_rate = audio_output_get_sample_rate(obs_get_audio()); - audio = new CaptionStream((DWORD)sample_rate); - audio->Release(); - - hr = recognizer->SetInput(audio, false); - if (FAILED(hr)) - throw HRError("SetInput failed", hr); - - hr = context->CreateGrammar(1, &grammar); - if (FAILED(hr)) - throw HRError("CreateGrammar failed", hr); - - hr = grammar->LoadDictation(nullptr, SPLO_STATIC); - if (FAILED(hr)) - throw HRError("LoadDictation failed", hr); - - hr = grammar->SetDictationState(SPRS_ACTIVE); - if (FAILED(hr)) - throw HRError("SetDictationState failed", hr); - - hr = recognizer->SetRecoState(SPRST_ACTIVE); - if (FAILED(hr)) - throw HRError("SetRecoState(SPRST_ACTIVE) failed", hr); - - HANDLE events[] = {notify, stop_event}; - - { - captions->source = GetWeakSourceByName( - captions->source_name.c_str()); - OBSSource strong = OBSGetStrongRef(source); - if (strong) - obs_source_add_audio_capture_callback(strong, - pre_cb, &cb); +static void caption_text(const std::string &text) +{ + obs_output *output = obs_frontend_get_streaming_output(); + if (output) { + obs_output_output_caption_text1(output, text.c_str()); + obs_output_release(output); } +} - for (;;) { - DWORD ret = WaitForMultipleObjects(2, events, false, INFINITE); - if (ret != WAIT_OBJECT_0) - break; - - CSpEvent event; - bool exit = false; - - while (event.GetFrom(context) == S_OK) { - if (event.eEventId == SPEI_RECOGNITION) { - ISpRecoResult *result = event.RecoResult(); - - CoTaskMemPtr text; - hr = result->GetText((ULONG)-1, (ULONG)-1, - true, &text, nullptr); - if (FAILED(hr)) - continue; - - char text_utf8[512]; - os_wcs_to_utf8(text, 0, text_utf8, 512); - - obs_output_t *output = - obs_frontend_get_streaming_output(); - if (output) - obs_output_output_caption_text1(output, - text_utf8); - - debug("\"%s\"", text_utf8); - - obs_output_release(output); - - } else if (event.eEventId == SPEI_END_SR_STREAM) { - exit = true; - break; - } - } - - if (exit) - break; - } - - { - OBSSource strong = OBSGetStrongRef(source); - if (strong) - obs_source_remove_audio_capture_callback(strong, - pre_cb, &cb); - } - - audio->Stop(); - - CoUninitialize(); - -} catch (HRError err) { - error("%s failed: %s (%lX)", __FUNCTION__, err.str, err.hr); - CoUninitialize(); - captions->th.detach(); +static void audio_capture(void*, obs_source_t*, + const struct audio_data *audio, bool) +{ + captions->handler->push_audio(audio); } void obs_captions::start() { - if (!captions->th.joinable()) { - ResetEvent(captions->stop_event); + if (!captions->handler && valid_lang(lang_id)) { + wchar_t wname[256]; - if (valid_lang(captions->lang_id)) - captions->th = thread([] () {captions->main_thread();}); + auto pair = handler_types.find(handler_id); + if (pair == handler_types.end()) { + warn("Failed to find handler '%s'", + handler_id.c_str()); + return; + } + + if (!LCIDToLocaleName(lang_id, wname, 256, 0)) { + warn("Failed to get locale name: %d", + (int)GetLastError()); + return; + } + + size_t len = (size_t)wcslen(wname); + + string lang_name; + lang_name.resize(len); + + for (size_t i = 0; i < len; i++) + lang_name[i] = (char)wname[i]; + + OBSSource s = OBSGetStrongRef(source); + if (!s) { + warn("Source invalid"); + return; + } + + try { + captions_handler *h = pair->second.create(caption_text, + lang_name); + handler.reset(h); + + OBSSource s = OBSGetStrongRef(source); + obs_source_add_audio_capture_callback(s, + audio_capture, nullptr); + + } catch (std::string text) { + QWidget *window = + (QWidget*)obs_frontend_get_main_window(); + + warn("Failed to create handler: %s", text.c_str()); + + QMessageBox::warning(window, + obs_module_text("Captions.Error.GenericFail"), + text.c_str()); + + } } } void obs_captions::stop() { - if (!captions->th.joinable()) - return; - - SetEvent(captions->stop_event); - captions->th.join(); + OBSSource s = OBSGetStrongRef(source); + if (s) + obs_source_remove_audio_capture_callback(s, + audio_capture, nullptr); + handler.reset(); } static bool get_locale_name(LANGID id, char *out) @@ -455,6 +384,15 @@ static void get_valid_locale_names(vector &locales) /* ------------------------------------------------------------------------- */ +extern captions_handler_info mssapi_info; + +obs_captions::obs_captions() +{ + register_handler("mssapi", mssapi_info); +} + +/* ------------------------------------------------------------------------- */ + extern "C" void FreeCaptions() { delete captions; @@ -470,37 +408,36 @@ static void obs_event(enum obs_frontend_event event, void *) static void save_caption_data(obs_data_t *save_data, bool saving, void*) { if (saving) { - lock_guard lock(captions->m); obs_data_t *obj = obs_data_create(); obs_data_set_string(obj, "source", captions->source_name.c_str()); - obs_data_set_bool(obj, "enabled", captions->th.joinable()); + obs_data_set_bool(obj, "enabled", !!captions->handler); obs_data_set_int(obj, "lang_id", captions->lang_id); + obs_data_set_string(obj, "provider", + captions->handler_id.c_str()); obs_data_set_obj(save_data, "captions", obj); obs_data_release(obj); } else { captions->stop(); - captions->m.lock(); - obs_data_t *obj = obs_data_get_obj(save_data, "captions"); if (!obj) obj = obs_data_create(); obs_data_set_default_int(obj, "lang_id", GetUserDefaultUILanguage()); + obs_data_set_default_string(obj, "provider", DEFAULT_HANDLER); bool enabled = obs_data_get_bool(obj, "enabled"); captions->source_name = obs_data_get_string(obj, "source"); captions->lang_id = (int)obs_data_get_int(obj, "lang_id"); + captions->handler_id = obs_data_get_string(obj, "provider"); captions->source = GetWeakSourceByName( captions->source_name.c_str()); obs_data_release(obj); - captions->m.unlock(); - if (enabled) captions->start(); } diff --git a/UI/frontend-plugins/frontend-tools/captions.hpp b/UI/frontend-plugins/frontend-tools/captions.hpp index d499755..8201f39 100644 --- a/UI/frontend-plugins/frontend-tools/captions.hpp +++ b/UI/frontend-plugins/frontend-tools/captions.hpp @@ -17,4 +17,5 @@ public slots: void on_source_currentIndexChanged(int idx); void on_enable_clicked(bool checked); void on_language_currentIndexChanged(int idx); + void on_provider_currentIndexChanged(int idx); }; diff --git a/UI/frontend-plugins/frontend-tools/data/locale/bn-BD.ini b/UI/frontend-plugins/frontend-tools/data/locale/bn-BD.ini new file mode 100644 index 0000000..848720a --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/bn-BD.ini @@ -0,0 +1,13 @@ +SceneSwitcher="স্বয়ংক্রিয় দৃশ্য পরিবর্তক" +SceneSwitcher.OnNoMatch.SwitchTo="পরিবর্তন করুন:" +SceneSwitcher.CheckInterval="সক্রিয় উইন্ডো শিরোনাম চেক করুন প্রতি:" +InvalidRegex.Text="এক্সপ্রেশন প্রবেশ করিয়েছেন তা অবৈধ।." +Active="সক্রিয়" +Inactive="নিষ্ক্রিয়" + +Captions.AudioSource="অডিও উৎস" +Captions.CurrentSystemLanguage="বর্তমান সিস্টেমের ভাষা (%1)" + +OutputTimer="আউটপুট টাইমার" +OutputTimer.Stream="এর পরে বন্ধ।:" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini b/UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini index 509d1c9..0b7ff0d 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini @@ -14,6 +14,8 @@ Stop="Atura" Captions="Subtítols (Experimental)" Captions.AudioSource="Font d'àudio" Captions.CurrentSystemLanguage="Idioma actual del sistema (%1)" +Captions.Provider="Proveïdor" +Captions.Error.GenericFail="No s'ha pogut iniciar els subtítols" OutputTimer="Temporitzador de sortida" OutputTimer.Stream="Atura la transmissió després de:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini b/UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini index 832040c..a70a46a 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini @@ -14,6 +14,8 @@ Stop="Zastavit" Captions="Titulky (experiment.)" Captions.AudioSource="Zdroj zvuku" Captions.CurrentSystemLanguage="Aktuální systémový jazyk (%1)" +Captions.Provider="Zprostředkovatel" +Captions.Error.GenericFail="Nezdařilo se spuštění titulků" OutputTimer="Časovač" OutputTimer.Stream="Přestat vysílat po:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini b/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini index f830d7c..de1619d 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini @@ -14,6 +14,8 @@ Stop="Stop" Captions="Undertekster (eksperimentel)" Captions.AudioSource="Lydkilde" Captions.CurrentSystemLanguage="Aktuelt systemsprog (%1)" +Captions.Provider="Leverandør" +Captions.Error.GenericFail="Kunne ikke starte tekster" OutputTimer="Output-timer" OutputTimer.Stream="Stands streaming efter:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini b/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini index 97d1909..7f09c60 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini @@ -14,6 +14,8 @@ Stop="Stop" Captions="Untertitel (experimentell)" Captions.AudioSource="Audioquelle" Captions.CurrentSystemLanguage="Aktuelle Systemsprache (%1)" +Captions.Provider="Service" +Captions.Error.GenericFail="Fehler beim Starten der Untertitel" OutputTimer="Ausgabetimer" OutputTimer.Stream="Stoppe Stream nach:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini index daf4b31..092a097 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini @@ -14,6 +14,8 @@ Stop="Stop" Captions="Captions (Experimental)" Captions.AudioSource="Audio source" Captions.CurrentSystemLanguage="Current System Language (%1)" +Captions.Provider="Provider" +Captions.Error.GenericFail="Failed to start captions" OutputTimer="Output Timer" OutputTimer.Stream="Stop streaming after:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini b/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini index 4d76f15..0547065 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini @@ -14,6 +14,8 @@ Stop="Detener" Captions="Subtítulos (Experimental)" Captions.AudioSource="Fuente de audio" Captions.CurrentSystemLanguage="Idioma actual del sistema (%1)" +Captions.Provider="Proveedor" +Captions.Error.GenericFail="Fallo al iniciar los subtítulos" OutputTimer="Temporizador de salida" OutputTimer.Stream="Detener la transmisión después de:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/et-EE.ini b/UI/frontend-plugins/frontend-tools/data/locale/et-EE.ini index be17c53..583ef77 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/et-EE.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/et-EE.ini @@ -4,6 +4,8 @@ SceneSwitcher.OnNoMatch.DontSwitch="Ära vaheta" SceneSwitcher.OnNoMatch.SwitchTo="Lülitu ümber:" SceneSwitcher.CheckInterval="Kontrollige aktiivse akna pealkiri iga:" SceneSwitcher.ActiveOrNotActive="Stseen vahetaja on:" +InvalidRegex.Title="Kehtetu regulaaravaldis" +InvalidRegex.Text="Sisestatud regulaaravaldis on kehtetu." Active="Aktiivne" Inactive="Inaktiivne" Start="Alusta" @@ -18,4 +20,6 @@ OutputTimer.Stream="Lõpeta voogedastus pärast:" OutputTimer.Record="Lõpeta voogedastus pärast:" OutputTimer.Stream.StoppingIn="Voogedastus lõppeb:" OutputTimer.Record.StoppingIn="Salvestamine lõppeb:" +OutputTimer.Stream.EnableEverytime="Lülita voogedastuse taimer alati sisse" +OutputTimer.Record.EnableEverytime="Lülita salvestus taimer alati sisse" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini b/UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini index 79f760e..77ecdec 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini @@ -14,6 +14,8 @@ Stop="Gelditu" Captions="Epigrafeak (esperimentala)" Captions.AudioSource="Audio-iturburua" Captions.CurrentSystemLanguage="Sistemaren hizkuntza (%1)" +Captions.Provider="Hornitzailea" +Captions.Error.GenericFail="Huts egin du grabazioak" OutputTimer="Irteera tenporizadorea" OutputTimer.Stream="Gelditu transmisioa hau pasata:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini b/UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini index 77a4bc6..debb720 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini @@ -14,6 +14,8 @@ Stop="Pysäytä" Captions="Kuvatekstit (Experimental)" Captions.AudioSource="Äänilähde" Captions.CurrentSystemLanguage="Järjestelmän kieli (%1)" +Captions.Provider="Tarjoaja" +Captions.Error.GenericFail="Kuvatekstityksen aloittaminen epäonnistui" OutputTimer="Ulostulo-ajastin" OutputTimer.Stream="Pysäyttää lähetyksen:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini b/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini index 1ffb41b..04c8bb7 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini @@ -2,7 +2,7 @@ SceneSwitcher="Sélecteur automatique de scène" SceneSwitcher.OnNoMatch="Si aucune fenêtre ne correspond :" SceneSwitcher.OnNoMatch.DontSwitch="Ne rien faire" SceneSwitcher.OnNoMatch.SwitchTo="Basculer vers :" -SceneSwitcher.CheckInterval="Détecter le titre de la fenêtre active toutes les :" +SceneSwitcher.CheckInterval="Vérifier le titre de la fenêtre active toutes les :" SceneSwitcher.ActiveOrNotActive="Etat du sélecteur automatique :" InvalidRegex.Title="Expression invalide" InvalidRegex.Text="L'expression régulière saisie est invalide." @@ -14,6 +14,8 @@ Stop="Arrêter" Captions="Sous-titres (expérimental)" Captions.AudioSource="Source audio" Captions.CurrentSystemLanguage="Langue du système (%1)" +Captions.Provider="Sous-Titres" +Captions.Error.GenericFail="Impossible de démarrer les sous-titres" OutputTimer="Minuterie des sorties" OutputTimer.Stream="Arrêter le streaming dans :" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/he-IL.ini b/UI/frontend-plugins/frontend-tools/data/locale/he-IL.ini new file mode 100644 index 0000000..cff8e9b --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/he-IL.ini @@ -0,0 +1,25 @@ +SceneSwitcher.OnNoMatch="כאשר אין חלון מתאים:" +SceneSwitcher.OnNoMatch.DontSwitch="אל תעבור" +SceneSwitcher.OnNoMatch.SwitchTo="עבור ל:" +SceneSwitcher.CheckInterval="בדוק כותרת חלון פעיל בכל:" +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="הפעל טיימר הקלטה כל פעם" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini b/UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini index 94d6614..15b626c 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini @@ -14,6 +14,8 @@ Stop="Stop" Captions="Feliratok (Kísérleti)" Captions.AudioSource="Audio forrás" Captions.CurrentSystemLanguage="Rendszer aktuális nyelve (%1)" +Captions.Provider="Szolgáltató" +Captions.Error.GenericFail="Felirat indítása sikertelen" OutputTimer="Kimeneti időzítő" OutputTimer.Stream="Stream leállítása:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini b/UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini index 6005f8a..cd3e7ab 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini @@ -11,9 +11,11 @@ Inactive="非アクティブ" Start="開始" Stop="停止" -Captions="見出し (実験的)" +Captions="字幕 (実験的)" Captions.AudioSource="音声ソース" Captions.CurrentSystemLanguage="現在のシステム言語 (%1)" +Captions.Provider="プロバイダ" +Captions.Error.GenericFail="字幕の開始に失敗しました" OutputTimer="出力タイマー" OutputTimer.Stream="配信停止の時間設定:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini b/UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini index 0bb7dc2..522b976 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini @@ -14,6 +14,8 @@ Stop="중단" Captions="자막 (실험적 기능)" Captions.AudioSource="오디오 소스" Captions.CurrentSystemLanguage="현재 시스템 언어 (%1)" +Captions.Provider="공급자" +Captions.Error.GenericFail="자막을 시작하지 못했습니다" OutputTimer="출력 시간 설정" OutputTimer.Stream="이 시간 이후 방송 중단:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini b/UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini index 661d16c..65441e9 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini @@ -14,6 +14,8 @@ Stop="Stop" Captions="Ondertiteling (Experimenteel)" Captions.AudioSource="Audiobron" Captions.CurrentSystemLanguage="Huidige Systeemtaal (%1)" +Captions.Provider="Provider" +Captions.Error.GenericFail="Kon de ondertitelingen niet starten" OutputTimer="Uitvoertimer" OutputTimer.Stream="Stop met streamen na:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini b/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini index 79b8a6a..8bd3edb 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini @@ -14,6 +14,8 @@ Stop="Stop" Captions="Podpisy (eksperymentalne)" Captions.AudioSource="Źródła dźwięku" Captions.CurrentSystemLanguage="Obecny język systemu (%1)" +Captions.Provider="Silnik" +Captions.Error.GenericFail="Uruchomienie napisów nie powiodło się" OutputTimer="Wyłącznik czasowy" OutputTimer.Stream="Zatrzymaj stream po:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini b/UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini index e3770c5..ca9f22a 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini @@ -14,6 +14,8 @@ Stop="Parar" Captions="Legendas (Experimental)" Captions.AudioSource="Fonte de Áudio" Captions.CurrentSystemLanguage="Idioma Atual do Sistema (%1)" +Captions.Provider="Provedor" +Captions.Error.GenericFail="Falha ao iniciar legendas" OutputTimer="Temporizador de saída" OutputTimer.Stream="Parar a transmissão após:" 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 84e8e53..f8ad049 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini @@ -14,6 +14,8 @@ Stop="Остановить" Captions="Субтитры (экспериментально)" Captions.AudioSource="Источник звука" Captions.CurrentSystemLanguage="Текущий язык системы (%1)" +Captions.Provider="Поставщик" +Captions.Error.GenericFail="Не удалось запустить субтитры" OutputTimer="Таймер записи и стрима" OutputTimer.Stream="Завершить стрим через:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini b/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini index 4740873..dc6bb46 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini @@ -14,6 +14,8 @@ Stop="Stoppa" Captions="Undertexter (experimentell)" Captions.AudioSource="Ljudkälla" Captions.CurrentSystemLanguage="Aktuellt systemspråk (%1)" +Captions.Provider="Tillhandahållare" +Captions.Error.GenericFail="Det gick inte att starta undertexter" OutputTimer="Utdatatimer" OutputTimer.Stream="Sluta streama efter:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/tr-TR.ini b/UI/frontend-plugins/frontend-tools/data/locale/tr-TR.ini index 287208b..7408838 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/tr-TR.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/tr-TR.ini @@ -14,6 +14,8 @@ Stop="Durdur" Captions="Altyazı (Deneysel)" Captions.AudioSource="Ses kaynağı" Captions.CurrentSystemLanguage="Geçerli Sistem Dili (%1)" +Captions.Provider="Sağlayıcı" +Captions.Error.GenericFail="Altyazı başlatılamadı" OutputTimer="Çıkış Zamanlayıcısı" OutputTimer.Stream="Şuradan sonra yayını durdur:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini b/UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini index ba03f1b..41fe668 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini @@ -14,6 +14,8 @@ Stop="Зупинити" Captions="Субтитри (експериментально)" Captions.AudioSource="Джерело Аудіо" Captions.CurrentSystemLanguage="Поточна мова Системи (%1)" +Captions.Provider="Постачальник" +Captions.Error.GenericFail="Не вдалося запустити субтитри" OutputTimer="Таймер для Виводу" OutputTimer.Stream="Закінчити трансляцію за:" 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 eb09aa7..168a072 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini @@ -14,6 +14,8 @@ Stop="停止" Captions="标题(实验)" Captions.AudioSource="音频源" Captions.CurrentSystemLanguage="当前系统语言 (%1)" +Captions.Provider="供应商" +Captions.Error.GenericFail="启动捕获失败" OutputTimer="输出计时器" OutputTimer.Stream="停止流处理后:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini b/UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini index 17412b8..4f48415 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini @@ -14,6 +14,8 @@ Stop="停止" Captions="標題 (實驗)" Captions.AudioSource="音訊源" Captions.CurrentSystemLanguage="目前系統語言 (%1)" +Captions.Provider="提供程式" +Captions.Error.GenericFail="啟動標題失敗" OutputTimer="輸出計時器" OutputTimer.Stream="在下面時間後停止串流:" diff --git a/UI/frontend-plugins/frontend-tools/forms/captions.ui b/UI/frontend-plugins/frontend-tools/forms/captions.ui index 4f08214..f5c0b30 100644 --- a/UI/frontend-plugins/frontend-tools/forms/captions.ui +++ b/UI/frontend-plugins/frontend-tools/forms/captions.ui @@ -7,7 +7,7 @@ 0 0 519 - 140 + 152 @@ -56,6 +56,20 @@ + + + + QComboBox::InsertAlphabetically + + + + + + + Captions.Provider + + + diff --git a/UI/frontend-plugins/frontend-tools/forms/output-timer.ui b/UI/frontend-plugins/frontend-tools/forms/output-timer.ui index b9ea3d9..e56ae42 100644 --- a/UI/frontend-plugins/frontend-tools/forms/output-timer.ui +++ b/UI/frontend-plugins/frontend-tools/forms/output-timer.ui @@ -201,6 +201,13 @@ + + + + QDialogButtonBox::Close + + + diff --git a/UI/frontend-plugins/frontend-tools/frontend-tools.c b/UI/frontend-plugins/frontend-tools/frontend-tools.c index a586b88..81ee4b8 100644 --- a/UI/frontend-plugins/frontend-tools/frontend-tools.c +++ b/UI/frontend-plugins/frontend-tools/frontend-tools.c @@ -4,10 +4,8 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("frontend-tools", "en-US") -#if defined(_WIN32) || defined(__APPLE__) void InitSceneSwitcher(); void FreeSceneSwitcher(); -#endif #if defined(_WIN32) && BUILD_CAPTIONS void InitCaptions(); @@ -19,23 +17,19 @@ void FreeOutputTimer(); bool obs_module_load(void) { -#if defined(_WIN32) || defined(__APPLE__) - InitSceneSwitcher(); -#endif #if defined(_WIN32) && BUILD_CAPTIONS InitCaptions(); #endif + InitSceneSwitcher(); InitOutputTimer(); return true; } void obs_module_unload(void) { -#if defined(_WIN32) || defined(__APPLE__) - FreeSceneSwitcher(); -#endif #if defined(_WIN32) && BUILD_CAPTIONS FreeCaptions(); #endif + FreeSceneSwitcher(); FreeOutputTimer(); } diff --git a/UI/frontend-plugins/frontend-tools/output-timer.cpp b/UI/frontend-plugins/frontend-tools/output-timer.cpp index 441e929..155c8d2 100644 --- a/UI/frontend-plugins/frontend-tools/output-timer.cpp +++ b/UI/frontend-plugins/frontend-tools/output-timer.cpp @@ -22,6 +22,8 @@ OutputTimer::OutputTimer(QWidget *parent) SLOT(StreamingTimerButton())); QObject::connect(ui->outputTimerRecord, SIGNAL(clicked()), this, SLOT(RecordingTimerButton())); + QObject::connect(ui->buttonBox->button(QDialogButtonBox::Close), + SIGNAL(clicked()), this, SLOT(hide())); streamingTimer = new QTimer(this); streamingTimerDisplay = new QTimer(this); diff --git a/UI/installer/mp-installer.nsi b/UI/installer/mp-installer.nsi index 23480b1..27017cd 100644 --- a/UI/installer/mp-installer.nsi +++ b/UI/installer/mp-installer.nsi @@ -105,7 +105,7 @@ Function PreReqCheck vs2013Missing: MessageBox MB_YESNO|MB_ICONEXCLAMATION "Your system is missing runtime components that ${APPNAME} requires. Please make sure to install both vcredist_x64 and vcredist_x86. Would you like to download them?" IDYES vs2013true IDNO vs2013false vs2013true: - ExecShell "open" "http://www.microsoft.com/en-us/download/details.aspx?id=40784" + ExecShell "open" "https://obsproject.com/visual-studio-2013-runtimes" vs2013false: Quit vs2013OK: @@ -273,7 +273,7 @@ SectionEnd !ifdef FULL SectionGroup /e "Plugins" SecPlugins - Section "Browser plugin" SecPlugins_Browser + Section "Browser Source" SecPlugins_Browser ; Set Section properties SetOverwrite on AllowSkipFiles off @@ -292,16 +292,16 @@ SectionGroup /e "Plugins" SecPlugins SectionEnd !ifdef REALSENSE_PLUGIN - Section /o "Realsense plugin" SecPlugins_Realsense + Section /o "Realsense Source" SecPlugins_Realsense SetOverwrite on AllowSkipFiles off SetShellVarContext all SetOutPath "$INSTDIR\obs-plugins" - File /r "new\realsense\32bit" + File /r "new\realsense\obs-plugins\32bit" ${if} ${RunningX64} - File /r "new\realsense\64bit" + File /r "new\realsense\obs-plugins\64bit" ${endif} SetOutPath "$INSTDIR\data\obs-plugins" diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index d233d77..01db714 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -61,7 +61,7 @@ static log_handler_t def_log_handler; static string currentLogFile; static string lastLogFile; -static bool portable_mode = false; +bool portable_mode = false; static bool log_verbose = false; static bool unfiltered_log = false; bool opt_start_streaming = false; @@ -69,6 +69,8 @@ bool opt_start_recording = false; bool opt_studio_mode = false; bool opt_start_replaybuffer = false; bool opt_minimize_tray = false; +bool opt_allow_opengl = false; +bool opt_always_on_top = false; string opt_starting_collection; string opt_starting_profile; string opt_starting_scene; @@ -162,6 +164,7 @@ QObject *CreateShortcutFilter() case Qt::Key_Return: if (dialog && pressed) return false; + /* Falls through. */ default: hotkey.key = obs_key_from_virtual_key( event->nativeVirtualKey()); @@ -342,11 +345,11 @@ static void do_log(int log_level, const char *msg, va_list args, void *param) def_log_handler(log_level, msg, args2, nullptr); #endif - if (too_many_repeated_entries(logFile, msg, str)) - return; - - if (log_level <= LOG_INFO || log_verbose) + if (log_level <= LOG_INFO || log_verbose) { + if (too_many_repeated_entries(logFile, msg, str)) + return; LogStringChunk(logFile, str); + } #if defined(_WIN32) && defined(OBS_DEBUGBREAK_ON_ERROR) if (log_level <= LOG_ERROR && IsDebuggerPresent()) @@ -408,6 +411,11 @@ bool OBSApp::InitGlobalConfigDefaults() config_set_default_bool(globalConfig, "BasicWindow", "ShowStatusBar", true); +#ifdef _WIN32 + config_set_default_bool(globalConfig, "Audio", "DisableAudioDucking", + true); +#endif + #ifdef __APPLE__ config_set_default_bool(globalConfig, "Video", "DisableOSXVSync", true); config_set_default_bool(globalConfig, "Video", "ResetOSXVSyncOnExit", @@ -576,6 +584,7 @@ static string GetSceneCollectionFileFromName(const char *name) bool OBSApp::InitGlobalConfig() { char path[512]; + bool changed = false; int len = GetConfigPath(path, sizeof(path), "obs-studio/global.ini"); @@ -599,6 +608,7 @@ bool OBSApp::InitGlobalConfig() config_set_string(globalConfig, "Basic", "SceneCollectionFile", path.c_str()); + changed = true; } } @@ -610,9 +620,24 @@ bool OBSApp::InitGlobalConfig() opt_starting_profile.c_str()); config_set_string(globalConfig, "Basic", "ProfileDir", path.c_str()); + changed = true; } } + if (!config_has_user_value(globalConfig, "General", "Pre19Defaults")) { + uint32_t lastVersion = config_get_int(globalConfig, "General", + "LastVersion"); + bool useOldDefaults = lastVersion && + lastVersion < MAKE_SEMANTIC_VERSION(19, 0, 0); + + config_set_bool(globalConfig, "General", "Pre19Defaults", + useOldDefaults); + changed = true; + } + + if (changed) + config_save_safe(globalConfig, "tmp", nullptr); + return InitGlobalConfigDefaults(); } @@ -731,6 +756,13 @@ OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store) OBSApp::~OBSApp() { +#ifdef _WIN32 + bool disableAudioDucking = config_get_bool(globalConfig, "Audio", + "DisableAudioDucking"); + if (disableAudioDucking) + DisableAudioDucking(false); +#endif + #ifdef __APPLE__ bool vsyncDiabled = config_get_bool(globalConfig, "Video", "DisableOSXVSync"); @@ -849,6 +881,13 @@ void OBSApp::AppInit() config_set_default_string(globalConfig, "Basic", "SceneCollectionFile", Str("Untitled")); +#ifdef _WIN32 + bool disableAudioDucking = config_get_bool(globalConfig, "Audio", + "DisableAudioDucking"); + if (disableAudioDucking) + DisableAudioDucking(true); +#endif + #ifdef __APPLE__ if (config_get_bool(globalConfig, "Video", "DisableOSXVSync")) EnableOSXVSync(false); @@ -901,6 +940,8 @@ bool OBSApp::OBSInit() blog(LOG_INFO, "Portable mode: %s", portable_mode ? "true" : "false"); + setQuitOnLastWindowClosed(false); + mainWindow = new OBSBasic(); mainWindow->setAttribute(Qt::WA_DeleteOnClose, true); @@ -1183,8 +1224,16 @@ static void create_log_file(fstream &logFile) dst << "obs-studio/logs/" << currentLogFile.c_str(); BPtr path(GetConfigPathPtr(dst.str().c_str())); + +#ifdef _WIN32 + BPtr wpath; + os_utf8_to_wcs_ptr(path, 0, &wpath); + logFile.open(wpath, + ios_base::in | ios_base::out | ios_base::trunc); +#else logFile.open(path, ios_base::in | ios_base::out | ios_base::trunc); +#endif if (logFile.is_open()) { delete_oldest_file("obs-studio/logs"); @@ -1287,6 +1336,46 @@ static int run_program(fstream &logFile, int argc, char *argv[]) program.installTranslator(&translator); +#ifdef _WIN32 + /* --------------------------------------- */ + /* check and warn if already running */ + + bool already_running = false; + RunOnceMutex rom = GetRunOnceMutex(already_running); + + if (already_running) { + blog(LOG_WARNING, "\n================================"); + blog(LOG_WARNING, "Warning: OBS is already running!"); + blog(LOG_WARNING, "================================\n"); + + QMessageBox::StandardButtons buttons( + QMessageBox::Yes | QMessageBox::Cancel); + QMessageBox mb(QMessageBox::Question, + QTStr("AlreadyRunning.Title"), + QTStr("AlreadyRunning.Text"), + buttons, + nullptr); + mb.setButtonText(QMessageBox::Yes, + QTStr("AlreadyRunning.LaunchAnyway")); + mb.setButtonText(QMessageBox::Cancel, QTStr("Cancel")); + mb.setDefaultButton(QMessageBox::Cancel); + + 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; + } + + blog(LOG_WARNING, "User is now running a secondary " + "instance of OBS!"); + } + + /* --------------------------------------- */ +#endif + if (!program.OBSInit()) return 0; @@ -1302,7 +1391,7 @@ static int run_program(fstream &logFile, int argc, char *argv[]) return ret; } -#define MAX_CRASH_REPORT_SIZE (50 * 1024) +#define MAX_CRASH_REPORT_SIZE (150 * 1024) #ifdef _WIN32 @@ -1326,8 +1415,16 @@ static void main_crash_handler(const char *format, va_list args, void *param) BPtr path(GetConfigPathPtr(name.c_str())); fstream file; - file.open(path, ios_base::in | ios_base::out | ios_base::trunc | + +#ifdef _WIN32 + BPtr wpath; + os_utf8_to_wcs_ptr(path, 0, &wpath); + file.open(wpath, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary); +#else + file.open(path, ios_base::in | ios_base::out | ios_base::trunc | + ios_base::binary); +#endif file << text; file.close(); @@ -1739,6 +1836,7 @@ int main(int argc, char *argv[]) #endif #ifdef _WIN32 + SetErrorMode(SEM_FAILCRITICALERRORS); load_debug_privilege(); base_set_crash_handler(main_crash_handler, nullptr); #endif @@ -1756,6 +1854,9 @@ int main(int argc, char *argv[]) } else if (arg_is(argv[i], "--verbose", nullptr)) { log_verbose = true; + } else if (arg_is(argv[i], "--always-on-top", nullptr)) { + opt_always_on_top = true; + } else if (arg_is(argv[i], "--unfiltered_log", nullptr)) { unfiltered_log = true; @@ -1783,6 +1884,9 @@ int main(int argc, char *argv[]) } else if (arg_is(argv[i], "--studio-mode", nullptr)) { opt_studio_mode = true; + } else if (arg_is(argv[i], "--allow-opengl", nullptr)) { + opt_allow_opengl = true; + } else if (arg_is(argv[i], "--help", "-h")) { std::cout << "--help, -h: Get list of available commands.\n\n" << @@ -1797,7 +1901,9 @@ int main(int argc, char *argv[]) "--minimize-to-tray: Minimize to system tray.\n" << "--portable, -p: Use portable mode.\n\n" << "--verbose: Make log more verbose.\n" << + "--always-on-top: Start in 'always on top' mode.\n\n" << "--unfiltered_log: Make log unfiltered.\n\n" << + "--allow-opengl: Allow OpenGL on Windows.\n\n" << "--version, -V: Get current version.\n"; exit(0); diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index 381f9d6..53043ac 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -176,9 +176,12 @@ static inline int GetProfilePath(char *path, size_t size, const char *file) return window->GetProfilePath(path, size, file); } +extern bool portable_mode; extern bool opt_start_streaming; extern bool opt_start_recording; extern bool opt_start_replaybuffer; extern bool opt_minimize_tray; extern bool opt_studio_mode; +extern bool opt_allow_opengl; +extern bool opt_always_on_top; extern std::string opt_starting_scene; diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index 6951bc9..030fded 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -317,3 +317,22 @@ void obs_frontend_pop_ui_translation(void) if (callbacks_valid()) c->obs_frontend_pop_ui_translation(); } + +void obs_frontend_set_streaming_service(obs_service_t *service) +{ + if (callbacks_valid()) + c->obs_frontend_set_streaming_service(service); +} + +obs_service_t* obs_frontend_get_streaming_service(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_streaming_service() + : nullptr; +} + +void obs_frontend_save_streaming_service(void) +{ + if (callbacks_valid()) + c->obs_frontend_save_streaming_service(); +} diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index 7be113c..9918705 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -137,6 +137,10 @@ EXPORT void obs_frontend_push_ui_translation( obs_frontend_translate_ui_cb translate); EXPORT void obs_frontend_pop_ui_translation(void); +EXPORT void obs_frontend_set_streaming_service(obs_service_t *service); +EXPORT obs_service_t* obs_frontend_get_streaming_service(void); +EXPORT void obs_frontend_save_streaming_service(void); + /* ------------------------------------------------------------------------- */ #ifdef __cplusplus diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index 5784eeb..d9e4522 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -70,6 +70,11 @@ struct obs_frontend_callbacks { obs_frontend_translate_ui_cb translate)=0; virtual void obs_frontend_pop_ui_translation(void)=0; + virtual void obs_frontend_set_streaming_service( + obs_service_t *service)=0; + virtual obs_service_t *obs_frontend_get_streaming_service(void)=0; + virtual void obs_frontend_save_streaming_service()=0; + virtual void on_load(obs_data_t *settings)=0; virtual void on_save(obs_data_t *settings)=0; virtual void on_event(enum obs_frontend_event event)=0; diff --git a/UI/platform-windows.cpp b/UI/platform-windows.cpp index 7386375..524361b 100644 --- a/UI/platform-windows.cpp +++ b/UI/platform-windows.cpp @@ -30,6 +30,13 @@ using namespace std; #include #include #include +#include +#include +#include + +#include +#include +#include static inline bool check_path(const char* data, const char *path, string &output) @@ -216,3 +223,114 @@ void SetWin32DropStyle(QWidget *window) ex_style |= WS_EX_ACCEPTFILES; SetWindowLongPtr(hwnd, GWL_EXSTYLE, ex_style); } + +bool DisableAudioDucking(bool disable) +{ + ComPtr devEmum; + ComPtr device; + ComPtr sessionManager2; + ComPtr sessionControl; + ComPtr sessionControl2; + + HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator), + nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), + (void **)&devEmum); + if (FAILED(result)) + return false; + + result = devEmum->GetDefaultAudioEndpoint(eRender, eConsole, &device); + if (FAILED(result)) + return false; + + result = device->Activate(__uuidof(IAudioSessionManager2), + CLSCTX_INPROC_SERVER, nullptr, + (void **)&sessionManager2); + if (FAILED(result)) + return false; + + result = sessionManager2->GetAudioSessionControl(nullptr, 0, + &sessionControl); + if (FAILED(result)) + return false; + + result = sessionControl->QueryInterface(&sessionControl2); + if (FAILED(result)) + return false; + + result = sessionControl2->SetDuckingPreference(disable); + return SUCCEEDED(result); +} + +uint64_t CurrentMemoryUsage() +{ + PROCESS_MEMORY_COUNTERS pmc = {}; + pmc.cb = sizeof(pmc); + + if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + return 0; + + return (uint64_t)pmc.WorkingSetSize; +} + +struct RunOnceMutexData { + WinHandle handle; + + inline RunOnceMutexData(HANDLE h) : handle(h) {} +}; + +RunOnceMutex::RunOnceMutex(RunOnceMutex &&rom) +{ + delete data; + data = rom.data; + rom.data = nullptr; +} + +RunOnceMutex::~RunOnceMutex() +{ + delete data; +} + +RunOnceMutex &RunOnceMutex::operator=(RunOnceMutex &&rom) +{ + delete data; + data = rom.data; + rom.data = nullptr; + return *this; +} + +RunOnceMutex GetRunOnceMutex(bool &already_running) +{ + string name; + + if (!portable_mode) { + name = "OBSStudioCore"; + } else { + char path[500]; + *path = 0; + GetConfigPath(path, sizeof(path), ""); + name = "OBSStudioPortable"; + name += path; + } + + BPtr wname; + os_utf8_to_wcs_ptr(name.c_str(), name.size(), &wname); + + if (wname) { + wchar_t *temp = wname; + while (*temp) { + if (!iswalnum(*temp)) + *temp = L'_'; + temp++; + } + } + + HANDLE h = OpenMutexW(SYNCHRONIZE, false, wname.Get()); + already_running = !!h; + + if (!already_running) + h = CreateMutexW(nullptr, false, wname.Get()); + + RunOnceMutex rom(h ? new RunOnceMutexData(h) : nullptr); + return rom; +} diff --git a/UI/platform.hpp b/UI/platform.hpp index 1b1344b..ca0780b 100644 --- a/UI/platform.hpp +++ b/UI/platform.hpp @@ -42,6 +42,24 @@ uint32_t GetWindowsVersion(); void SetAeroEnabled(bool enable); void SetProcessPriority(const char *priority); void SetWin32DropStyle(QWidget *window); +bool DisableAudioDucking(bool disable); +uint64_t CurrentMemoryUsage(); + +struct RunOnceMutexData; + +class RunOnceMutex { + RunOnceMutexData *data = nullptr; +public: + RunOnceMutex(RunOnceMutexData *data_) : data(data_) {} + RunOnceMutex(const RunOnceMutex &rom) = delete; + RunOnceMutex(RunOnceMutex &&rom); + ~RunOnceMutex(); + + RunOnceMutex &operator=(const RunOnceMutex &rom) = delete; + RunOnceMutex &operator=(RunOnceMutex &&rom); +}; + +RunOnceMutex GetRunOnceMutex(bool &already_running); #endif #ifdef __APPLE__ diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index f8e8faf..17bcbfd 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -291,6 +291,11 @@ void OBSPropertiesView::AddPath(obs_property_t *prop, QFormLayout *layout, QLineEdit *edit = new QLineEdit(); QPushButton *button = new QPushButton(QTStr("Browse")); + if (!obs_property_enabled(prop)) { + edit->setEnabled(false); + button->setEnabled(false); + } + edit->setText(QT_UTF8(val)); edit->setReadOnly(true); edit->setToolTip(QT_UTF8(obs_property_long_description(prop))); @@ -316,6 +321,9 @@ void OBSPropertiesView::AddInt(obs_property_t *prop, QFormLayout *layout, int val = (int)obs_data_get_int(settings, name); QSpinBox *spin = new QSpinBox(); + if (!obs_property_enabled(prop)) + spin->setEnabled(false); + int minVal = obs_property_int_min(prop); int maxVal = obs_property_int_max(prop); int stepVal = obs_property_int_step(prop); @@ -362,6 +370,9 @@ void OBSPropertiesView::AddFloat(obs_property_t *prop, QFormLayout *layout, double val = obs_data_get_double(settings, name); QDoubleSpinBox *spin = new QDoubleSpinBox(); + if (!obs_property_enabled(prop)) + spin->setEnabled(false); + double minVal = obs_property_float_min(prop); double maxVal = obs_property_float_max(prop); double stepVal = obs_property_float_step(prop); @@ -554,6 +565,9 @@ void OBSPropertiesView::AddEditableList(obs_property_t *prop, QListWidget *list = new QListWidget(); size_t count = obs_data_array_count(array); + if (!obs_property_enabled(prop)) + list->setEnabled(false); + list->setSortingEnabled(false); list->setSelectionMode(QAbstractItemView::ExtendedSelection); list->setToolTip(QT_UTF8(obs_property_long_description(prop))); @@ -609,6 +623,11 @@ void OBSPropertiesView::AddColor(obs_property_t *prop, QFormLayout *layout, long long val = obs_data_get_int(settings, name); QColor color = color_from_int(val); + if (!obs_property_enabled(prop)) { + button->setEnabled(false); + colorLabel->setEnabled(false); + } + button->setText(QTStr("Basic.PropertiesWindow.SelectColor")); button->setToolTip(QT_UTF8(obs_property_long_description(prop))); @@ -671,6 +690,11 @@ void OBSPropertiesView::AddFont(obs_property_t *prop, QFormLayout *layout, QLabel *fontLabel = new QLabel; QFont font; + if (!obs_property_enabled(prop)) { + button->setEnabled(false); + fontLabel->setEnabled(false); + } + font = fontLabel->font(); MakeQFont(font_obj, font, true); @@ -1356,6 +1380,9 @@ void OBSPropertiesView::AddProperty(obs_property_t *property, label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); } + if (label && !obs_property_enabled(property)) + label->setEnabled(false); + if (!widget) return; diff --git a/UI/qt-wrappers.cpp b/UI/qt-wrappers.cpp index 5ab41a9..eac5ea0 100644 --- a/UI/qt-wrappers.cpp +++ b/UI/qt-wrappers.cpp @@ -16,6 +16,8 @@ ******************************************************************************/ #include "qt-wrappers.hpp" +#include "obs-app.hpp" + #include #include #include @@ -42,6 +44,51 @@ void OBSErrorBox(QWidget *parent, const char *msg, ...) va_end(args); } +QMessageBox::StandardButton OBSMessageBox::question( + QWidget *parent, + const QString &title, + const QString &text, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) +{ + QMessageBox mb(QMessageBox::Question, + title, text, buttons, + parent); + mb.setDefaultButton(defaultButton); + if (buttons & QMessageBox::Ok) \ + mb.setButtonText(QMessageBox::Ok, QTStr("OK")); +#define translate_button(x) \ + if (buttons & QMessageBox::x) \ + mb.setButtonText(QMessageBox::x, QTStr(#x)); + translate_button(Open); + translate_button(Save); + translate_button(Cancel); + translate_button(Close); + translate_button(Discard); + translate_button(Apply); + translate_button(Reset); + translate_button(Yes); + translate_button(No); + translate_button(No); + translate_button(Abort); + translate_button(Retry); + translate_button(Ignore); +#undef translate_button + return (QMessageBox::StandardButton)mb.exec(); +} + +void OBSMessageBox::information( + QWidget *parent, + const QString &title, + const QString &text) +{ + QMessageBox mb(QMessageBox::Information, + title, text, QMessageBox::Ok, + parent); + mb.setButtonText(QMessageBox::Ok, QTStr("OK")); + mb.exec(); +} + void QTToGSWindow(WId windowId, gs_window &gswindow) { #ifdef _WIN32 diff --git a/UI/qt-wrappers.hpp b/UI/qt-wrappers.hpp index aec516e..7899387 100644 --- a/UI/qt-wrappers.hpp +++ b/UI/qt-wrappers.hpp @@ -17,6 +17,7 @@ #pragma once +#include #include #include @@ -29,8 +30,23 @@ class QDataStream; class QWidget; class QLayout; +class QString; struct gs_window; +class OBSMessageBox { +public: + static QMessageBox::StandardButton question( + QWidget *parent, + const QString &title, + const QString &text, + QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons( QMessageBox::Yes | QMessageBox::No ), + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static void information( + QWidget *parent, + const QString &title, + const QString &text); +}; + void OBSErrorBox(QWidget *parent, const char *msg, ...); void QTToGSWindow(WId windowId, gs_window &gswindow); diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index e158475..7d0e45c 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -2,6 +2,10 @@ if(NOT ENABLE_WIN_UPDATER) return() endif() +if(DISABLE_UPDATE_MODULE) + return() +endif() + if(NOT DEFINED STATIC_ZLIB_PATH OR "${STATIC_ZLIB_PATH}" STREQUAL "") message(STATUS "STATIC_ZLIB_PATH not set, windows updater disabled") return() diff --git a/UI/win-update/updater/http.cpp b/UI/win-update/updater/http.cpp index cbea2b3..adf5652 100644 --- a/UI/win-update/updater/http.cpp +++ b/UI/win-update/updater/http.cpp @@ -36,10 +36,10 @@ public: static bool ReadZippedHTTPData(string &responseBuf, z_stream *strm, string &zipBuf, const uint8_t *buffer, DWORD outSize) { - do { - strm->avail_in = outSize; - strm->next_in = buffer; + strm->avail_in = outSize; + strm->next_in = buffer; + do { strm->avail_out = (uInt)zipBuf.size(); strm->next_out = (Bytef *)zipBuf.data(); @@ -293,10 +293,10 @@ static bool ReadHTTPZippedFile(z_stream *strm, HANDLE updateFile, string &zipBuf, const uint8_t *buffer, DWORD outSize, int *responseCode) { - do { - strm->avail_in = outSize; - strm->next_in = buffer; + strm->avail_in = outSize; + strm->next_in = buffer; + do { strm->avail_out = (uInt)zipBuf.size(); strm->next_out = (Bytef *)zipBuf.data(); diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index 5ada461..10f850d 100644 --- a/UI/win-update/updater/updater.cpp +++ b/UI/win-update/updater/updater.cpp @@ -580,6 +580,27 @@ static bool NonCorePackageInstalled(const char *name) return false; } +static inline bool is_64bit_windows(void) +{ +#ifdef _WIN64 + return true; +#else + BOOL x86 = false; + bool success = !!IsWow64Process(GetCurrentProcess(), &x86); + return success && !!x86; +#endif +} + +static inline bool is_64bit_file(const char *file) +{ + if (!file) + return false; + + return strstr(file, "64bit") != nullptr || + strstr(file, "64.dll") != nullptr || + strstr(file, "64.exe") != nullptr; +} + #define UTF8ToWideBuf(wide, utf8) UTF8ToWide(wide, _countof(wide), utf8) #define WideToUTF8Buf(utf8, wide) WideToUTF8(utf8, _countof(utf8), wide) @@ -592,6 +613,8 @@ static bool AddPackageUpdateFiles(json_t *root, size_t idx, json_t *name = json_object_get(package, "name"); json_t *files = json_object_get(package, "files"); + bool isWin64 = is_64bit_windows(); + if (!json_is_array(files)) return true; if (!json_is_string(name)) @@ -628,6 +651,9 @@ static bool AddPackageUpdateFiles(json_t *root, size_t idx, if (strlen(hashUTF8) != BLAKE2_HASH_LENGTH * 2) continue; + if (!isWin64 && is_64bit_file(fileUTF8)) + continue; + /* convert strings to wide */ wchar_t sourceURL[1024]; @@ -972,10 +998,11 @@ static bool Update(wchar_t *cmdLine) } StringCbCopy(lpAppDataPath, sizeof(lpAppDataPath), pOut); - StringCbCat(lpAppDataPath, sizeof(lpAppDataPath), - L"\\obs-studio"); } + StringCbCat(lpAppDataPath, sizeof(lpAppDataPath), + L"\\obs-studio"); + /* ------------------------------------- * * Get download path */ @@ -987,18 +1014,18 @@ static bool Update(wchar_t *cmdLine) StringCbPrintf(manifestPath, sizeof(manifestPath), L"%s\\updates\\manifest.json", lpAppDataPath); - if (!GetTempPathW(_countof(tempPath), tempPath)) { + if (!GetTempPathW(_countof(tempDirName), tempDirName)) { Status(L"Update failed: Failed to get temp path: %ld", GetLastError()); return false; } - if (!GetTempFileNameW(tempDirName, L"obs-studio", 0, tempDirName)) { + if (!GetTempFileNameW(tempDirName, L"obs-studio", 0, tempPath)) { Status(L"Update failed: Failed to create temp dir name: %ld", GetLastError()); return false; } - StringCbCat(tempPath, sizeof(tempPath), tempDirName); + DeleteFile(tempPath); CreateDirectory(tempPath, nullptr); /* ------------------------------------- * @@ -1086,7 +1113,8 @@ static bool Update(wchar_t *cmdLine) * Send file hashes */ string newManifest; - { + + if (json_array_size(files) > 0) { char *post_body = json_dumps(files, JSON_COMPACT); int responseCode; @@ -1117,6 +1145,8 @@ static bool Update(wchar_t *cmdLine) responseCode); return false; } + } else { + newManifest = "[]"; } /* ------------------------------------- * diff --git a/UI/win-update/win-update.cpp b/UI/win-update/win-update.cpp index 6cfec73..510fddf 100644 --- a/UI/win-update/win-update.cpp +++ b/UI/win-update/win-update.cpp @@ -1,6 +1,7 @@ #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" @@ -479,7 +480,7 @@ void GenerateGUID(string &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 +491,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 +537,7 @@ try { string text; string error; string signature; - CryptProvider provider; + CryptProvider localProvider; BYTE manifestHash[BLAKE2_HASH_LENGTH]; bool updatesAvailable = false; bool success; @@ -579,7 +580,7 @@ try { /* ----------------------------------- * * create signature provider */ - if (!CryptAcquireContext(&provider, + if (!CryptAcquireContext(&localProvider, nullptr, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, @@ -587,7 +588,7 @@ try { throw strprintf("CryptAcquireContext failed: %lu", GetLastError()); - ::provider = provider; + provider = localProvider; /* ----------------------------------- * * avoid downloading manifest again */ @@ -638,7 +639,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 +659,11 @@ try { if (responseCode == 200) { if (!QuickWriteFile(manifestPath, text.data(), text.size())) throw strprintf("Could not write file '%s'", - manifestPath); + manifestPath.Get()); } else { if (!QuickReadFile(manifestPath, text)) throw strprintf("Could not read file '%s'", - manifestPath); + manifestPath.Get()); } /* ----------------------------------- * @@ -764,7 +765,7 @@ try { QString msg = QTStr("Updater.FailedToLaunch"); info(msg, msg); throw strprintf("Can't launch updater '%s': %d", - updateFilePath, GetLastError()); + updateFilePath.Get(), GetLastError()); } /* force OBS to perform another update check immediately after updating diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index ad3977d..33fe0cb 100644 --- a/UI/window-basic-adv-audio.cpp +++ b/UI/window-basic-adv-audio.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include "window-basic-adv-audio.hpp" #include "window-basic-main.hpp" @@ -65,11 +67,20 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) scrollArea->setWidget(widget); scrollArea->setWidgetResizable(true); + QPushButton *closeButton = new QPushButton(QTStr("Close")); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(); + buttonLayout->addWidget(closeButton); + vlayout = new QVBoxLayout; vlayout->setContentsMargins(11, 11, 11, 11); vlayout->addWidget(scrollArea); + vlayout->addLayout(buttonLayout); setLayout(vlayout); + connect(closeButton, &QPushButton::clicked, [this] () {close();}); + installEventFilter(CreateShortcutFilter()); /* enum user scene/sources */ diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp new file mode 100644 index 0000000..04b44e1 --- /dev/null +++ b/UI/window-basic-auto-config-test.cpp @@ -0,0 +1,1124 @@ +#include + +#include + +#include +#include +#include +#include +#include + +#include "window-basic-auto-config.hpp" +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" +#include "obs-app.hpp" + +#include "ui_AutoConfigTestPage.h" + +#define wiz reinterpret_cast(wizard()) + +using namespace std; + +/* ------------------------------------------------------------------------- */ + +class TestMode { + obs_video_info ovi; + OBSSource source[6]; + + static void render_rand(void *, uint32_t cx, uint32_t cy) + { + gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID); + gs_eparam_t *randomvals[3] = { + gs_effect_get_param_by_name(solid, "randomvals1"), + gs_effect_get_param_by_name(solid, "randomvals2"), + gs_effect_get_param_by_name(solid, "randomvals3") + }; + + struct vec4 r; + + for (int i = 0; i < 3; i++) { + vec4_set(&r, + rand_float(true) * 100.0f, + rand_float(true) * 100.0f, + rand_float(true) * 50000.0f + 10000.0f, + 0.0f); + gs_effect_set_vec4(randomvals[i], &r); + } + + while (gs_effect_loop(solid, "Random")) + gs_draw_sprite(nullptr, 0, cx, cy); + } + +public: + inline TestMode() + { + obs_get_video_info(&ovi); + obs_add_main_render_callback(render_rand, this); + + for (uint32_t i = 0; i < 6; i++) { + source[i] = obs_get_output_source(i); + obs_source_release(source[i]); + obs_set_output_source(i, nullptr); + } + } + + inline ~TestMode() + { + for (uint32_t i = 0; i < 6; i++) + obs_set_output_source(i, source[i]); + + obs_remove_main_render_callback(render_rand, this); + obs_reset_video(&ovi); + } + + inline void SetVideo(int cx, int cy, int fps_num, int fps_den) + { + obs_video_info newOVI = ovi; + + newOVI.output_width = (uint32_t)cx; + newOVI.output_height = (uint32_t)cy; + newOVI.fps_num = (uint32_t)fps_num; + newOVI.fps_den = (uint32_t)fps_den; + + obs_reset_video(&newOVI); + } +}; + +/* ------------------------------------------------------------------------- */ + +#define TEST_STR(x) "Basic.AutoConfig.TestPage." x +#define SUBTITLE_TESTING TEST_STR("Subtitle.Testing") +#define SUBTITLE_COMPLETE TEST_STR("Subtitle.Complete") +#define TEST_BW TEST_STR("TestingBandwidth") +#define TEST_BW_CONNECTING TEST_STR("TestingBandwidth.Connecting") +#define TEST_BW_CONNECT_FAIL TEST_STR("TestingBandwidth.ConnectFailed") +#define TEST_BW_SERVER TEST_STR("TestingBandwidth.Server") +#define TEST_RES TEST_STR("TestingRes") +#define TEST_RES_VAL TEST_STR("TestingRes.Resolution") +#define TEST_RES_FAIL TEST_STR("TestingRes.Fail") +#define TEST_SE TEST_STR("TestingStreamEncoder") +#define TEST_RE TEST_STR("TestingRecordingEncoder") +#define TEST_RESULT_SE TEST_STR("Result.StreamingEncoder") +#define TEST_RESULT_RE TEST_STR("Result.RecordingEncoder") + +void AutoConfigTestPage::StartBandwidthStage() +{ + ui->progressLabel->setText(QTStr(TEST_BW)); + testThread = std::thread([this] () {TestBandwidthThread();}); +} + +void AutoConfigTestPage::StartStreamEncoderStage() +{ + ui->progressLabel->setText(QTStr(TEST_SE)); + testThread = std::thread([this] () {TestStreamEncoderThread();}); +} + +void AutoConfigTestPage::StartRecordingEncoderStage() +{ + ui->progressLabel->setText(QTStr(TEST_RE)); + testThread = std::thread([this] () {TestRecordingEncoderThread();}); +} + +void AutoConfigTestPage::GetServers(std::vector &servers) +{ + OBSData settings = obs_data_create(); + obs_data_release(settings); + obs_data_set_string(settings, "service", wiz->serviceName.c_str()); + + obs_properties_t *ppts = obs_get_service_properties("rtmp_common"); + obs_property_t *p = obs_properties_get(ppts, "service"); + obs_property_modified(p, settings); + + p = obs_properties_get(ppts, "server"); + size_t count = obs_property_list_item_count(p); + servers.reserve(count); + + for (size_t i = 0; i < count; i++) { + const char *name = obs_property_list_item_name(p, i); + const char *server = obs_property_list_item_string(p, i); + + if (wiz->CanTestServer(name)) { + ServerInfo info(name, server); + servers.push_back(info); + } + } + + obs_properties_destroy(ppts); +} + +static inline void string_depad_key(string &key) +{ + while (!key.empty()) { + char ch = key.back(); + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') + key.pop_back(); + else + break; + } +} + +void AutoConfigTestPage::TestBandwidthThread() +{ + bool connected = false; + bool stopped = false; + + TestMode testMode; + testMode.SetVideo(128, 128, 60, 1); + + QMetaObject::invokeMethod(this, "Progress", Q_ARG(int, 0)); + + /* + * create encoders + * create output + * test for 10 seconds + */ + + QMetaObject::invokeMethod(this, "UpdateMessage", + Q_ARG(QString, QStringLiteral(""))); + + /* -----------------------------------*/ + /* create obs objects */ + + const char *serverType = wiz->customServer + ? "rtmp_custom" + : "rtmp_common"; + + OBSEncoder vencoder = obs_video_encoder_create("obs_x264", + "test_x264", nullptr, nullptr); + OBSEncoder aencoder = obs_audio_encoder_create("ffmpeg_aac", + "test_aac", nullptr, 0, nullptr); + OBSService service = obs_service_create(serverType, + "test_service", nullptr, nullptr); + OBSOutput output = obs_output_create("rtmp_output", + "test_stream", nullptr, nullptr); + obs_output_release(output); + obs_encoder_release(vencoder); + obs_encoder_release(aencoder); + obs_service_release(service); + + /* -----------------------------------*/ + /* configure settings */ + + // service: "service", "server", "key" + // vencoder: "bitrate", "rate_control", + // obs_service_apply_encoder_settings + // aencoder: "bitrate" + // output: "bind_ip" via main config -> "Output", "BindIP" + // obs_output_set_service + + OBSData service_settings = obs_data_create(); + OBSData vencoder_settings = obs_data_create(); + OBSData aencoder_settings = obs_data_create(); + OBSData output_settings = obs_data_create(); + obs_data_release(service_settings); + obs_data_release(vencoder_settings); + obs_data_release(aencoder_settings); + obs_data_release(output_settings); + + std::string key = wiz->key; + if (wiz->service == AutoConfig::Service::Twitch) { + string_depad_key(key); + key += "?bandwidthtest"; + } + + obs_data_set_string(service_settings, "service", + wiz->serviceName.c_str()); + obs_data_set_string(service_settings, "key", key.c_str()); + + obs_data_set_int(vencoder_settings, "bitrate", wiz->startingBitrate); + obs_data_set_string(vencoder_settings, "rate_control", "CBR"); + obs_data_set_string(vencoder_settings, "preset", "veryfast"); + obs_data_set_int(vencoder_settings, "keyint_sec", 2); + + obs_data_set_int(aencoder_settings, "bitrate", 32); + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + const char *bind_ip = config_get_string(main->Config(), "Output", + "BindIP"); + obs_data_set_string(output_settings, "bind_ip", bind_ip); + + /* -----------------------------------*/ + /* determine which servers to test */ + + std::vector servers; + if (wiz->customServer) + servers.emplace_back(wiz->server.c_str(), wiz->server.c_str()); + else + GetServers(servers); + + /* just use the first server if it only has one alternate server */ + if (servers.size() < 3) + servers.resize(1); + + /* -----------------------------------*/ + /* apply settings */ + + obs_service_update(service, service_settings); + obs_service_apply_encoder_settings(service, + vencoder_settings, aencoder_settings); + + obs_encoder_update(vencoder, vencoder_settings); + obs_encoder_update(aencoder, aencoder_settings); + obs_output_update(output, output_settings); + + /* -----------------------------------*/ + /* connect encoders/services/outputs */ + + obs_encoder_set_video(vencoder, obs_get_video()); + obs_encoder_set_audio(aencoder, obs_get_audio()); + + obs_output_set_video_encoder(output, vencoder); + obs_output_set_audio_encoder(output, aencoder, 0); + + obs_output_set_service(output, service); + + /* -----------------------------------*/ + /* connect signals */ + + auto on_started = [&] () + { + unique_lock lock(m); + connected = true; + stopped = false; + cv.notify_one(); + }; + + auto on_stopped = [&] () + { + unique_lock lock(m); + connected = false; + stopped = true; + cv.notify_one(); + }; + + using on_started_t = decltype(on_started); + using on_stopped_t = decltype(on_stopped); + + auto pre_on_started = [] (void *data, calldata_t *) + { + on_started_t &on_started = + *reinterpret_cast(data); + on_started(); + }; + + auto pre_on_stopped = [] (void *data, calldata_t *) + { + on_stopped_t &on_stopped = + *reinterpret_cast(data); + on_stopped(); + }; + + signal_handler *sh = obs_output_get_signal_handler(output); + signal_handler_connect(sh, "start", pre_on_started, &on_started); + signal_handler_connect(sh, "stop", pre_on_stopped, &on_stopped); + + /* -----------------------------------*/ + /* test servers */ + + bool success = false; + + for (size_t i = 0; i < servers.size(); i++) { + auto &server = servers[i]; + + connected = false; + stopped = false; + + int per = int((i + 1) * 100 / servers.size()); + QMetaObject::invokeMethod(this, "Progress", Q_ARG(int, per)); + QMetaObject::invokeMethod(this, "UpdateMessage", + Q_ARG(QString, QTStr(TEST_BW_CONNECTING) + .arg(server.name.c_str()))); + + obs_data_set_string(service_settings, "server", + server.address.c_str()); + obs_service_update(service, service_settings); + + if (!obs_output_start(output)) + continue; + + unique_lock ul(m); + if (cancel) { + ul.unlock(); + obs_output_force_stop(output); + return; + } + if (!stopped && !connected) + cv.wait(ul); + if (cancel) { + ul.unlock(); + obs_output_force_stop(output); + return; + } + if (!connected) + continue; + + QMetaObject::invokeMethod(this, "UpdateMessage", + Q_ARG(QString, QTStr(TEST_BW_SERVER) + .arg(server.name.c_str()))); + + /* ignore first 2.5 seconds due to possible buffering skewing + * the result */ + cv.wait_for(ul, chrono::milliseconds(2500)); + if (stopped) + continue; + if (cancel) { + ul.unlock(); + obs_output_force_stop(output); + return; + } + + /* continue test */ + int start_bytes = (int)obs_output_get_total_bytes(output); + uint64_t t_start = os_gettime_ns(); + + cv.wait_for(ul, chrono::seconds(10)); + if (stopped) + continue; + if (cancel) { + ul.unlock(); + obs_output_force_stop(output); + return; + } + + obs_output_stop(output); + cv.wait(ul); + + uint64_t total_time = os_gettime_ns() - t_start; + + int total_bytes = (int)obs_output_get_total_bytes(output) - + start_bytes; + uint64_t bitrate = (uint64_t)total_bytes * 8 + * 1000000000 / total_time / 1000; + + if (obs_output_get_frames_dropped(output) || + (int)bitrate < (wiz->startingBitrate * 75 / 100)) { + server.bitrate = (int)bitrate * 70 / 100; + } else { + server.bitrate = wiz->startingBitrate; + } + + server.ms = obs_output_get_connect_time_ms(output); + success = true; + } + + if (!success) { + QMetaObject::invokeMethod(this, "Failure", + Q_ARG(QString, QTStr(TEST_BW_CONNECT_FAIL))); + return; + } + + int bestBitrate = 0; + int bestMS = 0x7FFFFFFF; + string bestServer; + string bestServerName; + + for (auto &server : servers) { + bool close = abs(server.bitrate - bestBitrate) < 400; + + if ((!close && server.bitrate > bestBitrate) || + (close && server.ms < bestMS)) { + bestServer = server.address; + bestServerName = server.name; + bestBitrate = server.bitrate; + bestMS = server.ms; + } + } + + wiz->server = bestServer; + wiz->serverName = bestServerName; + wiz->idealBitrate = bestBitrate; + + QMetaObject::invokeMethod(this, "NextStage"); +} + +/* this is used to estimate the lower bitrate limit for a given + * resolution/fps. yes, it is a totally arbitrary equation that gets + * the closest to the expected values */ +static long double EstimateBitrateVal(int cx, int cy, int fps_num, int fps_den) +{ + long fps = (long double)fps_num / (long double)fps_den; + long double areaVal = pow((long double)(cx * cy), 0.85l); + return areaVal * sqrt(pow(fps, 1.1l)); +} + +static long double EstimateMinBitrate(int cx, int cy, int fps_num, int fps_den) +{ + long double val = EstimateBitrateVal(1920, 1080, 60, 1) / 5800.0l; + return EstimateBitrateVal(cx, cy, fps_num, fps_den) / val; +} + +static long double EstimateUpperBitrate(int cx, int cy, int fps_num, int fps_den) +{ + long double val = EstimateBitrateVal(1280, 720, 30, 1) / 3000.0l; + return EstimateBitrateVal(cx, cy, fps_num, fps_den) / val; +} + +struct Result { + int cx; + int cy; + int fps_num; + int fps_den; + + inline Result(int cx_, int cy_, int fps_num_, int fps_den_) + : cx(cx_), cy(cy_), fps_num(fps_num_), fps_den(fps_den_) + { + } +}; + +static void CalcBaseRes(int &baseCX, int &baseCY) +{ + const int maxBaseArea = 1920 * 1200; + const int clipResArea = 1920 * 1080; + + /* if base resolution unusually high, recalculate to a more reasonable + * value to start the downscaling at, based upon 1920x1080's area. + * + * for 16:9 resolutions this will always change the starting value to + * 1920x1080 */ + if ((baseCX * baseCY) > maxBaseArea) { + long double xyAspect = + (long double)baseCX / (long double)baseCY; + baseCY = (int)sqrt((long double)clipResArea / xyAspect); + baseCX = (int)((long double)baseCY * xyAspect); + } +} + +bool AutoConfigTestPage::TestSoftwareEncoding() +{ + TestMode testMode; + QMetaObject::invokeMethod(this, "UpdateMessage", + Q_ARG(QString, QStringLiteral(""))); + + /* -----------------------------------*/ + /* create obs objects */ + + OBSEncoder vencoder = obs_video_encoder_create("obs_x264", + "test_x264", nullptr, nullptr); + OBSEncoder aencoder = obs_audio_encoder_create("ffmpeg_aac", + "test_aac", nullptr, 0, nullptr); + OBSOutput output = obs_output_create("null_output", + "null", nullptr, nullptr); + obs_output_release(output); + obs_encoder_release(vencoder); + obs_encoder_release(aencoder); + + /* -----------------------------------*/ + /* configure settings */ + + OBSData aencoder_settings = obs_data_create(); + OBSData vencoder_settings = obs_data_create(); + obs_data_release(aencoder_settings); + obs_data_release(vencoder_settings); + obs_data_set_int(aencoder_settings, "bitrate", 32); + + if (wiz->type != AutoConfig::Type::Recording) { + obs_data_set_int(vencoder_settings, "keyint_sec", 2); + obs_data_set_int(vencoder_settings, "bitrate", + wiz->idealBitrate); + obs_data_set_string(vencoder_settings, "rate_control", "CBR"); + obs_data_set_string(vencoder_settings, "profile", "main"); + obs_data_set_string(vencoder_settings, "preset", "veryfast"); + } else { + obs_data_set_int(vencoder_settings, "crf", 20); + obs_data_set_string(vencoder_settings, "rate_control", "CRF"); + obs_data_set_string(vencoder_settings, "profile", "high"); + obs_data_set_string(vencoder_settings, "preset", "veryfast"); + } + + /* -----------------------------------*/ + /* apply settings */ + + obs_encoder_update(vencoder, vencoder_settings); + obs_encoder_update(aencoder, aencoder_settings); + + /* -----------------------------------*/ + /* connect encoders/services/outputs */ + + obs_output_set_video_encoder(output, vencoder); + obs_output_set_audio_encoder(output, aencoder, 0); + + /* -----------------------------------*/ + /* connect signals */ + + auto on_stopped = [&] () + { + unique_lock lock(m); + cv.notify_one(); + }; + + using on_stopped_t = decltype(on_stopped); + + auto pre_on_stopped = [] (void *data, calldata_t *) + { + on_stopped_t &on_stopped = + *reinterpret_cast(data); + on_stopped(); + }; + + signal_handler *sh = obs_output_get_signal_handler(output); + signal_handler_connect(sh, "deactivate", pre_on_stopped, &on_stopped); + + /* -----------------------------------*/ + /* calculate starting resolution */ + + int baseCX = wiz->baseResolutionCX; + int baseCY = wiz->baseResolutionCY; + CalcBaseRes(baseCX, baseCY); + + /* -----------------------------------*/ + /* calculate starting test rates */ + + int pcores = os_get_physical_cores(); + int lcores = os_get_logical_cores(); + int maxDataRate; + if (lcores > 8 || pcores > 4) { + /* superb */ + maxDataRate = 1920 * 1200 * 60 + 1000; + + } else if (lcores > 4 && pcores == 4) { + /* great */ + maxDataRate = 1920 * 1080 * 60 + 1000; + + } else if (pcores == 4) { + /* okay */ + maxDataRate = 1920 * 1080 * 30 + 1000; + + } else { + /* toaster */ + maxDataRate = 960 * 540 * 30 + 1000; + } + + /* -----------------------------------*/ + /* perform tests */ + + vector results; + int i = 0; + int count = 1; + + auto testRes = [&] (long double div, int fps_num, int fps_den, + bool force) + { + int per = ++i * 100 / count; + QMetaObject::invokeMethod(this, "Progress", Q_ARG(int, per)); + + /* no need for more than 3 tests max */ + if (results.size() >= 3) + return true; + + if (!fps_num || !fps_den) { + fps_num = wiz->specificFPSNum; + fps_den = wiz->specificFPSDen; + } + + long double fps = ((long double)fps_num / (long double)fps_den); + + int cx = int((long double)baseCX / div); + int cy = int((long double)baseCY / div); + + if (!force && wiz->type != AutoConfig::Type::Recording) { + int est = EstimateMinBitrate(cx, cy, fps_num, fps_den); + if (est > wiz->idealBitrate) + return true; + } + + long double rate = (long double)cx * (long double)cy * fps; + if (!force && rate > maxDataRate) + return true; + + testMode.SetVideo(cx, cy, fps_num, fps_den); + + obs_encoder_set_video(vencoder, obs_get_video()); + obs_encoder_set_audio(aencoder, obs_get_audio()); + obs_encoder_update(vencoder, vencoder_settings); + + obs_output_set_media(output, obs_get_video(), obs_get_audio()); + + QString cxStr = QString::number(cx); + QString cyStr = QString::number(cy); + + QString fpsStr = (fps_den > 1) + ? QString::number(fps, 'f', 2) + : QString::number(fps, 'g', 2); + + QMetaObject::invokeMethod(this, "UpdateMessage", + Q_ARG(QString, QTStr(TEST_RES_VAL) + .arg(cxStr, cyStr, fpsStr))); + + unique_lock ul(m); + if (cancel) + return false; + + if (!obs_output_start(output)) { + QMetaObject::invokeMethod(this, "Failure", + Q_ARG(QString, QTStr(TEST_RES_FAIL))); + return false; + } + + cv.wait_for(ul, chrono::seconds(5)); + + obs_output_stop(output); + cv.wait(ul); + + int skipped = (int)video_output_get_skipped_frames( + obs_get_video()); + if (force || skipped <= 10) + results.emplace_back(cx, cy, fps_num, fps_den); + + return !cancel; + }; + + if (wiz->specificFPSNum && wiz->specificFPSDen) { + count = 5; + if (!testRes(1.0, 0, 0, false)) return false; + if (!testRes(1.5, 0, 0, false)) return false; + if (!testRes(1.0 / 0.6, 0, 0, false)) return false; + if (!testRes(2.0, 0, 0, false)) return false; + if (!testRes(2.25, 0, 0, true)) return false; + } else { + count = 10; + if (!testRes(1.0, 60, 1, false)) return false; + if (!testRes(1.0, 30, 1, false)) return false; + if (!testRes(1.5, 60, 1, false)) return false; + if (!testRes(1.5, 30, 1, false)) return false; + if (!testRes(1.0 / 0.6, 60, 1, false)) return false; + if (!testRes(1.0 / 0.6, 30, 1, false)) return false; + if (!testRes(2.0, 60, 1, false)) return false; + if (!testRes(2.0, 30, 1, false)) return false; + if (!testRes(2.25, 60, 1, false)) return false; + if (!testRes(2.25, 30, 1, true)) return false; + } + + /* -----------------------------------*/ + /* find preferred settings */ + + int minArea = 960 * 540 + 1000; + + if (!wiz->specificFPSNum && wiz->preferHighFPS && results.size() > 1) { + Result &result1 = results[0]; + Result &result2 = results[1]; + + if (result1.fps_num == 30 && result2.fps_num == 60) { + int nextArea = result2.cx * result2.cy; + if (nextArea >= minArea) + results.erase(results.begin()); + } + } + + Result result = results.front(); + wiz->idealResolutionCX = result.cx; + wiz->idealResolutionCY = result.cy; + wiz->idealFPSNum = result.fps_num; + wiz->idealFPSDen = result.fps_den; + + long double fUpperBitrate = EstimateUpperBitrate( + result.cx, result.cy, result.fps_num, result.fps_den); + + int upperBitrate = int(floor(fUpperBitrate / 50.0l) * 50.0l); + + if (wiz->streamingEncoder != AutoConfig::Encoder::x264) { + upperBitrate *= 114; + upperBitrate /= 100; + } + + if (wiz->idealBitrate > upperBitrate) + wiz->idealBitrate = upperBitrate; + + softwareTested = true; + return true; +} + +void AutoConfigTestPage::FindIdealHardwareResolution() +{ + int baseCX = wiz->baseResolutionCX; + int baseCY = wiz->baseResolutionCY; + CalcBaseRes(baseCX, baseCY); + + vector results; + + int pcores = os_get_physical_cores(); + int maxDataRate; + if (pcores >= 4) { + maxDataRate = 1920 * 1200 * 60 + 1000; + } else { + maxDataRate = 1280 * 720 * 30 + 1000; + } + + auto testRes = [&] (long double div, int fps_num, int fps_den, + bool force) + { + if (results.size() >= 3) + return; + + if (!fps_num || !fps_den) { + fps_num = wiz->specificFPSNum; + fps_den = wiz->specificFPSDen; + } + + long double fps = ((long double)fps_num / (long double)fps_den); + + int cx = int((long double)baseCX / div); + int cy = int((long double)baseCY / div); + + long double rate = (long double)cx * (long double)cy * fps; + if (!force && rate > maxDataRate) + return; + + int minBitrate = EstimateMinBitrate(cx, cy, fps_num, fps_den) + * 114 / 100; + if (wiz->type == AutoConfig::Type::Recording) + force = true; + if (force || wiz->idealBitrate >= minBitrate) + results.emplace_back(cx, cy, fps_num, fps_den); + }; + + if (wiz->specificFPSNum && wiz->specificFPSDen) { + testRes(1.0, 0, 0, false); + testRes(1.5, 0, 0, false); + testRes(1.0 / 0.6, 0, 0, false); + testRes(2.0, 0, 0, false); + testRes(2.25, 0, 0, true); + } else { + testRes(1.0, 60, 1, false); + testRes(1.0, 30, 1, false); + testRes(1.5, 60, 1, false); + testRes(1.5, 30, 1, false); + testRes(1.0 / 0.6, 60, 1, false); + testRes(1.0 / 0.6, 30, 1, false); + testRes(2.0, 60, 1, false); + testRes(2.0, 30, 1, false); + testRes(2.25, 60, 1, false); + testRes(2.25, 30, 1, true); + } + + int minArea = 960 * 540 + 1000; + + if (!wiz->specificFPSNum && wiz->preferHighFPS && results.size() > 1) { + Result &result1 = results[0]; + Result &result2 = results[1]; + + if (result1.fps_num == 30 && result2.fps_num == 60) { + int nextArea = result2.cx * result2.cy; + if (nextArea >= minArea) + results.erase(results.begin()); + } + } + + Result result = results.front(); + wiz->idealResolutionCX = result.cx; + wiz->idealResolutionCY = result.cy; + wiz->idealFPSNum = result.fps_num; + wiz->idealFPSDen = result.fps_den; +} + +void AutoConfigTestPage::TestStreamEncoderThread() +{ + bool preferHardware = wiz->preferHardware; + if (!softwareTested) { + if (!preferHardware || !wiz->hardwareEncodingAvailable) { + if (!TestSoftwareEncoding()) { + return; + } + } + } + + if (preferHardware && !softwareTested && wiz->hardwareEncodingAvailable) + FindIdealHardwareResolution(); + + if (!softwareTested) { + if (wiz->nvencAvailable) + wiz->streamingEncoder = AutoConfig::Encoder::NVENC; + else if (wiz->qsvAvailable) + wiz->streamingEncoder = AutoConfig::Encoder::QSV; + else + wiz->streamingEncoder = AutoConfig::Encoder::AMD; + } else { + wiz->streamingEncoder = AutoConfig::Encoder::x264; + } + + QMetaObject::invokeMethod(this, "NextStage"); +} + +void AutoConfigTestPage::TestRecordingEncoderThread() +{ + if (!wiz->hardwareEncodingAvailable && !softwareTested) { + if (!TestSoftwareEncoding()) { + return; + } + } + + if (wiz->type == AutoConfig::Type::Recording && + wiz->hardwareEncodingAvailable) + FindIdealHardwareResolution(); + + wiz->recordingQuality = AutoConfig::Quality::High; + + bool recordingOnly = wiz->type == AutoConfig::Type::Recording; + + if (wiz->hardwareEncodingAvailable) { + if (wiz->nvencAvailable) + wiz->recordingEncoder = AutoConfig::Encoder::NVENC; + else if (wiz->qsvAvailable) + wiz->recordingEncoder = AutoConfig::Encoder::QSV; + else + wiz->recordingEncoder = AutoConfig::Encoder::AMD; + } else { + wiz->recordingEncoder = AutoConfig::Encoder::x264; + } + + if (wiz->recordingEncoder != AutoConfig::Encoder::NVENC) { + if (!recordingOnly) { + wiz->recordingEncoder = AutoConfig::Encoder::Stream; + wiz->recordingQuality = AutoConfig::Quality::Stream; + } + } + + QMetaObject::invokeMethod(this, "NextStage"); +} + +#define ENCODER_TEXT(x) "Basic.Settings.Output.Simple.Encoder." x +#define ENCODER_SOFTWARE ENCODER_TEXT("Software") +#define ENCODER_NVENC ENCODER_TEXT("Hardware.NVENC") +#define ENCODER_QSV ENCODER_TEXT("Hardware.QSV") +#define ENCODER_AMD ENCODER_TEXT("Hardware.AMD") + +#define QUALITY_SAME "Basic.Settings.Output.Simple.RecordingQuality.Stream" +#define QUALITY_HIGH "Basic.Settings.Output.Simple.RecordingQuality.Small" + +void AutoConfigTestPage::FinalizeResults() +{ + ui->stackedWidget->setCurrentIndex(1); + setSubTitle(QTStr(SUBTITLE_COMPLETE)); + + QFormLayout *form = results; + + auto encName = [] (AutoConfig::Encoder enc) -> QString + { + switch (enc) { + case AutoConfig::Encoder::x264: + return QTStr(ENCODER_SOFTWARE); + case AutoConfig::Encoder::NVENC: + return QTStr(ENCODER_NVENC); + case AutoConfig::Encoder::QSV: + return QTStr(ENCODER_QSV); + case AutoConfig::Encoder::AMD: + return QTStr(ENCODER_AMD); + case AutoConfig::Encoder::Stream: + return QTStr(QUALITY_SAME); + } + + return QTStr(ENCODER_SOFTWARE); + }; + + auto newLabel = [this] (const char *str) -> QLabel * + { + return new QLabel(QTStr(str), this); + }; + + if (wiz->type != AutoConfig::Type::Recording) { + const char *serverType = wiz->customServer + ? "rtmp_custom" + : "rtmp_common"; + + OBSService service = obs_service_create(serverType, + "temp_service", nullptr, nullptr); + obs_service_release(service); + + OBSData service_settings = obs_data_create(); + OBSData vencoder_settings = obs_data_create(); + obs_data_release(service_settings); + obs_data_release(vencoder_settings); + + obs_data_set_int(vencoder_settings, "bitrate", + wiz->idealBitrate); + + obs_data_set_string(service_settings, "service", + wiz->serviceName.c_str()); + obs_service_update(service, service_settings); + obs_service_apply_encoder_settings(service, + vencoder_settings, nullptr); + + wiz->idealBitrate = (int)obs_data_get_int(vencoder_settings, + "bitrate"); + + if (!wiz->customServer) + form->addRow( + newLabel("Basic.AutoConfig.StreamPage.Service"), + new QLabel(wiz->serviceName.c_str(), + ui->finishPage)); + form->addRow(newLabel("Basic.AutoConfig.StreamPage.Server"), + new QLabel(wiz->serverName.c_str(), ui->finishPage)); + form->addRow(newLabel("Basic.Settings.Output.VideoBitrate"), + new QLabel(QString::number(wiz->idealBitrate), + ui->finishPage)); + form->addRow(newLabel(TEST_RESULT_SE), + new QLabel(encName(wiz->streamingEncoder), + ui->finishPage)); + } + + QString baseRes = QString("%1x%2").arg( + QString::number(wiz->baseResolutionCX), + QString::number(wiz->baseResolutionCY)); + QString scaleRes = QString("%1x%2").arg( + QString::number(wiz->idealResolutionCX), + QString::number(wiz->idealResolutionCY)); + + if (wiz->recordingEncoder != AutoConfig::Encoder::Stream || + wiz->recordingQuality != AutoConfig::Quality::Stream) + form->addRow(newLabel(TEST_RESULT_RE), + new QLabel(encName(wiz->recordingEncoder), + ui->finishPage)); + + QString recQuality; + + switch (wiz->recordingQuality) { + case AutoConfig::Quality::High: + recQuality = QTStr(QUALITY_HIGH); + break; + case AutoConfig::Quality::Stream: + recQuality = QTStr(QUALITY_SAME); + break; + } + + form->addRow(newLabel("Basic.Settings.Output.Simple.RecordingQuality"), + new QLabel(recQuality, ui->finishPage)); + + long double fps = + (long double)wiz->idealFPSNum / (long double)wiz->idealFPSDen; + + QString fpsStr = (wiz->idealFPSDen > 1) + ? QString::number(fps, 'f', 2) + : QString::number(fps, 'g', 2); + + form->addRow(newLabel("Basic.Settings.Video.BaseResolution"), + new QLabel(baseRes, ui->finishPage)); + form->addRow(newLabel("Basic.Settings.Video.ScaledResolution"), + new QLabel(scaleRes, ui->finishPage)); + form->addRow(newLabel("Basic.Settings.Video.FPS"), + new QLabel(fpsStr, ui->finishPage)); +} + +#define STARTING_SEPARATOR \ + "\n==== Auto-config wizard testing commencing ======\n" +#define STOPPING_SEPARATOR \ + "\n==== Auto-config wizard testing stopping ========\n" + +void AutoConfigTestPage::NextStage() +{ + if (testThread.joinable()) + testThread.join(); + if (cancel) + return; + + ui->subProgressLabel->setText(QString()); + + /* make it skip to bandwidth stage if only set to config recording */ + if (stage == Stage::Starting) { + if (!started) { + blog(LOG_INFO, STARTING_SEPARATOR); + started = true; + } + + if (wiz->type == AutoConfig::Type::Recording) { + stage = Stage::StreamEncoder; + } else if (!wiz->bandwidthTest) { + stage = Stage::BandwidthTest; + } + } + + if (stage == Stage::Starting) { + stage = Stage::BandwidthTest; + StartBandwidthStage(); + + } else if (stage == Stage::BandwidthTest) { + stage = Stage::StreamEncoder; + StartStreamEncoderStage(); + + } else if (stage == Stage::StreamEncoder) { + stage = Stage::RecordingEncoder; + StartRecordingEncoderStage(); + + } else { + stage = Stage::Finished; + FinalizeResults(); + emit completeChanged(); + } +} + +void AutoConfigTestPage::UpdateMessage(QString message) +{ + ui->subProgressLabel->setText(message); +} + +void AutoConfigTestPage::Failure(QString message) +{ + ui->errorLabel->setText(message); + ui->stackedWidget->setCurrentIndex(2); +} + +void AutoConfigTestPage::Progress(int percentage) +{ + ui->progressBar->setValue(percentage); +} + +AutoConfigTestPage::AutoConfigTestPage(QWidget *parent) + : QWizardPage (parent), + ui (new Ui_AutoConfigTestPage) +{ + ui->setupUi(this); + setTitle(QTStr("Basic.AutoConfig.TestPage")); + setSubTitle(QTStr(SUBTITLE_TESTING)); + setCommitPage(true); +} + +AutoConfigTestPage::~AutoConfigTestPage() +{ + delete ui; + + if (testThread.joinable()) { + { + unique_lock ul(m); + cancel = true; + cv.notify_one(); + } + testThread.join(); + } + + if (started) + blog(LOG_INFO, STOPPING_SEPARATOR); +} + +void AutoConfigTestPage::initializePage() +{ + setSubTitle(QTStr(SUBTITLE_TESTING)); + stage = Stage::Starting; + softwareTested = false; + cancel = false; + DeleteLayout(results); + results = new QFormLayout(); + results->setContentsMargins(0, 0, 0, 0); + ui->finishPageLayout->insertLayout(1, results); + ui->stackedWidget->setCurrentIndex(0); + NextStage(); +} + +void AutoConfigTestPage::cleanupPage() +{ + if (testThread.joinable()) { + { + unique_lock ul(m); + cancel = true; + cv.notify_one(); + } + testThread.join(); + } +} + +bool AutoConfigTestPage::isComplete() const +{ + return stage == Stage::Finished; +} + +int AutoConfigTestPage::nextId() const +{ + return -1; +} diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp new file mode 100644 index 0000000..8411350 --- /dev/null +++ b/UI/window-basic-auto-config.cpp @@ -0,0 +1,806 @@ +#include "window-basic-auto-config.hpp" +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" +#include "obs-app.hpp" + +#include +#include + +#include + +#include "ui_AutoConfigStartPage.h" +#include "ui_AutoConfigVideoPage.h" +#include "ui_AutoConfigStreamPage.h" + +#define wiz reinterpret_cast(wizard()) + +/* ------------------------------------------------------------------------- */ + +#define SERVICE_PATH "service.json" + +static OBSData OpenServiceSettings(std::string &type) +{ + char serviceJsonPath[512]; + int ret = GetProfilePath(serviceJsonPath, sizeof(serviceJsonPath), + SERVICE_PATH); + if (ret <= 0) + return OBSData(); + + OBSData data = obs_data_create_from_json_file_safe(serviceJsonPath, + "bak"); + obs_data_release(data); + + obs_data_set_default_string(data, "type", "rtmp_common"); + type = obs_data_get_string(data, "type"); + + OBSData settings = obs_data_get_obj(data, "settings"); + obs_data_release(settings); + + return settings; +} + +static void GetServiceInfo(std::string &type, std::string &service, + std::string &server, std::string &key) +{ + OBSData settings = OpenServiceSettings(type); + + service = obs_data_get_string(settings, "service"); + server = obs_data_get_string(settings, "server"); + key = obs_data_get_string(settings, "key"); +} + +/* ------------------------------------------------------------------------- */ + +AutoConfigStartPage::AutoConfigStartPage(QWidget *parent) + : QWizardPage (parent), + ui (new Ui_AutoConfigStartPage) +{ + ui->setupUi(this); + setTitle(QTStr("Basic.AutoConfig.StartPage")); + setSubTitle(QTStr("Basic.AutoConfig.StartPage.SubTitle")); +} + +AutoConfigStartPage::~AutoConfigStartPage() +{ + delete ui; +} + +int AutoConfigStartPage::nextId() const +{ + return AutoConfig::VideoPage; +} + +void AutoConfigStartPage::on_prioritizeStreaming_clicked() +{ + wiz->type = AutoConfig::Type::Streaming; +} + +void AutoConfigStartPage::on_prioritizeRecording_clicked() +{ + wiz->type = AutoConfig::Type::Recording; +} + +/* ------------------------------------------------------------------------- */ + +#define RES_TEXT(x) "Basic.AutoConfig.VideoPage." x +#define RES_USE_CURRENT RES_TEXT("BaseResolution.UseCurrent") +#define RES_USE_DISPLAY RES_TEXT("BaseResolution.Display") +#define FPS_USE_CURRENT RES_TEXT("FPS.UseCurrent") +#define FPS_PREFER_HIGH_FPS RES_TEXT("FPS.PreferHighFPS") +#define FPS_PREFER_HIGH_RES RES_TEXT("FPS.PreferHighRes") + +AutoConfigVideoPage::AutoConfigVideoPage(QWidget *parent) + : QWizardPage (parent), + ui (new Ui_AutoConfigVideoPage) +{ + ui->setupUi(this); + + setTitle(QTStr("Basic.AutoConfig.VideoPage")); + setSubTitle(QTStr("Basic.AutoConfig.VideoPage.SubTitle")); + + obs_video_info ovi; + obs_get_video_info(&ovi); + + long double fpsVal = + (long double)ovi.fps_num / (long double)ovi.fps_den; + + QString fpsStr = (ovi.fps_den > 1) + ? QString::number(fpsVal, 'f', 2) + : QString::number(fpsVal, 'g', 2); + + ui->fps->addItem(QTStr(FPS_PREFER_HIGH_FPS), + (int)AutoConfig::FPSType::PreferHighFPS); + ui->fps->addItem(QTStr(FPS_PREFER_HIGH_RES), + (int)AutoConfig::FPSType::PreferHighRes); + ui->fps->addItem(QTStr(FPS_USE_CURRENT).arg(fpsStr), + (int)AutoConfig::FPSType::UseCurrent); + ui->fps->addItem(QStringLiteral("30"), (int)AutoConfig::FPSType::fps30); + ui->fps->addItem(QStringLiteral("60"), (int)AutoConfig::FPSType::fps60); + ui->fps->setCurrentIndex(0); + + QString cxStr = QString::number(ovi.base_width); + QString cyStr = QString::number(ovi.base_height); + + int encRes = int(ovi.base_width << 16) | int(ovi.base_height); + ui->canvasRes->addItem(QTStr(RES_USE_CURRENT).arg(cxStr, cyStr), + (int)encRes); + + QList screens = QGuiApplication::screens(); + for (int i = 0; i < screens.size(); i++) { + QScreen *screen = screens[i]; + QSize as = screen->size(); + + encRes = int(as.width() << 16) | int(as.height()); + + QString str = QTStr(RES_USE_DISPLAY) + .arg(QString::number(i + 1), + QString::number(as.width()), + QString::number(as.height())); + + ui->canvasRes->addItem(str, encRes); + } + + auto addRes = [&] (int cx, int cy) + { + encRes = (cx << 16) | cy; + QString str = QString("%1x%2").arg( + QString::number(cx), + QString::number(cy)); + ui->canvasRes->addItem(str, encRes); + }; + + addRes(1920, 1080); + addRes(1280, 720); + + ui->canvasRes->setCurrentIndex(0); +} + +AutoConfigVideoPage::~AutoConfigVideoPage() +{ + delete ui; +} + +int AutoConfigVideoPage::nextId() const +{ + return wiz->type == AutoConfig::Type::Recording + ? AutoConfig::TestPage + : AutoConfig::StreamPage; +} + +bool AutoConfigVideoPage::validatePage() +{ + int encRes = ui->canvasRes->currentData().toInt(); + wiz->baseResolutionCX = encRes >> 16; + wiz->baseResolutionCY = encRes & 0xFFFF; + wiz->fpsType = (AutoConfig::FPSType)ui->fps->currentData().toInt(); + + obs_video_info ovi; + obs_get_video_info(&ovi); + + switch (wiz->fpsType) { + case AutoConfig::FPSType::PreferHighFPS: + wiz->specificFPSNum = 0; + wiz->specificFPSDen = 0; + wiz->preferHighFPS = true; + break; + case AutoConfig::FPSType::PreferHighRes: + wiz->specificFPSNum = 0; + wiz->specificFPSDen = 0; + wiz->preferHighFPS = false; + break; + case AutoConfig::FPSType::UseCurrent: + wiz->specificFPSNum = ovi.fps_num; + wiz->specificFPSDen = ovi.fps_den; + wiz->preferHighFPS = false; + break; + case AutoConfig::FPSType::fps30: + wiz->specificFPSNum = 30; + wiz->specificFPSDen = 1; + wiz->preferHighFPS = false; + break; + case AutoConfig::FPSType::fps60: + wiz->specificFPSNum = 60; + wiz->specificFPSDen = 1; + wiz->preferHighFPS = false; + break; + } + + return true; +} + +/* ------------------------------------------------------------------------- */ + +AutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) + : QWizardPage (parent), + ui (new Ui_AutoConfigStreamPage) +{ + ui->setupUi(this); + 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")); + + setTitle(QTStr("Basic.AutoConfig.StreamPage")); + setSubTitle(QTStr("Basic.AutoConfig.StreamPage.SubTitle")); + + LoadServices(false); + + connect(ui->streamType, SIGNAL(currentIndexChanged(int)), + this, SLOT(ServiceChanged())); + connect(ui->service, SIGNAL(currentIndexChanged(int)), + this, SLOT(ServiceChanged())); + connect(ui->customServer, SIGNAL(textChanged(const QString &)), + this, SLOT(ServiceChanged())); + connect(ui->doBandwidthTest, SIGNAL(toggled(bool)), + this, SLOT(ServiceChanged())); + + connect(ui->service, SIGNAL(currentIndexChanged(int)), + this, SLOT(UpdateServerList())); + + connect(ui->streamType, SIGNAL(currentIndexChanged(int)), + this, SLOT(UpdateKeyLink())); + connect(ui->service, SIGNAL(currentIndexChanged(int)), + this, SLOT(UpdateKeyLink())); + + connect(ui->key, SIGNAL(textChanged(const QString &)), + this, SLOT(UpdateCompleted())); + connect(ui->regionUS, SIGNAL(toggled(bool)), + this, SLOT(UpdateCompleted())); + connect(ui->regionEU, SIGNAL(toggled(bool)), + this, SLOT(UpdateCompleted())); + connect(ui->regionAsia, SIGNAL(toggled(bool)), + this, SLOT(UpdateCompleted())); + connect(ui->regionOther, SIGNAL(toggled(bool)), + this, SLOT(UpdateCompleted())); +} + +AutoConfigStreamPage::~AutoConfigStreamPage() +{ + delete ui; +} + +bool AutoConfigStreamPage::isComplete() const +{ + return ready; +} + +int AutoConfigStreamPage::nextId() const +{ + return AutoConfig::TestPage; +} + +bool AutoConfigStreamPage::validatePage() +{ + OBSData service_settings = obs_data_create(); + obs_data_release(service_settings); + + wiz->customServer = ui->streamType->currentIndex() == 1; + + const char *serverType = wiz->customServer + ? "rtmp_custom" + : "rtmp_common"; + + if (!wiz->customServer) { + obs_data_set_string(service_settings, "service", + QT_TO_UTF8(ui->service->currentText())); + } + + OBSService service = obs_service_create(serverType, "temp_service", + service_settings, nullptr); + obs_service_release(service); + + int bitrate = 10000; + if (!ui->doBandwidthTest->isChecked()) { + bitrate = ui->bitrate->value(); + wiz->idealBitrate = bitrate; + } + + OBSData settings = obs_data_create(); + obs_data_release(settings); + obs_data_set_int(settings, "bitrate", bitrate); + obs_service_apply_encoder_settings(service, settings, nullptr); + + if (wiz->customServer) { + QString server = ui->customServer->text(); + wiz->server = wiz->serverName = QT_TO_UTF8(server); + } else { + wiz->serverName = QT_TO_UTF8(ui->server->currentText()); + wiz->server = QT_TO_UTF8(ui->server->currentData().toString()); + } + + wiz->bandwidthTest = ui->doBandwidthTest->isChecked(); + wiz->startingBitrate = (int)obs_data_get_int(settings, "bitrate"); + wiz->idealBitrate = wiz->startingBitrate; + wiz->regionUS = ui->regionUS->isChecked(); + wiz->regionEU = ui->regionEU->isChecked(); + wiz->regionAsia = ui->regionAsia->isChecked(); + wiz->regionOther = ui->regionOther->isChecked(); + wiz->serviceName = QT_TO_UTF8(ui->service->currentText()); + if (ui->preferHardware) + wiz->preferHardware = ui->preferHardware->isChecked(); + wiz->key = QT_TO_UTF8(ui->key->text()); + + if (!wiz->customServer) { + if (wiz->serviceName == "Twitch") + wiz->service = AutoConfig::Service::Twitch; + else if (wiz->serviceName == "hitbox.tv") + wiz->service = AutoConfig::Service::Hitbox; + else if (wiz->serviceName == "beam.pro") + wiz->service = AutoConfig::Service::Beam; + else + wiz->service = AutoConfig::Service::Other; + } else { + wiz->service = AutoConfig::Service::Other; + } + + if (wiz->service != AutoConfig::Service::Twitch && wiz->bandwidthTest) { + QMessageBox::StandardButton button; +#define WARNING_TEXT(x) QTStr("Basic.AutoConfig.StreamPage.StreamWarning." x) + button = OBSMessageBox::question(this, + WARNING_TEXT("Title"), + WARNING_TEXT("Text")); +#undef WARNING_TEXT + + if (button == QMessageBox::No) + return false; + } + + return true; +} + +void AutoConfigStreamPage::on_show_clicked() +{ + if (ui->key->echoMode() == QLineEdit::Password) { + ui->key->setEchoMode(QLineEdit::Normal); + ui->show->setText(QTStr("Hide")); + } else { + ui->key->setEchoMode(QLineEdit::Password); + ui->show->setText(QTStr("Show")); + } +} + +void AutoConfigStreamPage::ServiceChanged() +{ + bool showMore = ui->service->currentData().toBool(); + if (showMore) + return; + + std::string service = QT_TO_UTF8(ui->service->currentText()); + bool regionBased = service == "Twitch" || + service == "hitbox.tv" || + service == "beam.pro"; + bool testBandwidth = ui->doBandwidthTest->isChecked(); + bool custom = ui->streamType->currentIndex() == 1; + + ui->service->setVisible(!custom); + ui->serviceLabel->setVisible(!custom); + + ui->formLayout->removeWidget(ui->serviceLabel); + ui->formLayout->removeWidget(ui->service); + + ui->formLayout->removeWidget(ui->serverLabel); + ui->formLayout->removeWidget(ui->serverStackedWidget); + + if (custom) { + ui->formLayout->insertRow(1, ui->serverLabel, + ui->serverStackedWidget); + + ui->region->setVisible(false); + ui->serverStackedWidget->setCurrentIndex(1); + ui->serverStackedWidget->setVisible(true); + ui->serverLabel->setVisible(true); + } else { + ui->formLayout->insertRow(1, ui->serviceLabel, ui->service); + + if (!testBandwidth) + ui->formLayout->insertRow(2, ui->serverLabel, + ui->serverStackedWidget); + + ui->region->setVisible(regionBased && testBandwidth); + ui->serverStackedWidget->setCurrentIndex(0); + ui->serverStackedWidget->setHidden(testBandwidth); + ui->serverLabel->setHidden(testBandwidth); + } + + wiz->testRegions = regionBased && testBandwidth; + + ui->bitrateLabel->setHidden(testBandwidth); + ui->bitrate->setHidden(testBandwidth); + + UpdateCompleted(); +} + +void AutoConfigStreamPage::UpdateKeyLink() +{ + bool custom = ui->streamType->currentIndex() == 1; + QString serviceName = ui->service->currentText(); + + if (custom) + serviceName = ""; + + QString text = QTStr("Basic.AutoConfig.StreamPage.StreamKey"); + if (serviceName == "Twitch") { + text += " "; + text += QTStr("Basic.AutoConfig.StreamPage.StreamKey.LinkToSite"); + text += ""; + } else if (serviceName == "YouTube / YouTube Gaming") { + text += " "; + text += QTStr("Basic.AutoConfig.StreamPage.StreamKey.LinkToSite"); + text += ""; + } + + ui->streamKeyLabel->setText(text); +} + +void AutoConfigStreamPage::LoadServices(bool showAll) +{ + obs_properties_t *props = obs_get_service_properties("rtmp_common"); + + OBSData settings = obs_data_create(); + obs_data_release(settings); + + obs_data_set_bool(settings, "show_all", showAll); + + obs_property_t *prop = obs_properties_get(props, "show_all"); + obs_property_modified(prop, settings); + + ui->service->blockSignals(true); + ui->service->clear(); + + QStringList names; + + obs_property_t *services = obs_properties_get(props, "service"); + size_t services_count = obs_property_list_item_count(services); + for (size_t i = 0; i < services_count; i++) { + const char *name = obs_property_list_item_string(services, i); + names.push_back(name); + } + + if (showAll) + names.sort(); + + for (QString &name : names) + ui->service->addItem(name); + + if (!lastService.isEmpty()) { + int idx = ui->service->findText(lastService); + if (idx != -1) + ui->service->setCurrentIndex(idx); + } + + if (!showAll) { + ui->service->addItem( + QTStr("Basic.AutoConfig.StreamPage.Service.ShowAll"), + QVariant(true)); + } + + obs_properties_destroy(props); + + ui->service->blockSignals(false); +} + +void AutoConfigStreamPage::UpdateServerList() +{ + QString serviceName = ui->service->currentText(); + bool showMore = ui->service->currentData().toBool(); + + if (showMore) { + LoadServices(true); + ui->service->showPopup(); + return; + } else { + lastService = serviceName; + } + + obs_properties_t *props = obs_get_service_properties("rtmp_common"); + obs_property_t *services = obs_properties_get(props, "service"); + + OBSData settings = obs_data_create(); + obs_data_release(settings); + + obs_data_set_string(settings, "service", QT_TO_UTF8(serviceName)); + obs_property_modified(services, settings); + + obs_property_t *servers = obs_properties_get(props, "server"); + + ui->server->clear(); + + size_t servers_count = obs_property_list_item_count(servers); + for (size_t i = 0; i < servers_count; i++) { + const char *name = obs_property_list_item_name(servers, i); + const char *server = obs_property_list_item_string(servers, i); + ui->server->addItem(name, server); + } + + obs_properties_destroy(props); +} + +void AutoConfigStreamPage::UpdateCompleted() +{ + if (ui->key->text().isEmpty()) { + ready = false; + } else { + bool custom = ui->streamType->currentIndex() == 1; + if (custom) { + ready = !ui->customServer->text().isEmpty(); + } else { + ready = !wiz->testRegions || + ui->regionUS->isChecked() || + ui->regionEU->isChecked() || + ui->regionAsia->isChecked() || + ui->regionOther->isChecked(); + } + } + emit completeChanged(); +} + +/* ------------------------------------------------------------------------- */ + +AutoConfig::AutoConfig(QWidget *parent) + : QWizard(parent) +{ + OBSBasic *main = reinterpret_cast(parent); + main->EnableOutputs(false); + + installEventFilter(CreateShortcutFilter()); + + std::string serviceType; + GetServiceInfo(serviceType, serviceName, server, key); +#ifdef _WIN32 + setWizardStyle(QWizard::ModernStyle); +#endif + AutoConfigStreamPage *streamPage = new AutoConfigStreamPage(); + + setPage(StartPage, new AutoConfigStartPage()); + setPage(VideoPage, new AutoConfigVideoPage()); + setPage(StreamPage, streamPage); + setPage(TestPage, new AutoConfigTestPage()); + setWindowTitle(QTStr("Basic.AutoConfig.Beta")); + + obs_video_info ovi; + obs_get_video_info(&ovi); + + baseResolutionCX = ovi.base_width; + baseResolutionCY = ovi.base_height; + + /* ----------------------------------------- */ + /* load service/servers */ + + customServer = serviceType == "rtmp_custom"; + + QComboBox *serviceList = streamPage->ui->service; + + if (!serviceName.empty()) { + serviceList->blockSignals(true); + + int count = serviceList->count(); + bool found = false; + + for (int i = 0; i < count; i++) { + QString name = serviceList->itemText(i); + + if (name == serviceName.c_str()) { + serviceList->setCurrentIndex(i); + found = true; + break; + } + } + + if (!found) { + serviceList->insertItem(0, serviceName.c_str()); + serviceList->setCurrentIndex(0); + } + + serviceList->blockSignals(false); + } + + streamPage->UpdateServerList(); + streamPage->UpdateKeyLink(); + + if (!customServer) { + QComboBox *serverList = streamPage->ui->server; + int idx = serverList->findData(QString(server.c_str())); + if (idx == -1) + idx = 0; + + serverList->setCurrentIndex(idx); + } else { + streamPage->ui->customServer->setText(server.c_str()); + streamPage->ui->streamType->setCurrentIndex(1); + } + + if (!key.empty()) + streamPage->ui->key->setText(key.c_str()); + + int bitrate = config_get_int(main->Config(), "SimpleOutput", "VBitrate"); + streamPage->ui->bitrate->setValue(bitrate); + streamPage->ServiceChanged(); + + streamPage->ui->preferHardware->setChecked(os_get_physical_cores() <= 4); + + TestHardwareEncoding(); + if (!hardwareEncodingAvailable) { + delete streamPage->ui->preferHardware; + streamPage->ui->preferHardware = nullptr; + } + + setOptions(0); + setButtonText(QWizard::FinishButton, + QTStr("Basic.AutoConfig.ApplySettings")); + setButtonText(QWizard::BackButton, QTStr("Back")); + setButtonText(QWizard::NextButton, QTStr("Next")); + setButtonText(QWizard::CancelButton, QTStr("Cancel")); +} + +AutoConfig::~AutoConfig() +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + main->EnableOutputs(true); +} + +void AutoConfig::TestHardwareEncoding() +{ + size_t idx = 0; + const char *id; + while (obs_enum_encoder_types(idx++, &id)) { + if (strcmp(id, "ffmpeg_nvenc") == 0) + hardwareEncodingAvailable = nvencAvailable = true; + else if (strcmp(id, "obs_qsv11") == 0) + hardwareEncodingAvailable = qsvAvailable = true; + else if (strcmp(id, "amd_amf_h264") == 0) + hardwareEncodingAvailable = vceAvailable = true; + } +} + +bool AutoConfig::CanTestServer(const char *server) +{ + if (!testRegions || (regionUS && regionEU && regionAsia && regionOther)) + return true; + + if (service == Service::Twitch) { + if (astrcmp_n(server, "US West:", 8) == 0 || + astrcmp_n(server, "US East:", 8) == 0 || + astrcmp_n(server, "US Central:", 11) == 0) { + return regionUS; + } else if (astrcmp_n(server, "EU:", 3) == 0) { + return regionEU; + } else if (astrcmp_n(server, "Asia:", 5) == 0) { + return regionAsia; + } else if (regionOther) { + return true; + } + } else if (service == Service::Hitbox) { + if (strcmp(server, "Default") == 0) { + return true; + } else if (astrcmp_n(server, "US-West:", 8) == 0 || + astrcmp_n(server, "US-East:", 8) == 0) { + return regionUS; + } else if (astrcmp_n(server, "EU-", 3) == 0) { + return regionEU; + } else if (astrcmp_n(server, "South Korea:", 12) == 0 || + astrcmp_n(server, "Asia:", 5) == 0 || + astrcmp_n(server, "China:", 6) == 0) { + return regionAsia; + } else if (regionOther) { + return true; + } + } else if (service == Service::Beam) { + if (astrcmp_n(server, "US:", 3) == 0) { + return regionUS; + } else if (astrcmp_n(server, "EU:", 3) == 0) { + return regionEU; + } else if (astrcmp_n(server, "South Korea:", 12) == 0 || + astrcmp_n(server, "Asia:", 5) == 0) { + return regionAsia; + } else if (regionOther) { + return true; + } + } else { + return true; + } + + return false; +} + +void AutoConfig::done(int result) +{ + QWizard::done(result); + + if (result == QDialog::Accepted) { + if (type == Type::Streaming) + SaveStreamSettings(); + SaveSettings(); + } +} + +inline const char *AutoConfig::GetEncoderId(Encoder enc) +{ + switch (enc) { + case Encoder::NVENC: + return SIMPLE_ENCODER_NVENC; + case Encoder::QSV: + return SIMPLE_ENCODER_QSV; + case Encoder::AMD: + return SIMPLE_ENCODER_AMD; + default: + return SIMPLE_ENCODER_X264; + } +}; + +void AutoConfig::SaveStreamSettings() +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + /* ---------------------------------- */ + /* save service */ + + const char *service_id = customServer + ? "rtmp_custom" + : "rtmp_common"; + + obs_service_t *oldService = main->GetService(); + OBSData hotkeyData = obs_hotkeys_save_service(oldService); + obs_data_release(hotkeyData); + + OBSData settings = obs_data_create(); + obs_data_release(settings); + + if (!customServer) + obs_data_set_string(settings, "service", serviceName.c_str()); + obs_data_set_string(settings, "server", server.c_str()); + obs_data_set_string(settings, "key", key.c_str()); + + OBSService newService = obs_service_create(service_id, + "default_service", settings, hotkeyData); + obs_service_release(newService); + + if (!newService) + return; + + main->SetService(newService); + main->SaveService(); + + /* ---------------------------------- */ + /* save stream settings */ + + config_set_int(main->Config(), "SimpleOutput", "VBitrate", + idealBitrate); + config_set_string(main->Config(), "SimpleOutput", "StreamEncoder", + GetEncoderId(streamingEncoder)); + config_remove_value(main->Config(), "SimpleOutput", "UseAdvanced"); +} + +void AutoConfig::SaveSettings() +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + if (recordingEncoder != Encoder::Stream) + config_set_string(main->Config(), "SimpleOutput", "RecEncoder", + GetEncoderId(recordingEncoder)); + + const char *quality = recordingQuality == Quality::High + ? "Small" + : "Stream"; + + config_set_string(main->Config(), "Output", "Mode", "Simple"); + config_set_string(main->Config(), "SimpleOutput", "RecQuality", quality); + config_set_int(main->Config(), "Video", "BaseCX", baseResolutionCX); + config_set_int(main->Config(), "Video", "BaseCY", baseResolutionCY); + config_set_int(main->Config(), "Video", "OutputCX", idealResolutionCX); + config_set_int(main->Config(), "Video", "OutputCY", idealResolutionCY); + + if (fpsType != FPSType::UseCurrent) { + config_set_uint(main->Config(), "Video", "FPSType", 0); + config_set_string(main->Config(), "Video", "FPSCommon", + std::to_string(idealFPSNum).c_str()); + } + + main->ResetVideo(); + main->ResetOutputs(); + config_save_safe(main->Config(), "tmp", nullptr); +} diff --git a/UI/window-basic-auto-config.hpp b/UI/window-basic-auto-config.hpp new file mode 100644 index 0000000..1fbc692 --- /dev/null +++ b/UI/window-basic-auto-config.hpp @@ -0,0 +1,248 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class Ui_AutoConfigStartPage; +class Ui_AutoConfigVideoPage; +class Ui_AutoConfigStreamPage; +class Ui_AutoConfigTestPage; + +class AutoConfig : public QWizard { + Q_OBJECT + + friend class AutoConfigStartPage; + friend class AutoConfigVideoPage; + friend class AutoConfigStreamPage; + friend class AutoConfigTestPage; + + enum class Type { + Invalid, + Streaming, + Recording + }; + + enum class Service { + Twitch, + Hitbox, + Beam, + Other + }; + + enum class Encoder { + x264, + NVENC, + QSV, + AMD, + Stream + }; + + enum class Quality { + Stream, + High + }; + + enum class FPSType : int { + PreferHighFPS, + PreferHighRes, + UseCurrent, + fps30, + fps60 + }; + + static inline const char *GetEncoderId(Encoder enc); + + Service service = Service::Other; + Quality recordingQuality = Quality::Stream; + Encoder recordingEncoder = Encoder::Stream; + Encoder streamingEncoder = Encoder::x264; + Type type = Type::Streaming; + FPSType fpsType = FPSType::PreferHighFPS; + int idealBitrate = 2500; + int baseResolutionCX = 1920; + int baseResolutionCY = 1080; + int idealResolutionCX = 1280; + int idealResolutionCY = 720; + int idealFPSNum = 60; + int idealFPSDen = 1; + std::string serviceName; + std::string serverName; + std::string server; + std::string key; + + bool hardwareEncodingAvailable = false; + bool nvencAvailable = false; + bool qsvAvailable = false; + bool vceAvailable = false; + + int startingBitrate = 2500; + bool customServer = false; + bool bandwidthTest = false; + bool testRegions = true; + bool regionUS = true; + bool regionEU = true; + bool regionAsia = true; + bool regionOther = true; + bool preferHighFPS = false; + bool preferHardware = false; + int specificFPSNum = 0; + int specificFPSDen = 0; + + void TestHardwareEncoding(); + bool CanTestServer(const char *server); + + virtual void done(int result) override; + + void SaveStreamSettings(); + void SaveSettings(); + +public: + AutoConfig(QWidget *parent); + ~AutoConfig(); + + enum Page { + StartPage, + VideoPage, + StreamPage, + TestPage + }; +}; + +class AutoConfigStartPage : public QWizardPage { + Q_OBJECT + + friend class AutoConfig; + + Ui_AutoConfigStartPage *ui; + +public: + AutoConfigStartPage(QWidget *parent = nullptr); + ~AutoConfigStartPage(); + + virtual int nextId() const override; + +public slots: + void on_prioritizeStreaming_clicked(); + void on_prioritizeRecording_clicked(); +}; + +class AutoConfigVideoPage : public QWizardPage { + Q_OBJECT + + friend class AutoConfig; + + Ui_AutoConfigVideoPage *ui; + +public: + AutoConfigVideoPage(QWidget *parent = nullptr); + ~AutoConfigVideoPage(); + + virtual int nextId() const override; + virtual bool validatePage() override; +}; + +class AutoConfigStreamPage : public QWizardPage { + Q_OBJECT + + friend class AutoConfig; + + Ui_AutoConfigStreamPage *ui; + QString lastService; + bool ready = false; + + void LoadServices(bool showAll); + +public: + AutoConfigStreamPage(QWidget *parent = nullptr); + ~AutoConfigStreamPage(); + + virtual bool isComplete() const override; + virtual int nextId() const override; + virtual bool validatePage() override; + +public slots: + void on_show_clicked(); + void ServiceChanged(); + void UpdateKeyLink(); + void UpdateServerList(); + void UpdateCompleted(); +}; + +class AutoConfigTestPage : public QWizardPage { + Q_OBJECT + + friend class AutoConfig; + + QPointer results; + + Ui_AutoConfigTestPage *ui; + std::thread testThread; + std::condition_variable cv; + std::mutex m; + bool cancel = false; + bool started = false; + + enum class Stage { + Starting, + BandwidthTest, + StreamEncoder, + RecordingEncoder, + Finished + }; + + Stage stage = Stage::Starting; + bool softwareTested = false; + + void StartBandwidthStage(); + void StartStreamEncoderStage(); + void StartRecordingEncoderStage(); + + void FindIdealHardwareResolution(); + bool TestSoftwareEncoding(); + + void TestBandwidthThread(); + void TestStreamEncoderThread(); + void TestRecordingEncoderThread(); + + void FinalizeResults(); + + struct ServerInfo { + std::string name; + std::string address; + int bitrate = 0; + int ms = -1; + + inline ServerInfo() {} + + inline ServerInfo(const char *name_, const char *address_) + : name(name_), address(address_) + { + } + }; + + void GetServers(std::vector &servers); + +public: + AutoConfigTestPage(QWidget *parent = nullptr); + ~AutoConfigTestPage(); + + virtual void initializePage() override; + virtual void cleanupPage() override; + virtual bool isComplete() const override; + virtual int nextId() const override; + +public slots: + void NextStage(); + void UpdateMessage(QString message); + void Failure(QString message); + void Progress(int percentage); +}; diff --git a/UI/window-basic-filters.cpp b/UI/window-basic-filters.cpp index f3d53e8..744e3f7 100644 --- a/UI/window-basic-filters.cpp +++ b/UI/window-basic-filters.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -322,20 +323,43 @@ static bool filter_compatible(bool async, uint32_t sourceFlags, QMenu *OBSBasicFilters::CreateAddFilterPopupMenu(bool async) { uint32_t sourceFlags = obs_source_get_output_flags(source); - const char *type; + const char *type_str; bool foundValues = false; size_t idx = 0; + struct FilterInfo { + string type; + string name; + + inline FilterInfo(const char *type_, const char *name_) + : type(type_), name(name_) + {} + }; + + vector types; + while (obs_enum_filter_types(idx++, &type_str)) { + const char *name = obs_source_get_display_name(type_str); + + auto it = types.begin(); + for (; it != types.end(); ++it) { + if (it->name >= name) + break; + } + + types.emplace(it, type_str, name); + } + QMenu *popup = new QMenu(QTStr("Add"), this); - while (obs_enum_filter_types(idx++, &type)) { - const char *name = obs_source_get_display_name(type); - uint32_t filterFlags = obs_get_source_output_flags(type); + for (FilterInfo &type : types) { + uint32_t filterFlags = obs_get_source_output_flags( + type.type.c_str()); if (!filter_compatible(async, sourceFlags, filterFlags)) continue; - QAction *popupItem = new QAction(QT_UTF8(name), this); - popupItem->setData(QT_UTF8(type)); + QAction *popupItem = new QAction(QT_UTF8(type.name.c_str()), + this); + popupItem->setData(QT_UTF8(type.type.c_str())); connect(popupItem, SIGNAL(triggered(bool)), this, SLOT(AddFilterFromAction())); popup->addAction(popupItem); @@ -365,7 +389,7 @@ void OBSBasicFilters::AddNewFilter(const char *id) return; if (name.empty()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); AddNewFilter(id); @@ -375,7 +399,7 @@ void OBSBasicFilters::AddNewFilter(const char *id) existing_filter = obs_source_get_filter_by_name(source, name.c_str()); if (existing_filter) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); obs_source_release(existing_filter); @@ -678,13 +702,13 @@ void OBSBasicFilters::FilterNameEdited(QWidget *editor, QListWidget *list) listItem->setText(QT_UTF8(prevName)); if (foundFilter) { - QMessageBox::information(window(), + OBSMessageBox::information(window(), QTStr("NameExists.Title"), QTStr("NameExists.Text")); obs_source_release(foundFilter); } else if (name.empty()) { - QMessageBox::information(window(), + OBSMessageBox::information(window(), QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); } diff --git a/UI/window-basic-main-dropfiles.cpp b/UI/window-basic-main-dropfiles.cpp index 45475a6..6d094f8 100644 --- a/UI/window-basic-main-dropfiles.cpp +++ b/UI/window-basic-main-dropfiles.cpp @@ -62,29 +62,45 @@ void OBSBasic::AddDropSource(const char *data, DropType image) obs_data_t *settings = obs_data_create(); obs_source_t *source = nullptr; const char *type = nullptr; + QString name; switch (image) { case DropType_RawText: obs_data_set_string(settings, "text", data); +#ifdef _WIN32 type = "text_gdiplus"; +#else + type = "text_ft2_source"; +#endif break; case DropType_Text: +#ifdef _WIN32 obs_data_set_bool(settings, "read_from_file", true); obs_data_set_string(settings, "file", data); + name = QUrl::fromLocalFile(QString(data)).fileName(); type = "text_gdiplus"; +#else + obs_data_set_bool(settings, "from_file", true); + obs_data_set_string(settings, "text_file", data); + type = "text_ft2_source"; +#endif break; case DropType_Image: obs_data_set_string(settings, "file", data); + name = QUrl::fromLocalFile(QString(data)).fileName(); type = "image_source"; break; case DropType_Media: obs_data_set_string(settings, "local_file", data); + name = QUrl::fromLocalFile(QString(data)).fileName(); type = "ffmpeg_source"; break; } - const char *name = obs_source_get_display_name(type); - source = obs_source_create(type, GenerateSourceName(name).c_str(), + if (name.isEmpty()) + name = obs_source_get_display_name(type); + source = obs_source_create(type, + GenerateSourceName(QT_TO_UTF8(name)).c_str(), settings, nullptr); if (source) { OBSScene scene = main->GetCurrentScene(); diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 2222fe9..1dcd873 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "qt-wrappers.hpp" #include "audio-encoders.hpp" #include "window-basic-main.hpp" #include "window-basic-main-outputs.hpp" @@ -47,11 +48,14 @@ static void OBSStopStreaming(void *data, calldata_t *params) { BasicOutputHandler *output = static_cast(data); int code = (int)calldata_int(params, "code"); + const char *last_error = calldata_string(params, "last_error"); + + QString arg_last_error = QString::fromUtf8(last_error); output->streamingActive = false; output->delayActive = false; QMetaObject::invokeMethod(output->main, - "StreamingStop", Q_ARG(int, code)); + "StreamingStop", Q_ARG(int, code), Q_ARG(QString, arg_last_error)); } static void OBSStartRecording(void *data, calldata_t *params) @@ -565,21 +569,21 @@ void SimpleOutput::UpdateStreamingSettings_amd(obs_data_t *settings, int bitrate) { // Static Properties - obs_data_set_int(settings, "AMF.H264.Usage", 0); - obs_data_set_int(settings, "AMF.H264.Profile", 100); // High + obs_data_set_int(settings, "Usage", 0); + obs_data_set_int(settings, "Profile", 100); // High obs_data_set_string(settings, "profile", "high"); // High // Rate Control Properties - obs_data_set_int(settings, "AMF.H264.RateControlMethod", 1); + obs_data_set_int(settings, "RateControlMethod", 1); obs_data_set_string(settings, "rate_control", "CBR"); - obs_data_set_int(settings, "AMF.H264.Bitrate.Target", bitrate); + obs_data_set_int(settings, "Bitrate.Target", bitrate); obs_data_set_int(settings, "bitrate", bitrate); - obs_data_set_int(settings, "AMF.H264.FillerData", 1); - obs_data_set_int(settings, "AMF.H264.VBVBuffer", 1); - obs_data_set_int(settings, "AMF.H264.VBVBuffer.Size", bitrate); + obs_data_set_int(settings, "FillerData", 1); + obs_data_set_int(settings, "VBVBuffer", 1); + obs_data_set_int(settings, "VBVBuffer.Size", bitrate); // Picture Control Properties - obs_data_set_double(settings, "AMF.H264.KeyframeInterval", 2.0); + obs_data_set_double(settings, "KeyframeInterval", 2.0); obs_data_set_int(settings, "keyint_sec", 2); } @@ -588,21 +592,21 @@ void SimpleOutput::UpdateRecordingSettings_amd_cqp(int cqp) obs_data_t *settings = obs_data_create(); // Static Properties - obs_data_set_int(settings, "AMF.H264.Usage", 0); - obs_data_set_int(settings, "AMF.H264.Profile", 100); // High + obs_data_set_int(settings, "Usage", 0); + obs_data_set_int(settings, "Profile", 100); // High obs_data_set_string(settings, "profile", "high"); // High // Rate Control Properties - obs_data_set_int(settings, "AMF.H264.RateControlMethod", 0); + obs_data_set_int(settings, "RateControlMethod", 0); obs_data_set_string(settings, "rate_control", "CQP"); - obs_data_set_int(settings, "AMF.H264.QP.IFrame", cqp); - obs_data_set_int(settings, "AMF.H264.QP.PFrame", cqp); - obs_data_set_int(settings, "AMF.H264.QP.BFrame", cqp); - obs_data_set_int(settings, "AMF.H264.VBVBuffer", 1); - obs_data_set_int(settings, "AMF.H264.VBVBuffer.Size", 50000); + obs_data_set_int(settings, "QP.IFrame", cqp); + obs_data_set_int(settings, "QP.PFrame", cqp); + obs_data_set_int(settings, "QP.BFrame", cqp); + obs_data_set_int(settings, "VBVBuffer", 1); + obs_data_set_int(settings, "VBVBuffer.Size", 100000); // Picture Control Properties - obs_data_set_double(settings, "AMF.H264.KeyframeInterval", 2.0); + obs_data_set_double(settings, "KeyframeInterval", 2.0); obs_data_set_int(settings, "keyint_sec", 2); // Update and release @@ -778,7 +782,7 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer) if (!dir) { if (main->isVisible()) - QMessageBox::information(main, + OBSMessageBox::information(main, QTStr("Output.BadPath.Title"), QTStr("Output.BadPath.Text")); else @@ -1398,7 +1402,7 @@ bool AdvancedOutput::StartRecording() if (!dir) { if (main->isVisible()) - QMessageBox::information(main, + OBSMessageBox::information(main, QTStr("Output.BadPath.Title"), QTStr("Output.BadPath.Text")); else diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index badf5fa..7c5d426 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -102,13 +102,13 @@ static bool GetProfileName(QWidget *parent, std::string &name, return false; } if (name.empty()) { - QMessageBox::information(parent, + OBSMessageBox::information(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; } if (ProfileExists(name.c_str())) { - QMessageBox::information(parent, + OBSMessageBox::information(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); continue; @@ -407,7 +407,7 @@ void OBSBasic::on_actionRemoveProfile_triggered() QString text = QTStr("ConfirmRemove.Text"); text.replace("$1", QT_UTF8(oldName.c_str())); - QMessageBox::StandardButton button = QMessageBox::question(this, + QMessageBox::StandardButton button = OBSMessageBox::question(this, QTStr("ConfirmRemove.Title"), text); if (button == QMessageBox::No) return; @@ -487,7 +487,7 @@ void OBSBasic::on_actionImportProfile_triggered() profileDir + "/recordEncoder.json"); RefreshProfiles(); } else { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Basic.MainMenu.Profile.Import"), QTStr("Basic.MainMenu.Profile.Exists")); } diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index a715441..088fb70 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -114,13 +114,13 @@ static bool GetSceneCollectionName(QWidget *parent, std::string &name, return false; } if (name.empty()) { - QMessageBox::information(parent, + OBSMessageBox::information(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; } if (SceneCollectionExists(name.c_str())) { - QMessageBox::information(parent, + OBSMessageBox::information(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); continue; @@ -221,10 +221,26 @@ void OBSBasic::RefreshSceneCollections() EnumSceneCollections(addCollection); + /* force saving of first scene collection on first run, otherwise + * no scene collections will show up */ + if (!count) { + long prevDisableVal = disableSaving; + + disableSaving = 0; + SaveProjectNow(); + disableSaving = prevDisableVal; + + EnumSceneCollections(addCollection); + } + ui->actionRemoveSceneCollection->setEnabled(count > 1); OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + main->OpenSavedProjectors(); + main->ui->actionPasteFilters->setEnabled(false); + main->ui->actionPasteRef->setEnabled(false); + main->ui->actionPasteDup->setEnabled(false); } void OBSBasic::on_actionNewSceneCollection_triggered() @@ -314,7 +330,7 @@ void OBSBasic::on_actionRemoveSceneCollection_triggered() QString text = QTStr("ConfirmRemove.Text"); text.replace("$1", QT_UTF8(oldName.c_str())); - QMessageBox::StandardButton button = QMessageBox::question(this, + QMessageBox::StandardButton button = OBSMessageBox::question(this, QTStr("ConfirmRemove.Title"), text); if (button == QMessageBox::No) return; @@ -379,7 +395,7 @@ void OBSBasic::on_actionImportSceneCollection_triggered() QFile::copy(file, path + filename); RefreshSceneCollections(); } else { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Basic.MainMenu.SceneCollection.Import"), QTStr("Basic.MainMenu.SceneCollection.Exists")); } diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 64906d8..176d6bb 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -364,7 +364,7 @@ void OBSBasic::AddTransition() if (accepted) { if (name.empty()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); AddTransition(); @@ -373,7 +373,7 @@ void OBSBasic::AddTransition() source = FindTransition(name.c_str()); if (source) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -392,6 +392,9 @@ void OBSBasic::AddTransition() if (api) api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); + + ClearQuickTransitionWidgets(); + RefreshQuickTransitions(); } } @@ -445,6 +448,9 @@ void OBSBasic::on_transitionRemove_clicked() if (api) api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); + + ClearQuickTransitionWidgets(); + RefreshQuickTransitions(); } void OBSBasic::RenameTransition() @@ -464,7 +470,7 @@ void OBSBasic::RenameTransition() if (accepted) { if (name.empty()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); RenameTransition(); @@ -473,7 +479,7 @@ void OBSBasic::RenameTransition() source = FindTransition(name.c_str()); if (source) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -483,8 +489,11 @@ void OBSBasic::RenameTransition() obs_source_set_name(transition, name.c_str()); int idx = ui->transitions->findData(variant); - if (idx != -1) + if (idx != -1) { ui->transitions->setItemText(idx, QT_UTF8(name.c_str())); + ClearQuickTransitionWidgets(); + RefreshQuickTransitions(); + } } } @@ -919,6 +928,31 @@ void OBSBasic::QuickTransitionRemoveClicked() quickTransitions.erase(quickTransitions.begin() + idx); } +void OBSBasic::ClearQuickTransitionWidgets() +{ + if (!IsPreviewProgramMode()) + return; + + QVBoxLayout *programLayout = + reinterpret_cast(programOptions->layout()); + + for (int idx = 0;; idx++) { + QLayoutItem *item = programLayout->itemAt(idx); + if (!item) + break; + + QWidget *widget = item->widget(); + if (!widget) + continue; + + int id = widget->property("id").toInt(); + if (id != 0) { + delete widget; + idx--; + } + } +} + void OBSBasic::RefreshQuickTransitions() { if (!IsPreviewProgramMode()) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 8590dd7..aa47c39 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "obs-app.hpp" @@ -40,8 +41,10 @@ #include "item-widget-helpers.hpp" #include "window-basic-settings.hpp" #include "window-namedialog.hpp" +#include "window-basic-auto-config.hpp" #include "window-basic-source-select.hpp" #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" @@ -112,6 +115,11 @@ static void AddExtraModulePaths() string path = (char*)base_module_dir; #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"); + obs_add_module_path(config_bin, config_data); + #elif ARCH_BITS == 64 obs_add_module_path((path + "/bin/64bit").c_str(), (path + "/data").c_str()); @@ -625,20 +633,12 @@ void OBSBasic::LogScenes() void OBSBasic::Load(const char *file) { - if (!file || !os_file_exists(file)) { - blog(LOG_INFO, "No scene file found, creating default scene"); - CreateDefaultScene(true); - SaveProject(); - return; - } - disableSaving++; obs_data_t *data = obs_data_create_from_json_file_safe(file, "bak"); if (!data) { disableSaving--; - blog(LOG_ERROR, "Failed to load '%s', creating default scene", - file); + blog(LOG_INFO, "No scene file found, creating default scene"); CreateDefaultScene(true); SaveProject(); return; @@ -921,6 +921,17 @@ bool OBSBasic::InitBasicConfigDefaults() uint32_t cx = primaryScreen->size().width(); uint32_t cy = primaryScreen->size().height(); + bool oldResolutionDefaults = config_get_bool(App()->GlobalConfig(), + "General", "Pre19Defaults"); + + /* use 1920x1080 for new default base res if main monitor is above + * 1920x1080, but don't apply for people from older builds -- only to + * new users */ + if (!oldResolutionDefaults && (cx * cy) > (1920 * 1080)) { + cx = 1920; + cy = 1080; + } + /* ----------------------------------------------------- */ /* move over mixer values in advanced if older config */ if (config_has_user_value(basicConfig, "AdvOut", "RecTrackIndex") && @@ -1004,6 +1015,14 @@ bool OBSBasic::InitBasicConfigDefaults() config_set_default_uint (basicConfig, "Video", "BaseCX", cx); config_set_default_uint (basicConfig, "Video", "BaseCY", cy); + /* don't allow BaseCX/BaseCY to be susceptible to defaults changing */ + if (!config_has_user_value(basicConfig, "Video", "BaseCX") || + !config_has_user_value(basicConfig, "Video", "BaseCY")) { + config_set_uint(basicConfig, "Video", "BaseCX", cx); + config_set_uint(basicConfig, "Video", "BaseCY", cy); + config_save_safe(basicConfig, "tmp", nullptr); + } + config_set_default_string(basicConfig, "Output", "FilenameFormatting", "%CCYY-%MM-%DD %hh-%mm-%ss"); @@ -1036,6 +1055,15 @@ bool OBSBasic::InitBasicConfigDefaults() config_set_default_uint (basicConfig, "Video", "OutputCX", scale_cx); config_set_default_uint (basicConfig, "Video", "OutputCY", scale_cy); + /* don't allow OutputCX/OutputCY to be susceptible to defaults + * changing */ + if (!config_has_user_value(basicConfig, "Video", "OutputCX") || + !config_has_user_value(basicConfig, "Video", "OutputCY")) { + config_set_uint(basicConfig, "Video", "OutputCX", scale_cx); + config_set_uint(basicConfig, "Video", "OutputCY", scale_cy); + config_save_safe(basicConfig, "tmp", nullptr); + } + config_set_default_uint (basicConfig, "Video", "FPSType", 0); config_set_default_string(basicConfig, "Video", "FPSCommon", "30"); config_set_default_uint (basicConfig, "Video", "FPSInt", 30); @@ -1371,7 +1399,7 @@ void OBSBasic::OBSInit() bool alwaysOnTop = config_get_bool(App()->GlobalConfig(), "BasicWindow", "AlwaysOnTop"); - if (alwaysOnTop) { + if (alwaysOnTop || opt_always_on_top) { SetAlwaysOnTop(this, true); ui->actionAlwaysOnTop->setChecked(true); } @@ -1402,6 +1430,41 @@ void OBSBasic::OBSInit() SystemTray(true); OpenSavedProjectors(); + + bool has_last_version = config_has_user_value(App()->GlobalConfig(), + "General", "LastVersion"); + bool first_run = config_get_bool(App()->GlobalConfig(), "General", + "FirstRun"); + + if (!first_run) { + config_set_bool(App()->GlobalConfig(), "General", "FirstRun", + true); + 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 (config_get_bool(basicConfig, "General", "OpenStatsOnStartup")) + on_stats_triggered(); + + OBSBasicStats::InitializeValues(); } void OBSBasic::InitHotkeys() @@ -1928,6 +1991,7 @@ void OBSBasic::RemoveScene(OBSSource source) QListWidgetItem *sel = nullptr; int count = ui->scenes->count(); + for (int i = 0; i < count; i++) { auto item = ui->scenes->item(i); auto cur_scene = GetOBSRef(item); @@ -2139,6 +2203,17 @@ void OBSBasic::DeactivateAudioSource(OBSSource source) bool OBSBasic::QueryRemoveSource(obs_source_t *source) { + if (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE) { + int count = ui->scenes->count(); + + if (count == 1) { + OBSMessageBox::information(this, + QTStr("FinalScene.Title"), + QTStr("FinalScene.Text")); + return false; + } + } + const char *name = obs_source_get_name(source); QString text = QTStr("ConfirmRemove.Text"); @@ -2207,14 +2282,6 @@ void OBSBasic::CheckForUpdates(bool manualUpdate) #endif } -#ifdef __APPLE__ -#define VERSION_ENTRY "mac" -#elif _WIN32 -#define VERSION_ENTRY "windows" -#else -#define VERSION_ENTRY "other" -#endif - void OBSBasic::updateCheckFinished() { ui->actionCheckForUpdates->setEnabled(true); @@ -2250,7 +2317,7 @@ void OBSBasic::DuplicateSelectedScene() return; if (name.empty()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; @@ -2258,7 +2325,7 @@ void OBSBasic::DuplicateSelectedScene() obs_source_t *source = obs_get_source_by_name(name.c_str()); if (source) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -2632,6 +2699,9 @@ static inline enum video_format GetVideoFormatFromName(const char *name) int OBSBasic::ResetVideo() { + if (outputHandler && outputHandler->Active()) + return OBS_VIDEO_CURRENTLY_ACTIVE; + ProfileScope("OBSBasic::ResetVideo"); struct obs_video_info ovi; @@ -2682,6 +2752,12 @@ int OBSBasic::ResetVideo() ret = AttemptToResetVideo(&ovi); if (IS_WIN32 && ret != OBS_VIDEO_SUCCESS) { + if (ret == OBS_VIDEO_CURRENTLY_ACTIVE) { + blog(LOG_WARNING, "Tried to reset when " + "already active"); + return ret; + } + /* Try OpenGL if DirectX fails on windows */ if (astrcmpi(ovi.graphics_module, DL_OPENGL) != 0) { blog(LOG_WARNING, "Failed to initialize obs video (%d) " @@ -2698,6 +2774,8 @@ int OBSBasic::ResetVideo() ResizeProgram(ovi.base_width, ovi.base_height); } + OBSBasicStats::InitializeValues(); + return ret; } @@ -2723,26 +2801,30 @@ bool OBSBasic::ResetAudio() void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, const char *deviceDesc, int channel) { + bool disable = deviceId && strcmp(deviceId, "disabled") == 0; obs_source_t *source; obs_data_t *settings; - bool same = false; source = obs_get_output_source(channel); if (source) { - settings = obs_source_get_settings(source); - const char *curId = obs_data_get_string(settings, "device_id"); + if (disable) { + obs_set_output_source(channel, nullptr); + } else { + settings = obs_source_get_settings(source); + const char *oldId = obs_data_get_string(settings, + "device_id"); + if (strcmp(oldId, deviceId) != 0) { + obs_data_set_string(settings, "device_id", + deviceId); + obs_source_update(source, settings); + } + obs_data_release(settings); + } - same = (strcmp(curId, deviceId) == 0); - - obs_data_release(settings); obs_source_release(source); - } - if (!same) - obs_set_output_source(channel, nullptr); - - if (!same && strcmp(deviceId, "disabled") != 0) { - obs_data_t *settings = obs_data_create(); + } else if (!disable) { + settings = obs_data_create(); obs_data_set_string(settings, "device_id", deviceId); source = obs_source_create(sourceId, deviceDesc, settings, nullptr); @@ -2803,10 +2885,16 @@ void OBSBasic::CloseDialogs() } } + for (QPointer &projector : windowProjectors) { + delete projector; + projector.clear(); + } for (QPointer &projector : projectors) { delete projector; projector.clear(); } + + delete stats; } void OBSBasic::EnumDialogs() @@ -2879,7 +2967,7 @@ void OBSBasic::closeEvent(QCloseEvent *event) if (outputHandler && outputHandler->Active()) { SetShowing(true); - QMessageBox::StandardButton button = QMessageBox::question( + QMessageBox::StandardButton button = OBSMessageBox::question( this, QTStr("ConfirmExit.Title"), QTStr("ConfirmExit.Text")); @@ -2912,12 +3000,15 @@ void OBSBasic::closeEvent(QCloseEvent *event) /* Clear all scene data (dialogs, widgets, widget sub-items, scenes, * sources, etc) so that all references are released before shutdown */ ClearSceneData(); + + App()->quit(); } void OBSBasic::changeEvent(QEvent *event) { if (event->type() == QEvent::WindowStateChange && isMinimized() && + trayIcon && trayIcon->isVisible() && sysTrayMinimizeToTray()) { @@ -3063,6 +3154,12 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) AddProjectorMenuMonitors(sceneProjectorMenu, this, SLOT(OpenSceneProjector())); popup.addMenu(sceneProjectorMenu); + + QAction *sceneWindow = popup.addAction( + QTStr("SceneWindow"), + this, SLOT(OpenSceneWindow())); + + popup.addAction(sceneWindow); popup.addSeparator(); popup.addAction(QTStr("Filters"), this, SLOT(OpenSceneFilters())); @@ -3092,7 +3189,7 @@ void OBSBasic::on_actionAddScene_triggered() if (accepted) { if (name.empty()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); on_actionAddScene_triggered(); @@ -3101,7 +3198,7 @@ void OBSBasic::on_actionAddScene_triggered() obs_source_t *source = obs_get_source_by_name(name.c_str()); if (source) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -3330,6 +3427,12 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) popup.addMenu(previewProjector); + QAction *previewWindow = popup.addAction( + QTStr("PreviewWindow"), + this, SLOT(OpenPreviewWindow())); + + popup.addAction(previewWindow); + popup.addSeparator(); } @@ -3337,6 +3440,19 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) if (addSourceMenu) popup.addMenu(addSourceMenu); + ui->actionCopyFilters->setEnabled(false); + + popup.addSeparator(); + popup.addAction(ui->actionCopySource); + popup.addAction(ui->actionPasteRef); + popup.addAction(ui->actionPasteDup); + popup.addSeparator(); + + popup.addSeparator(); + popup.addAction(ui->actionCopyFilters); + popup.addAction(ui->actionPasteFilters); + popup.addSeparator(); + if (item) { if (addSourceMenu) popup.addSeparator(); @@ -3361,6 +3477,12 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) AddProjectorMenuMonitors(sourceProjector, this, SLOT(OpenSourceProjector())); + QAction *sourceWindow = popup.addAction( + QTStr("SourceWindow"), + this, SLOT(OpenSourceWindow())); + + popup.addAction(sourceWindow); + popup.addSeparator(); if (isAsyncVideo) { popup.addMenu(AddDeinterlacingMenu(source)); @@ -3371,6 +3493,7 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) popup.addSeparator(); popup.addMenu(sourceProjector); + popup.addAction(sourceWindow); popup.addSeparator(); action = popup.addAction(QTStr("Interact"), this, @@ -3383,6 +3506,8 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview) SLOT(OpenFilters())); popup.addAction(QTStr("Properties"), this, SLOT(on_actionSourceProperties_triggered())); + + ui->actionCopyFilters->setEnabled(true); } popup.exec(QCursor::pos()); @@ -3495,7 +3620,7 @@ void OBSBasic::AddSourcePopupMenu(const QPoint &pos) { if (!GetCurrentScene()) { // Tell the user he needs a scene first (help beginners). - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Basic.Main.AddSourceHelp.Title"), QTStr("Basic.Main.AddSourceHelp.Text")); return; @@ -3730,7 +3855,7 @@ void OBSBasic::logUploadFinished(const QString &text, const QString &error) ui->menuLogFiles->setEnabled(true); if (text.isEmpty()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("LogReturnDialog.ErrorUploadingLog"), error); return; @@ -3758,11 +3883,11 @@ static void RenameListItem(OBSBasic *parent, QListWidget *listWidget, listItem->setText(QT_UTF8(prevName)); if (foundSource) { - QMessageBox::information(parent, + OBSMessageBox::information(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); } else if (name.empty()) { - QMessageBox::information(parent, + OBSMessageBox::information(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); } @@ -3846,6 +3971,8 @@ void OBSBasic::StartStreaming() { if (outputHandler->StreamingActive()) return; + if (disableOutputsRef) + return; if (api) api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTING); @@ -3911,6 +4038,7 @@ inline void OBSBasic::OnActivate() { if (ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(false); + ui->autoConfigure->setEnabled(false); App()->IncrementSleepInhibition(); UpdateProcessPriority(); @@ -3923,6 +4051,7 @@ inline void OBSBasic::OnDeactivate() { if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); + ui->autoConfigure->setEnabled(true); App()->DecrementSleepInhibition(); ClearProcessPriority(); @@ -4058,34 +4187,45 @@ void OBSBasic::StreamStopping() api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING); } -void OBSBasic::StreamingStop(int code) +void OBSBasic::StreamingStop(int code, QString last_error) { - const char *errorMessage; + const char *errorDescription; + DStr errorMessage; + bool use_last_error = false; switch (code) { case OBS_OUTPUT_BAD_PATH: - errorMessage = Str("Output.ConnectFail.BadPath"); + errorDescription = Str("Output.ConnectFail.BadPath"); break; case OBS_OUTPUT_CONNECT_FAILED: - errorMessage = Str("Output.ConnectFail.ConnectFailed"); + use_last_error = true; + errorDescription = Str("Output.ConnectFail.ConnectFailed"); break; case OBS_OUTPUT_INVALID_STREAM: - errorMessage = Str("Output.ConnectFail.InvalidStream"); + errorDescription = Str("Output.ConnectFail.InvalidStream"); break; default: case OBS_OUTPUT_ERROR: - errorMessage = Str("Output.ConnectFail.Error"); + use_last_error = true; + errorDescription = Str("Output.ConnectFail.Error"); break; case OBS_OUTPUT_DISCONNECTED: /* doesn't happen if output is set to reconnect. note that * reconnects are handled in the output, not in the UI */ - errorMessage = Str("Output.ConnectFail.Disconnected"); + use_last_error = true; + errorDescription = Str("Output.ConnectFail.Disconnected"); } + if (use_last_error && !last_error.isEmpty()) + dstr_printf(errorMessage, "%s\n\n%s", errorDescription, + QT_TO_UTF8(last_error)); + else + dstr_copy(errorMessage, errorDescription); + ui->statusbar->StreamStopped(); ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); @@ -4105,11 +4245,11 @@ void OBSBasic::StreamingStop(int code) blog(LOG_INFO, STREAMING_STOP); if (code != OBS_OUTPUT_SUCCESS && isVisible()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Output.ConnectFail.Title"), QT_UTF8(errorMessage)); } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { - SysTrayNotify(QT_UTF8(errorMessage), QSystemTrayIcon::Warning); + SysTrayNotify(QT_UTF8(errorDescription), QSystemTrayIcon::Warning); } if (!startStreamMenu.isNull()) { @@ -4123,6 +4263,8 @@ void OBSBasic::StartRecording() { if (outputHandler->RecordingActive()) return; + if (disableOutputsRef) + return; if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING); @@ -4181,17 +4323,17 @@ void OBSBasic::RecordingStop(int code) blog(LOG_INFO, RECORDING_STOP); if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Output.RecordFail.Title"), QTStr("Output.RecordFail.Unsupported")); } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Output.RecordNoSpace.Title"), QTStr("Output.RecordNoSpace.Msg")); } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Output.RecordError.Title"), QTStr("Output.RecordError.Msg")); @@ -4223,6 +4365,8 @@ void OBSBasic::StartReplayBuffer() return; if (outputHandler->ReplayBufferActive()) return; + if (disableOutputsRef) + return; obs_output_t *output = outputHandler->replayBuffer; obs_data_t *hotkeys = obs_hotkeys_save_output(output); @@ -4233,7 +4377,7 @@ void OBSBasic::StartReplayBuffer() obs_data_release(hotkeys); if (!count) { - QMessageBox::information(this, + OBSMessageBox::information(this, RP_NO_HOTKEY_TITLE, RP_NO_HOTKEY_TEXT); return; @@ -4306,17 +4450,17 @@ void OBSBasic::ReplayBufferStop(int code) blog(LOG_INFO, REPLAY_BUFFER_STOP); if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Output.RecordFail.Title"), QTStr("Output.RecordFail.Unsupported")); } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Output.RecordNoSpace.Title"), QTStr("Output.RecordNoSpace.Msg")); } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("Output.RecordError.Title"), QTStr("Output.RecordError.Msg")); @@ -4347,7 +4491,7 @@ void OBSBasic::on_streamButton_clicked() if (confirm && isVisible()) { QMessageBox::StandardButton button = - QMessageBox::question(this, + OBSMessageBox::question(this, QTStr("ConfirmStop.Title"), QTStr("ConfirmStop.Text")); @@ -4362,7 +4506,7 @@ void OBSBasic::on_streamButton_clicked() if (confirm && isVisible()) { QMessageBox::StandardButton button = - QMessageBox::question(this, + OBSMessageBox::question(this, QTStr("ConfirmStart.Title"), QTStr("ConfirmStart.Text")); @@ -4445,7 +4589,12 @@ void OBSBasic::on_previewDisabledLabel_customContextMenuRequested( AddProjectorMenuMonitors(previewProjector, this, SLOT(OpenPreviewProjector())); + QAction *previewWindow = popup.addAction( + QTStr("PreviewWindow"), + this, SLOT(OpenPreviewWindow())); + popup.addMenu(previewProjector); + popup.addAction(previewWindow); popup.exec(QCursor::pos()); UNUSED_PARAMETER(pos); @@ -4881,7 +5030,8 @@ 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) +void OBSBasic::OpenProjector(obs_source_t *source, int monitor, bool window, + QString title) { /* seriously? 10 monitors? */ if (monitor > 9 || monitor > QGuiApplication::screens().size() - 1) @@ -4892,29 +5042,45 @@ void OBSBasic::OpenProjector(obs_source_t *source, int monitor) if (source == nullptr) isPreview = true; - delete projectors[monitor]; - projectors[monitor].clear(); - - RemoveSavedProjectors(monitor); - - OBSProjector *projector = new OBSProjector(nullptr, source); - - const char *name = obs_source_get_name(source); - - if (isPreview) { - previewProjectorArray.at((size_t)monitor) = 1; - } else { - projectorArray.at((size_t)monitor) = name; + if (!window) { + delete projectors[monitor]; + projectors[monitor].clear(); + RemoveSavedProjectors(monitor); } - projector->Init(monitor); - projectors[monitor] = projector; + OBSProjector *projector = new OBSProjector(nullptr, source, !!window); + const char *name = obs_source_get_name(source); + + if (!window) { + if (isPreview) { + previewProjectorArray.at((size_t)monitor) = 1; + } else { + projectorArray.at((size_t)monitor) = name; + } + } + + if (!window) { + projector->Init(monitor, false, nullptr); + projectors[monitor] = projector; + } else { + projector->Init(monitor, true, title); + + for (auto &projPtr : windowProjectors) { + if (!projPtr) { + projPtr = projector; + projector = nullptr; + } + } + + if (projector) + windowProjectors.push_back(projector); + } } void OBSBasic::OpenPreviewProjector() { int monitor = sender()->property("monitor").toInt(); - OpenProjector(nullptr, monitor); + OpenProjector(nullptr, monitor, false); } void OBSBasic::OpenSourceProjector() @@ -4924,7 +5090,7 @@ void OBSBasic::OpenSourceProjector() if (!item) return; - OpenProjector(obs_sceneitem_get_source(item), monitor); + OpenProjector(obs_sceneitem_get_source(item), monitor, false); } void OBSBasic::OpenSceneProjector() @@ -4934,7 +5100,44 @@ void OBSBasic::OpenSceneProjector() if (!scene) return; - OpenProjector(obs_scene_get_source(scene), monitor); + OpenProjector(obs_scene_get_source(scene), monitor, false); +} + +void OBSBasic::OpenPreviewWindow() +{ + int monitor = sender()->property("monitor").toInt(); + QString title = QTStr("PreviewWindow"); + OpenProjector(nullptr, monitor, true, title); +} + +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); +} + +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); } void OBSBasic::OpenSavedProjectors() @@ -4954,14 +5157,14 @@ void OBSBasic::OpenSavedProjectors() continue; } - OpenProjector(source, (int)i); + OpenProjector(source, (int)i, false); obs_source_release(source); } } for (size_t i = 0; i < previewProjectorArray.size(); i++) { if (previewProjectorArray.at(i) == 1) { - OpenProjector(nullptr, (int)i); + OpenProjector(nullptr, (int)i, false); } } } @@ -5260,3 +5463,82 @@ bool OBSBasic::sysTrayMinimizeToTray() return config_get_bool(GetGlobalConfig(), "BasicWindow", "SysTrayMinimizeToTray"); } + +void OBSBasic::on_actionCopySource_triggered() +{ + on_actionCopyTransform_triggered(); + + OBSSceneItem item = GetCurrentSceneItem(); + + if (!item) + return; + + OBSSource source = obs_sceneitem_get_source(item); + + copyString = obs_source_get_name(source); + copyVisible = obs_sceneitem_visible(item); + + ui->actionPasteRef->setEnabled(true); + ui->actionPasteDup->setEnabled(true); +} + +void OBSBasic::on_actionPasteRef_triggered() +{ + OBSBasicSourceSelect::SourcePaste(copyString, copyVisible, false); + on_actionPasteTransform_triggered(); +} + +void OBSBasic::on_actionPasteDup_triggered() +{ + OBSBasicSourceSelect::SourcePaste(copyString, copyVisible, true); + on_actionPasteTransform_triggered(); +} + +void OBSBasic::on_actionCopyFilters_triggered() +{ + OBSSceneItem item = GetCurrentSceneItem(); + + if (!item) + return; + + OBSSource source = obs_sceneitem_get_source(item); + + copyFiltersString = obs_source_get_name(source); + + ui->actionPasteFilters->setEnabled(true); +} + +void OBSBasic::on_actionPasteFilters_triggered() +{ + OBSSource source = obs_get_source_by_name(copyFiltersString); + OBSSceneItem sceneItem = GetCurrentSceneItem(); + + OBSSource dstSource = obs_sceneitem_get_source(sceneItem); + + if (source == dstSource) + return; + + obs_source_copy_filters(dstSource, source); +} + +void OBSBasic::on_autoConfigure_triggered() +{ + AutoConfig test(this); + test.setModal(true); + test.show(); + test.exec(); +} + +void OBSBasic::on_stats_triggered() +{ + if (!stats.isNull()) { + stats->show(); + stats->raise(); + return; + } + + OBSBasicStats *statsDlg; + statsDlg = new OBSBasicStats(nullptr); + statsDlg->show(); + stats = statsDlg; +} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index d89be13..9a4de40 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -42,6 +42,7 @@ class QMessageBox; class QListWidgetItem; class VolControl; class QNetworkReply; +class OBSBasicStats; #include "ui_OBSBasic.h" @@ -119,6 +120,10 @@ private: bool projectChanged = false; bool previewEnabled = true; + const char *copyString; + const char *copyFiltersString; + bool copyVisible = true; + QPointer updateCheckThread; QPointer logUploadThread; @@ -154,6 +159,9 @@ private: ConfigFile basicConfig; QPointer projectors[10]; + QList> windowProjectors; + + QPointer stats; QPointer startStreamMenu; @@ -229,7 +237,8 @@ private: void ClearSceneData(); void Nudge(int dist, MoveDir dir); - void OpenProjector(obs_source_t *source, int monitor); + void OpenProjector(obs_source_t *source, int monitor, bool window, + QString title = nullptr); void GetAudioSourceFilters(); void GetAudioSourceProperties(); @@ -273,6 +282,7 @@ private: void RemoveQuickTransitionHotkey(QuickTransition *qt); void LoadQuickTransitions(obs_data_array_t *array); obs_data_array_t *SaveQuickTransitions(); + void ClearQuickTransitionWidgets(); void RefreshQuickTransitions(); void CreateDefaultQuickTransitions(); @@ -308,6 +318,8 @@ private: int programCX = 0, programCY = 0; float programScale = 0.0f; + int disableOutputsRef = 0; + inline bool IsPreviewProgramMode() const { return os_atomic_load_bool(&previewProgramMode); @@ -351,7 +363,7 @@ public slots: void StreamingStart(); void StreamStopping(); - void StreamingStop(int errorcode); + void StreamingStop(int errorcode, QString last_error); void StartRecording(); void StopRecording(); @@ -413,6 +425,13 @@ private slots: void ToggleShowHide(); + void on_actionCopySource_triggered(); + void on_actionPasteRef_triggered(); + void on_actionPasteDup_triggered(); + + void on_actionCopyFilters_triggered(); + void on_actionPasteFilters_triggered(); + private: /* OBS Callbacks */ static void SceneReordered(void *data, calldata_t *params); @@ -480,6 +499,16 @@ public: void SaveService(); bool LoadService(); + inline void EnableOutputs(bool enable) + { + if (enable) { + if (--disableOutputsRef < 0) + disableOutputsRef = 0; + } else { + disableOutputsRef++; + } + } + void ReorderSceneItem(obs_sceneitem_t *item, size_t idx); QMenu *AddDeinterlacingMenu(obs_source_t *source); @@ -593,6 +622,9 @@ private slots: void on_modeSwitch_clicked(); + void on_autoConfigure_triggered(); + void on_stats_triggered(); + void logUploadFinished(const QString &text, const QString &error); void updateCheckFinished(); @@ -625,6 +657,10 @@ private slots: void OpenSourceProjector(); void OpenSceneProjector(); + void OpenPreviewWindow(); + void OpenSourceWindow(); + void OpenSceneWindow(); + public slots: void on_actionResetTransform_triggered(); diff --git a/UI/window-basic-preview.hpp b/UI/window-basic-preview.hpp index fed3c18..ac0ab30 100644 --- a/UI/window-basic-preview.hpp +++ b/UI/window-basic-preview.hpp @@ -108,7 +108,7 @@ public: /* use libobs allocator for alignment because the matrices itemToScreen * and screenToItem may contain SSE data, which will cause SSE * instructions to crash if the data is not aligned to at least a 16 - * byte boundry. */ + * byte boundary. */ static inline void* operator new(size_t size) {return bmalloc(size);} static inline void operator delete(void* ptr) {bfree(ptr);} }; diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp index b305f1d..d163d41 100644 --- a/UI/window-basic-properties.cpp +++ b/UI/window-basic-properties.cpp @@ -265,7 +265,7 @@ bool OBSBasicProperties::ConfirmQuit() { QMessageBox::StandardButton button; - button = QMessageBox::question(this, + button = OBSMessageBox::question(this, QTStr("Basic.PropertiesWindow.ConfirmTitle"), QTStr("Basic.PropertiesWindow.Confirm"), QMessageBox::Save | QMessageBox::Discard | diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 9493b31..52f10b9 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -151,6 +151,23 @@ static inline bool SetComboByValue(QComboBox *combo, const char *name) return false; } +static inline bool SetInvalidValue(QComboBox *combo, const char *name, + const char *data = nullptr) +{ + combo->insertItem(0, name, data); + + QStandardItemModel *model = + dynamic_cast(combo->model()); + if (!model) + return false; + + QStandardItem *item = model->item(0); + item->setFlags(Qt::NoItemFlags); + + combo->setCurrentIndex(0); + return true; +} + static inline QString GetComboData(QComboBox *combo) { int idx = combo->currentIndex(); @@ -260,6 +277,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) ui->setupUi(this); + main->EnableOutputs(false); + PopulateAACBitrates({ui->simpleOutputABitrate, ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate, ui->advOutTrack4Bitrate, @@ -274,6 +293,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->language, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->theme, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->enableAutoUpdates, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->openStatsOnStartup, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->warnBeforeStreamStart,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->warnBeforeStreamStop, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->hideProjectorCursor, CHECK_CHANGED, GENERAL_CHANGED); @@ -392,6 +412,9 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->resetOSXVSync, CHECK_CHANGED, ADV_CHANGED); #if defined(_WIN32) || defined(__APPLE__) HookWidget(ui->monitoringDevice, COMBO_CHANGED, ADV_CHANGED); +#endif +#ifdef _WIN32 + HookWidget(ui->disableAudioDucking, CHECK_CHANGED, ADV_CHANGED); #endif HookWidget(ui->filenameFormatting, EDIT_CHANGED, ADV_CHANGED); HookWidget(ui->overwriteIfExists, CHECK_CHANGED, ADV_CHANGED); @@ -461,6 +484,9 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) delete ui->advancedGeneralGroupBox; delete ui->enableNewSocketLoop; delete ui->enableLowLatencyMode; +#ifdef __APPLE__ + delete ui->disableAudioDucking; +#endif ui->rendererLabel = nullptr; ui->renderer = nullptr; ui->adapterLabel = nullptr; @@ -470,6 +496,9 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) ui->advancedGeneralGroupBox = nullptr; ui->enableNewSocketLoop = nullptr; ui->enableLowLatencyMode = nullptr; +#ifdef __APPLE__ + ui->disableAudioDucking = nullptr; +#endif #endif #ifndef __APPLE__ @@ -627,6 +656,11 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) UpdateAutomaticReplayBufferCheckboxes(); } +OBSBasicSettings::~OBSBasicSettings() +{ + main->EnableOutputs(true); +} + void OBSBasicSettings::SaveCombo(QComboBox *widget, const char *section, const char *value) { @@ -931,6 +965,9 @@ void OBSBasicSettings::LoadGeneralSettings() "General", "EnableAutoUpdates"); ui->enableAutoUpdates->setChecked(enableAutoUpdates); #endif + bool openStatsOnStartup = config_get_bool(main->Config(), + "General", "OpenStatsOnStartup"); + ui->openStatsOnStartup->setChecked(openStatsOnStartup); bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming"); @@ -1041,18 +1078,20 @@ void OBSBasicSettings::LoadRendererList() "Renderer"); ui->renderer->addItem(QT_UTF8("Direct3D 11")); - ui->renderer->addItem(QT_UTF8("OpenGL")); + if (opt_allow_opengl || strcmp(renderer, "OpenGL") == 0) + ui->renderer->addItem(QT_UTF8("OpenGL")); int idx = ui->renderer->findText(QT_UTF8(renderer)); if (idx == -1) idx = 0; - if (strcmp(renderer, "OpenGL") == 0) { - delete ui->adapter; - delete ui->adapterLabel; - ui->adapter = nullptr; - ui->adapterLabel = nullptr; - } + // the video adapter selection is not currently implemented, hide for now + // to avoid user confusion. was previously protected by + // if (strcmp(renderer, "OpenGL") == 0) + delete ui->adapter; + delete ui->adapterLabel; + ui->adapter = nullptr; + ui->adapterLabel = nullptr; ui->renderer->setCurrentIndex(idx); #endif @@ -1206,12 +1245,21 @@ void OBSBasicSettings::LoadResolutionLists() ui->baseResolution->clear(); + auto addRes = [this] (int cx, int cy) + { + QString res = ResString(cx, cy).c_str(); + if (ui->baseResolution->findText(res) == -1) + ui->baseResolution->addItem(res); + }; + for (QScreen* screen: QGuiApplication::screens()) { QSize as = screen->size(); - string res = ResString(as.width(), as.height()); - ui->baseResolution->addItem(res.c_str()); + addRes(as.width(), as.height()); } + addRes(1920, 1080); + addRes(1280, 720); + string outputResString = ResString(out_cx, out_cy); ui->baseResolution->lineEdit()->setText(ResString(cx, cy).c_str()); @@ -2012,22 +2060,8 @@ void OBSBasicSettings::LoadAdvancedSettings() LoadRendererList(); #if defined(_WIN32) || defined(__APPLE__) - QComboBox *cb = ui->monitoringDevice; - idx = cb->findData(monDevId); - if (idx == -1) { - cb->insertItem(0, monDevName, monDevId); - - QStandardItemModel *model = - dynamic_cast(cb->model()); - if (!model) - return; - - QStandardItem *item = model->item(0); - item->setFlags(Qt::NoItemFlags); - - idx = 0; - } - cb->setCurrentIndex(idx); + if (!SetComboByValue(ui->monitoringDevice, monDevId)) + SetInvalidValue(ui->monitoringDevice, monDevName, monDevId); #endif ui->filenameFormatting->setText(filename); @@ -2048,7 +2082,8 @@ void OBSBasicSettings::LoadAdvancedSettings() SetComboByName(ui->colorSpace, videoColorSpace); SetComboByValue(ui->colorRange, videoColorRange); - SetComboByValue(ui->bindToIP, bindIP); + if (!SetComboByValue(ui->bindToIP, bindIP)) + SetInvalidValue(ui->bindToIP, bindIP, bindIP); if (video_output_active(obs_get_video())) { ui->advancedVideoContainer->setEnabled(false); @@ -2063,6 +2098,10 @@ void OBSBasicSettings::LoadAdvancedSettings() ui->resetOSXVSync->setChecked(resetOSXVSync); ui->resetOSXVSync->setEnabled(disableOSXVSync); #elif _WIN32 + bool disableAudioDucking = config_get_bool(App()->GlobalConfig(), + "Audio", "DisableAudioDucking"); + ui->disableAudioDucking->setChecked(disableAudioDucking); + const char *processPriority = config_get_string(App()->GlobalConfig(), "General", "ProcessPriority"); bool enableNewSocketLoop = config_get_bool(main->Config(), "Output", @@ -2410,6 +2449,10 @@ void OBSBasicSettings::SaveGeneralSettings() "EnableAutoUpdates", ui->enableAutoUpdates->isChecked()); #endif + if (WidgetChanged(ui->openStatsOnStartup)) + config_set_bool(main->Config(), "General", + "OpenStatsOnStartup", + ui->openStatsOnStartup->isChecked()); if (WidgetChanged(ui->snappingEnabled)) config_set_bool(GetGlobalConfig(), "BasicWindow", "SnappingEnabled", @@ -2583,6 +2626,16 @@ void OBSBasicSettings::SaveAdvancedSettings() SaveCombo(ui->monitoringDevice, "Audio", "MonitoringDeviceName"); SaveComboData(ui->monitoringDevice, "Audio", "MonitoringDeviceId"); #endif + +#ifdef _WIN32 + if (WidgetChanged(ui->disableAudioDucking)) { + bool disable = ui->disableAudioDucking->isChecked(); + config_set_bool(App()->GlobalConfig(), + "Audio", "DisableAudioDucking", disable); + DisableAudioDucking(disable); + } +#endif + SaveEdit(ui->filenameFormatting, "Output", "FilenameFormatting"); SaveEdit(ui->simpleRBPrefix, "SimpleOutput", "RecRBPrefix"); SaveEdit(ui->simpleRBSuffix, "SimpleOutput", "RecRBSuffix"); @@ -2968,7 +3021,7 @@ bool OBSBasicSettings::QueryChanges() { QMessageBox::StandardButton button; - button = QMessageBox::question(this, + button = OBSMessageBox::question(this, QTStr("Basic.Settings.ConfirmTitle"), QTStr("Basic.Settings.Confirm"), QMessageBox::Yes | QMessageBox::No | @@ -3833,10 +3886,9 @@ void OBSBasicSettings::SimpleRecordingQualityLosslessWarning(int idx) QString("\n\n") + SIMPLE_OUTPUT_WARNING("Lossless.Msg"); - button = QMessageBox::question(this, + button = OBSMessageBox::question(this, SIMPLE_OUTPUT_WARNING("Lossless.Title"), - warningString, - QMessageBox::Yes | QMessageBox::No); + warningString); if (button == QMessageBox::No) { QMetaObject::invokeMethod(ui->simpleOutRecQuality, diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 6440322..5e56ec7 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -303,4 +303,5 @@ protected: public: OBSBasicSettings(QWidget *parent); + ~OBSBasicSettings(); }; diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index b66ba08..4cb6f09 100644 --- a/UI/window-basic-source-select.cpp +++ b/UI/window-basic-source-select.cpp @@ -93,7 +93,28 @@ static void AddSource(void *_data, obs_scene_t *scene) obs_sceneitem_set_visible(sceneitem, data->visible); } -static void AddExisting(const char *name, const bool visible) +static char *get_new_source_name(const char *name) +{ + struct dstr new_name = {0}; + int inc = 0; + + dstr_copy(&new_name, name); + + for (;;) { + obs_source_t *existing_source = obs_get_source_by_name( + new_name.array); + if (!existing_source) + break; + + obs_source_release(existing_source); + + dstr_printf(&new_name, "%s %d", name, ++inc + 1); + } + + return new_name.array; +} + +static void AddExisting(const char *name, bool visible, bool duplicate) { OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); OBSScene scene = main->GetCurrentScene(); @@ -102,6 +123,17 @@ static void AddExisting(const char *name, const bool visible) obs_source_t *source = obs_get_source_by_name(name); if (source) { + if (duplicate) { + obs_source_t *from = source; + char *new_name = get_new_source_name(name); + source = obs_source_duplicate(from, new_name, false); + bfree(new_name); + obs_source_release(from); + + if (!source) + return; + } + AddSourceData data; data.source = source; data.visible = visible; @@ -122,7 +154,7 @@ bool AddNew(QWidget *parent, const char *id, const char *name, obs_source_t *source = obs_get_source_by_name(name); if (source) { - QMessageBox::information(parent, + OBSMessageBox::information(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -155,10 +187,10 @@ void OBSBasicSourceSelect::on_buttonBox_accepted() if (!item) return; - AddExisting(QT_TO_UTF8(item->text()), visible); + AddExisting(QT_TO_UTF8(item->text()), visible, false); } else { if (ui->sourceName->text().isEmpty()) { - QMessageBox::information(this, + OBSMessageBox::information(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); return; @@ -243,3 +275,8 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) obs_enum_sources(EnumSources, this); } } + +void OBSBasicSourceSelect::SourcePaste(const char *name, bool visible, bool dup) +{ + AddExisting(name, visible, dup); +} diff --git a/UI/window-basic-source-select.hpp b/UI/window-basic-source-select.hpp index bdfdc21..7bab476 100644 --- a/UI/window-basic-source-select.hpp +++ b/UI/window-basic-source-select.hpp @@ -47,4 +47,6 @@ public: OBSBasicSourceSelect(OBSBasic *parent, const char *id); OBSSource newSource; + + static void SourcePaste(const char *name, bool visible, bool duplicate); }; diff --git a/UI/window-basic-stats.cpp b/UI/window-basic-stats.cpp new file mode 100644 index 0000000..d3dcbe3 --- /dev/null +++ b/UI/window-basic-stats.cpp @@ -0,0 +1,508 @@ +#include "obs-frontend-api/obs-frontend-api.h" + +#include "window-basic-stats.hpp" +#include "window-basic-main.hpp" +#include "platform.hpp" +#include "obs-app.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#define TIMER_INTERVAL 2000 + +static void setThemeID(QWidget *widget, const QString &themeID) +{ + if (widget->property("themeID").toString() != themeID) { + widget->setProperty("themeID", themeID); + + /* force style sheet recalculation */ + QString qss = widget->styleSheet(); + widget->setStyleSheet("/* */"); + widget->setStyleSheet(qss); + } +} + +OBSBasicStats::OBSBasicStats(QWidget *parent) + : QWidget (parent), + cpu_info (os_cpu_usage_info_start()), + timer (this) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(); + QGridLayout *topLayout = new QGridLayout(); + outputLayout = new QGridLayout(); + + int row = 0; + + auto newStatBare = [&] (QString name, QWidget *label, int col) + { + QLabel *typeLabel = new QLabel(name, this); + topLayout->addWidget(typeLabel, row, col); + topLayout->addWidget(label, row++, col + 1); + }; + + auto newStat = [&] (const char *strLoc, QWidget *label, int col) + { + std::string str = "Basic.Stats."; + str += strLoc; + newStatBare(QTStr(str.c_str()), label, col); + }; + + /* --------------------------------------------- */ + + cpuUsage = new QLabel(this); + hddSpace = new QLabel(this); +#ifdef _WIN32 + memUsage = new QLabel(this); +#endif + + newStat("CPUUsage", cpuUsage, 0); + newStat("HDDSpaceAvailable", hddSpace, 0); +#ifdef _WIN32 + newStat("MemoryUsage", memUsage, 0); +#endif + + fps = new QLabel(this); + renderTime = new QLabel(this); + skippedFrames = new QLabel(this); + missedFrames = new QLabel(this); + row = 0; + + newStatBare("FPS", fps, 2); + newStat("AverageTimeToRender", renderTime, 2); + newStat("MissedFrames", missedFrames, 2); + newStat("SkippedFrames", skippedFrames, 2); + + /* --------------------------------------------- */ + + QPushButton *closeButton = new QPushButton(QTStr("Close")); + QPushButton *resetButton = new QPushButton(QTStr("Reset")); + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(); + buttonLayout->addWidget(resetButton); + buttonLayout->addWidget(closeButton); + + /* --------------------------------------------- */ + + int col = 0; + auto addOutputCol = [&] (const char *loc) + { + QLabel *label = new QLabel(QTStr(loc), this); + label->setStyleSheet("font-weight: bold"); + outputLayout->addWidget(label, 0, col++); + }; + + addOutputCol("Basic.Settings.Output"); + addOutputCol("Basic.Stats.Status"); + addOutputCol("Basic.Stats.DroppedFrames"); + addOutputCol("Basic.Stats.MegabytesSent"); + addOutputCol("Basic.Stats.Bitrate"); + + /* --------------------------------------------- */ + + AddOutputLabels(QTStr("Basic.Stats.Output.Stream")); + AddOutputLabels(QTStr("Basic.Stats.Output.Recording")); + + /* --------------------------------------------- */ + + QVBoxLayout *outputContainerLayout = new QVBoxLayout(); + outputContainerLayout->addLayout(outputLayout); + outputContainerLayout->addStretch(); + + QWidget *widget = new QWidget(this); + widget->setLayout(outputContainerLayout); + + QScrollArea *scrollArea = new QScrollArea(this); + scrollArea->setWidget(widget); + scrollArea->setWidgetResizable(true); + + /* --------------------------------------------- */ + + mainLayout->addLayout(topLayout); + mainLayout->addWidget(scrollArea); + mainLayout->addLayout(buttonLayout); + setLayout(mainLayout); + + /* --------------------------------------------- */ + + 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")); + setWindowModality(Qt::NonModal); + setAttribute(Qt::WA_DeleteOnClose, true); + + QObject::connect(&timer, &QTimer::timeout, this, &OBSBasicStats::Update); + timer.setInterval(TIMER_INTERVAL); + timer.start(); + Update(); + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + const char *geometry = config_get_string(main->Config(), + "Stats", "geometry"); + if (geometry != NULL) { + QByteArray byteArray = QByteArray::fromBase64( + QByteArray(geometry)); + restoreGeometry(byteArray); + + QRect windowGeometry = normalGeometry(); + if (!WindowPositionValid(windowGeometry)) { + QRect rect = App()->desktop()->geometry(); + setGeometry(QStyle::alignedRect( + Qt::LeftToRight, + Qt::AlignCenter, + size(), rect)); + } + } +} + +void OBSBasicStats::closeEvent(QCloseEvent *event) +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + if (isVisible()) { + config_set_string(main->Config(), + "Stats", "geometry", + saveGeometry().toBase64().constData()); + config_save_safe(main->Config(), "tmp", nullptr); + } + + QWidget::closeEvent(event); +} + +OBSBasicStats::~OBSBasicStats() +{ + os_cpu_usage_info_destroy(cpu_info); +} + +void OBSBasicStats::AddOutputLabels(QString name) +{ + OutputLabels ol; + ol.name = new QLabel(name, this); + ol.status = new QLabel(this); + ol.droppedFrames = new QLabel(this); + ol.megabytesSent = new QLabel(this); + ol.bitrate = new QLabel(this); + + int newPointSize = ol.status->font().pointSize(); + newPointSize *= 13; + newPointSize /= 10; + QString qss = + QString("font-size: %1pt").arg(QString::number(newPointSize)); + ol.status->setStyleSheet(qss); + + int col = 0; + int row = outputLabels.size() + 1; + outputLayout->addWidget(ol.name, row, col++); + outputLayout->addWidget(ol.status, row, col++); + outputLayout->addWidget(ol.droppedFrames, row, col++); + outputLayout->addWidget(ol.megabytesSent, row, col++); + outputLayout->addWidget(ol.bitrate, row, col++); + outputLabels.push_back(ol); +} + +static uint32_t first_encoded = 0xFFFFFFFF; +static uint32_t first_skipped = 0xFFFFFFFF; +static uint32_t first_rendered = 0xFFFFFFFF; +static uint32_t first_lagged = 0xFFFFFFFF; + +void OBSBasicStats::InitializeValues() +{ + video_t *video = obs_get_video(); + first_encoded = video_output_get_total_frames(video); + first_skipped = video_output_get_skipped_frames(video); + first_rendered = obs_get_total_frames(); + first_lagged = obs_get_lagged_frames(); +} + +void OBSBasicStats::Update() +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + /* TODO: Un-hardcode */ + + struct obs_video_info ovi = {}; + obs_get_video_info(&ovi); + + OBSOutput strOutput = obs_frontend_get_streaming_output(); + OBSOutput recOutput = obs_frontend_get_recording_output(); + obs_output_release(strOutput); + obs_output_release(recOutput); + + if (!strOutput || !recOutput) + return; + + /* ------------------------------------------- */ + /* general usage */ + + double curFPS = obs_get_active_fps(); + double obsFPS = (double)ovi.fps_num / (double)ovi.fps_den; + + QString str = QString::number(curFPS, 'f', 2); + fps->setText(str); + + if (curFPS < (obsFPS * 0.8)) + setThemeID(fps, "error"); + else if (curFPS < (obsFPS * 0.95)) + setThemeID(fps, "warning"); + else + setThemeID(fps, ""); + + /* ------------------ */ + + double usage = os_cpu_usage_info_query(cpu_info); + str = QString::number(usage, 'g', 2) + QStringLiteral("%"); + cpuUsage->setText(str); + + /* ------------------ */ + + const char *mode = config_get_string(main->Config(), "Output", "Mode"); + const char *path = strcmp(mode, "Advanced") ? + config_get_string(main->Config(), "SimpleOutput", "FilePath") : + config_get_string(main->Config(), "AdvOut", "RecFilePath"); + +#define MBYTE (1024ULL * 1024ULL) +#define GBYTE (1024ULL * 1024ULL * 1024ULL) +#define TBYTE (1024ULL * 1024ULL * 1024ULL * 1024ULL) + uint64_t num_bytes = os_get_free_disk_space(path); + QString abrv = QStringLiteral(" MB"); + long double num; + + num = (long double)num_bytes / (1024.0l * 1024.0l); + if (num_bytes > TBYTE) { + num /= 1024.0l * 1024.0l; + abrv = QStringLiteral(" TB"); + } else if (num_bytes > GBYTE) { + num /= 1024.0l; + abrv = QStringLiteral(" GB"); + } + + str = QString::number(num, 'f', 1) + abrv; + hddSpace->setText(str); + + if (num_bytes < GBYTE) + setThemeID(hddSpace, "error"); + else if (num_bytes < (5 * GBYTE)) + setThemeID(hddSpace, "warning"); + else + setThemeID(hddSpace, ""); + + /* ------------------ */ + +#ifdef _WIN32 + num = (long double)CurrentMemoryUsage() / (1024.0l * 1024.0l); + + str = QString::number(num, 'f', 1) + QStringLiteral(" MB"); + memUsage->setText(str); +#endif + + /* ------------------ */ + + num = (long double)obs_get_average_frame_time_ns() / 1000000.0l; + + str = QString::number(num, 'f', 1) + QStringLiteral(" ms"); + renderTime->setText(str); + + long double fpsFrameTime = + (long double)ovi.fps_den * 1000.0l / (long double)ovi.fps_num; + + if (num > fpsFrameTime) + setThemeID(renderTime, "error"); + else if (num > fpsFrameTime * 0.75l) + setThemeID(renderTime, "warning"); + else + setThemeID(renderTime, ""); + + /* ------------------ */ + + video_t *video = obs_get_video(); + uint32_t total_encoded = video_output_get_total_frames(video); + uint32_t total_skipped = video_output_get_skipped_frames(video); + + if (total_encoded < first_encoded || total_skipped < first_skipped) { + first_encoded = total_encoded; + first_skipped = total_skipped; + } + total_encoded -= first_encoded; + total_skipped -= first_skipped; + + num = total_encoded + ? (long double)total_skipped / (long double)total_encoded + : 0.0l; + num *= 100.0l; + + str = QString("%1 / %2 (%3%)").arg( + QString::number(total_skipped), + QString::number(total_encoded), + QString::number(num, 'f', 1)); + skippedFrames->setText(str); + + if (num > 5.0l) + setThemeID(skippedFrames, "error"); + else if (num > 1.0l) + setThemeID(skippedFrames, "warning"); + else + setThemeID(skippedFrames, ""); + + /* ------------------ */ + + uint32_t total_rendered = obs_get_total_frames(); + uint32_t total_lagged = obs_get_lagged_frames(); + + if (total_rendered < first_rendered || total_lagged < first_lagged) { + first_rendered = total_rendered; + first_lagged = total_lagged; + } + total_rendered -= first_rendered; + total_lagged -= first_lagged; + + num = total_rendered + ? (long double)total_lagged / (long double)total_rendered + : 0.0l; + num *= 100.0l; + + str = QString("%1 / %2 (%3%)").arg( + QString::number(total_lagged), + QString::number(total_rendered), + QString::number(num, 'f', 1)); + missedFrames->setText(str); + + if (num > 5.0l) + setThemeID(missedFrames, "error"); + else if (num > 1.0l) + setThemeID(missedFrames, "warning"); + else + setThemeID(missedFrames, ""); + + /* ------------------------------------------- */ + /* recording/streaming stats */ + + outputLabels[0].Update(strOutput); + outputLabels[1].Update(recOutput); +} + +void OBSBasicStats::Reset() +{ + timer.start(); + + first_encoded = 0xFFFFFFFF; + first_skipped = 0xFFFFFFFF; + first_rendered = 0xFFFFFFFF; + first_lagged = 0xFFFFFFFF; + + OBSOutput strOutput = obs_frontend_get_streaming_output(); + OBSOutput recOutput = obs_frontend_get_recording_output(); + obs_output_release(strOutput); + obs_output_release(recOutput); + + outputLabels[0].Reset(strOutput); + outputLabels[1].Reset(recOutput); + Update(); +} + +void OBSBasicStats::OutputLabels::Update(obs_output_t *output) +{ + if (!output) + return; + + const char *id = obs_obj_get_id(output); + bool rec = strcmp(id, "rtmp_output") != 0; + + uint64_t totalBytes = obs_output_get_total_bytes(output); + uint64_t curTime = os_gettime_ns(); + uint64_t bytesSent = totalBytes; + + if (bytesSent < lastBytesSent) + bytesSent = 0; + if (bytesSent == 0) + lastBytesSent = 0; + + uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8; + long double timePassed = (long double)(curTime - lastBytesSentTime) / + 1000000000.0l; + long double kbps = (long double)bitsBetween / + timePassed / 1000.0l; + + if (timePassed < 0.01l) + kbps = 0.0l; + + QString str = QTStr("Basic.Stats.Status.Inactive"); + QString themeID; + if (rec) { + if (obs_output_active(output)) + str = QTStr("Basic.Stats.Status.Recording"); + } else { + if (obs_output_active(output)) { + if (obs_output_reconnecting(output)) { + str = QTStr("Basic.Stats.Status.Reconnecting"); + themeID = "error"; + } else { + str = QTStr("Basic.Stats.Status.Live"); + themeID = "good"; + } + } + } + + status->setText(str); + setThemeID(status, themeID); + + long double num = (long double)totalBytes / (1024.0l * 1024.0l); + + megabytesSent->setText( + QString("%1 MB").arg(QString::number(num, 'f', 1))); + bitrate->setText( + QString("%1 kb/s").arg(QString::number(kbps, 'f', 0))); + + if (!rec) { + int total = obs_output_get_total_frames(output); + int dropped = obs_output_get_frames_dropped(output); + + if (total < first_total || dropped < first_dropped) { + first_total = 0; + first_dropped = 0; + } + + total -= first_total; + dropped -= first_dropped; + + num = total + ? (long double)dropped / (long double)total * 100.0l + : 0.0l; + + str = QString("%1 / %2 (%3%)").arg( + QString::number(dropped), + QString::number(total), + QString::number(num, 'f', 1)); + droppedFrames->setText(str); + + if (num > 5.0l) + setThemeID(droppedFrames, "error"); + else if (num > 1.0l) + setThemeID(droppedFrames, "warning"); + else + setThemeID(droppedFrames, ""); + } + + lastBytesSent = bytesSent; + lastBytesSentTime = curTime; +} + +void OBSBasicStats::OutputLabels::Reset(obs_output_t *output) +{ + if (!output) + return; + + first_total = obs_output_get_total_frames(output); + first_dropped = obs_output_get_frames_dropped(output); +} diff --git a/UI/window-basic-stats.hpp b/UI/window-basic-stats.hpp new file mode 100644 index 0000000..d6502d5 --- /dev/null +++ b/UI/window-basic-stats.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class QGridLayout; +class QCloseEvent; + +class OBSBasicStats : public QWidget { + Q_OBJECT + + QLabel *fps = nullptr; + QLabel *cpuUsage = nullptr; + QLabel *hddSpace = nullptr; + QLabel *memUsage = nullptr; + + QLabel *renderTime = nullptr; + QLabel *skippedFrames = nullptr; + QLabel *missedFrames = nullptr; + + QGridLayout *outputLayout = nullptr; + + os_cpu_usage_info_t *cpu_info = nullptr; + + QTimer timer; + + struct OutputLabels { + QPointer name; + QPointer status; + QPointer droppedFrames; + QPointer megabytesSent; + QPointer bitrate; + + uint64_t lastBytesSent = 0; + uint64_t lastBytesSentTime = 0; + + int first_total = 0; + int first_dropped = 0; + + void Update(obs_output_t *output); + void Reset(obs_output_t *output); + }; + + QList outputLabels; + + void AddOutputLabels(QString name); + void Update(); + void Reset(); + + virtual void closeEvent(QCloseEvent *event) override; + +public: + OBSBasicStats(QWidget *parent = nullptr); + ~OBSBasicStats(); + + static void InitializeValues(); +}; diff --git a/UI/window-namedialog.cpp b/UI/window-namedialog.cpp index 38f15df..93193ac 100644 --- a/UI/window-namedialog.cpp +++ b/UI/window-namedialog.cpp @@ -16,6 +16,7 @@ ******************************************************************************/ #include "window-namedialog.hpp" +#include "qt-wrappers.hpp" #include "ui_NameDialog.h" #include "obs-app.hpp" @@ -46,7 +47,7 @@ bool NameDialog::AskForName(QWidget *parent, const QString &title, bool accepted = (dialog.exec() == DialogCode::Accepted); if (accepted) { - str = dialog.ui->userText->text().toStdString(); + str = QT_TO_UTF8(dialog.ui->userText->text()); while (str.size() && IsWhitespace(str.back())) str.erase(str.end() - 1); diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index e894897..134258d 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -9,14 +9,18 @@ #include "platform.hpp" #include "obs-app.hpp" -OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_) +OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window) : OBSQTDisplay (widget, - Qt::Window | Qt::FramelessWindowHint | - Qt::X11BypassWindowManagerHint), + Qt::Window), source (source_), removedSignal (obs_source_get_signal_handler(source), "remove", OBSSourceRemoved, this) { + if (!window) { + setWindowFlags(Qt::FramelessWindowHint | + Qt::X11BypassWindowManagerHint); + } + setAttribute(Qt::WA_DeleteOnClose, true); //disable application quit when last window closed @@ -34,13 +38,14 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_) bool hideCursor = config_get_bool(GetGlobalConfig(), "BasicWindow", "HideProjectorCursor"); - if (hideCursor) { + if (hideCursor && !window) { QPixmap empty(16, 16); empty.fill(Qt::transparent); setCursor(QCursor(empty)); } App()->IncrementSleepInhibition(); + resize(480, 270); } OBSProjector::~OBSProjector() @@ -50,29 +55,36 @@ OBSProjector::~OBSProjector() App()->DecrementSleepInhibition(); } -void OBSProjector::Init(int monitor) +void OBSProjector::Init(int monitor, bool window, QString title) { QScreen *screen = QGuiApplication::screens()[monitor]; - setGeometry(screen->geometry()); + if (!window) + setGeometry(screen->geometry()); bool alwaysOnTop = config_get_bool(GetGlobalConfig(), "BasicWindow", "ProjectorAlwaysOnTop"); - if (alwaysOnTop) + if (alwaysOnTop && !window) SetAlwaysOnTop(this, true); + if (window) + setWindowTitle(title); + show(); if (source) obs_source_inc_showing(source); - QAction *action = new QAction(this); - action->setShortcut(Qt::Key_Escape); - addAction(action); - - connect(action, SIGNAL(triggered()), this, SLOT(EscapeTriggered())); + if (!window) { + QAction *action = new QAction(this); + action->setShortcut(Qt::Key_Escape); + addAction(action); + connect(action, SIGNAL(triggered()), this, + SLOT(EscapeTriggered())); + } savedMonitor = monitor; + isWindow = window; } void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) @@ -136,8 +148,12 @@ void OBSProjector::mousePressEvent(QMouseEvent *event) void OBSProjector::EscapeTriggered() { - OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); - main->RemoveSavedProjectors(savedMonitor); + if (!isWindow) { + OBSBasic *main = + reinterpret_cast(App()->GetMainWindow()); + + main->RemoveSavedProjectors(savedMonitor); + } deleteLater(); } diff --git a/UI/window-projector.hpp b/UI/window-projector.hpp index 394d270..48205dd 100644 --- a/UI/window-projector.hpp +++ b/UI/window-projector.hpp @@ -19,13 +19,14 @@ private: void mousePressEvent(QMouseEvent *event) override; int savedMonitor = 0; + bool isWindow = false; private slots: void EscapeTriggered(); public: - OBSProjector(QWidget *parent, obs_source_t *source); + OBSProjector(QWidget *parent, obs_source_t *source, bool window); ~OBSProjector(); - void Init(int monitor); + void Init(int monitor, bool window, QString title); }; diff --git a/UI/window-remux.cpp b/UI/window-remux.cpp index ea9ecce..d7308ca 100644 --- a/UI/window-remux.cpp +++ b/UI/window-remux.cpp @@ -40,7 +40,8 @@ OBSRemux::OBSRemux(const char *path, QWidget *parent) ui->setupUi(this); ui->progressBar->setVisible(false); - ui->remux->setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(false); ui->targetFile->setEnabled(false); ui->browseTarget->setEnabled(false); @@ -54,11 +55,19 @@ OBSRemux::OBSRemux(const char *path, QWidget *parent) [&]() { BrowseInput(); }); connect(ui->browseTarget, &QPushButton::clicked, [&]() { BrowseOutput(); }); - connect(ui->remux, &QPushButton::clicked, [&]() { Remux(); }); connect(ui->sourceFile, &QLineEdit::textChanged, this, &OBSRemux::inputChanged); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setText(QTStr("Remux.Remux")); + + connect(ui->buttonBox->button(QDialogButtonBox::Ok), + SIGNAL(clicked()), this, SLOT(Remux())); + + connect(ui->buttonBox->button(QDialogButtonBox::Close), + SIGNAL(clicked()), this, SLOT(close())); + worker->moveToThread(&remuxer); remuxer.start(); @@ -116,12 +125,14 @@ void OBSRemux::BrowseInput() void OBSRemux::inputChanged(const QString &path) { if (!QFileInfo::exists(path)) { - ui->remux->setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(false); return; } ui->sourceFile->setText(path); - ui->remux->setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(true); QFileInfo fi(path); QString mp4 = fi.path() + "/" + fi.baseName() + ".mp4"; @@ -146,9 +157,8 @@ void OBSRemux::BrowseOutput() void OBSRemux::Remux() { if (QFileInfo::exists(ui->targetFile->text())) - if (QMessageBox::question(this, QTStr("Remux.FileExistsTitle"), - QTStr("Remux.FileExists"), - QMessageBox::Yes | QMessageBox::No) != + if (OBSMessageBox::question(this, QTStr("Remux.FileExistsTitle"), + QTStr("Remux.FileExists")) != QMessageBox::Yes) return; @@ -161,7 +171,8 @@ void OBSRemux::Remux() worker->lastProgress = 0.f; ui->progressBar->setVisible(true); - ui->remux->setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(false); emit remux(); } @@ -189,13 +200,14 @@ void OBSRemux::updateProgress(float percent) void OBSRemux::remuxFinished(bool success) { - QMessageBox::information(this, QTStr("Remux.FinishedTitle"), + OBSMessageBox::information(this, QTStr("Remux.FinishedTitle"), success ? QTStr("Remux.Finished") : QTStr("Remux.FinishedError")); worker->job.reset(); ui->progressBar->setVisible(false); - ui->remux->setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(true); } RemuxWorker::RemuxWorker() diff --git a/UI/window-remux.hpp b/UI/window-remux.hpp index 8bda363..d689828 100644 --- a/UI/window-remux.hpp +++ b/UI/window-remux.hpp @@ -39,7 +39,6 @@ class OBSRemux : public QDialog { void BrowseInput(); void BrowseOutput(); - void Remux(); bool Stop(); @@ -58,6 +57,7 @@ private slots: public slots: void updateProgress(float percent); void remuxFinished(bool success); + void Remux(); signals: void remux(); diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 5859065..33704b2 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -5,7 +5,12 @@ endif() add_subdirectory(glad) add_subdirectory(ipc-util) -add_subdirectory(libff) + +if(BUILD_LIBFF) + add_subdirectory(libff) +endif() + +add_subdirectory(media-playback) add_subdirectory(file-updater) if(WIN32) diff --git a/deps/libff/libff/ff-util.c b/deps/libff/libff/ff-util.c index ca6aa91..03fe39a 100644 --- a/deps/libff/libff/ff-util.c +++ b/deps/libff/libff/ff-util.c @@ -47,7 +47,7 @@ struct ff_codec_desc { void ff_init() { av_register_all(); - avdevice_register_all(); + //avdevice_register_all(); avcodec_register_all(); avformat_network_init(); } diff --git a/deps/media-playback/CMakeLists.txt b/deps/media-playback/CMakeLists.txt new file mode 100644 index 0000000..ae5b3c5 --- /dev/null +++ b/deps/media-playback/CMakeLists.txt @@ -0,0 +1,45 @@ +project(media-playback) + +find_package(FFmpeg REQUIRED + COMPONENTS avcodec avdevice avutil avformat) + +include_directories( + ${CMAKE_SOURCE_DIR}/libobs + ${FFMPEG_INCLUDE_DIRS} + ) + +set(media-playback_HEADERS + media-playback/decode.h + media-playback/media.h + ) +set(media-playback_SOURCES + media-playback/decode.c + media-playback/media.c + ) + +add_library(media-playback STATIC + ${media-playback_HEADERS} + ${media-playback_SOURCES} + ) + +if(NOT MSVC) + target_compile_options(media-playback + PUBLIC + -mmmx + -msse + -msse2) +endif() + +target_include_directories(media-playback + PUBLIC . + ) + +if(NOT MSVC) + if(NOT MINGW) + target_compile_options(media-playback PRIVATE -fPIC) + endif() +endif() + +target_link_libraries(media-playback + ${FFMPEG_LIBRARIES} + ) diff --git a/deps/media-playback/media-playback/closest-format.h b/deps/media-playback/media-playback/closest-format.h new file mode 100644 index 0000000..4a0c25e --- /dev/null +++ b/deps/media-playback/media-playback/closest-format.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 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 + +static enum AVPixelFormat closest_format(enum AVPixelFormat fmt) +{ + switch (fmt) { + case AV_PIX_FMT_YUYV422: + return AV_PIX_FMT_YUYV422; + + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_UYVY422: + case AV_PIX_FMT_YUV422P16LE: + case AV_PIX_FMT_YUV422P16BE: + case AV_PIX_FMT_YUV422P10BE: + case AV_PIX_FMT_YUV422P10LE: + case AV_PIX_FMT_YUV422P9BE: + case AV_PIX_FMT_YUV422P9LE: + case AV_PIX_FMT_YVYU422: + case AV_PIX_FMT_YUV422P12BE: + case AV_PIX_FMT_YUV422P12LE: + case AV_PIX_FMT_YUV422P14BE: + case AV_PIX_FMT_YUV422P14LE: + return AV_PIX_FMT_UYVY422; + + case AV_PIX_FMT_NV12: + case AV_PIX_FMT_NV21: + return AV_PIX_FMT_NV12; + + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUV411P: + case AV_PIX_FMT_UYYVYY411: + case AV_PIX_FMT_YUV410P: + case AV_PIX_FMT_YUV420P16LE: + case AV_PIX_FMT_YUV420P16BE: + case AV_PIX_FMT_YUV420P9BE: + case AV_PIX_FMT_YUV420P9LE: + case AV_PIX_FMT_YUV420P10BE: + case AV_PIX_FMT_YUV420P10LE: + case AV_PIX_FMT_YUV420P12BE: + case AV_PIX_FMT_YUV420P12LE: + case AV_PIX_FMT_YUV420P14BE: + case AV_PIX_FMT_YUV420P14LE: + return AV_PIX_FMT_YUV420P; + + case AV_PIX_FMT_RGBA: + case AV_PIX_FMT_BGRA: + case AV_PIX_FMT_BGR0: + return fmt; + + default: + break; + } + + return AV_PIX_FMT_BGRA; +} diff --git a/deps/media-playback/media-playback/decode.c b/deps/media-playback/media-playback/decode.c new file mode 100644 index 0000000..bd8f1ef --- /dev/null +++ b/deps/media-playback/media-playback/decode.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2017 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 "decode.h" +#include "media.h" + +static AVCodec *find_hardware_decoder(enum AVCodecID id) +{ + AVHWAccel *hwa = av_hwaccel_next(NULL); + AVCodec *c = NULL; + + while (hwa) { + if (hwa->id == id) { + if (hwa->pix_fmt == AV_PIX_FMT_VDA_VLD || + hwa->pix_fmt == AV_PIX_FMT_DXVA2_VLD || + hwa->pix_fmt == AV_PIX_FMT_VAAPI_VLD) { + c = avcodec_find_decoder_by_name(hwa->name); + if (c) + break; + } + } + + hwa = av_hwaccel_next(hwa); + } + + return c; +} + +static int mp_open_codec(struct mp_decode *d) +{ + AVCodecContext *c; + int ret; + +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + c = avcodec_alloc_context3(d->codec); + if (!c) { + blog(LOG_WARNING, "MP: Failed to allocate context"); + return -1; + } + + ret = avcodec_parameters_to_context(c, d->stream->codecpar); + if (ret < 0) + goto fail; +#else + c = d->stream->codec; +#endif + + if (c->thread_count == 1 && + c->codec_id != AV_CODEC_ID_PNG && + c->codec_id != AV_CODEC_ID_TIFF && + c->codec_id != AV_CODEC_ID_JPEG2000 && + c->codec_id != AV_CODEC_ID_MPEG4 && + c->codec_id != AV_CODEC_ID_WEBP) + c->thread_count = 0; + + ret = avcodec_open2(c, d->codec, NULL); + if (ret < 0) + goto fail; + + d->decoder = c; + return ret; + +fail: + avcodec_close(c); +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + av_free(d->decoder); +#endif + return ret; +} + +bool mp_decode_init(mp_media_t *m, enum AVMediaType type, bool hw) +{ + struct mp_decode *d = type == AVMEDIA_TYPE_VIDEO ? &m->v : &m->a; + enum AVCodecID id; + AVStream *stream; + int ret; + + memset(d, 0, sizeof(*d)); + d->m = m; + d->audio = type == AVMEDIA_TYPE_AUDIO; + + ret = av_find_best_stream(m->fmt, type, -1, -1, NULL, 0); + if (ret < 0) + return false; + stream = d->stream = m->fmt->streams[ret]; + +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + id = stream->codecpar->codec_id; +#else + id = stream->codec->codec_id; +#endif + + if (hw) { + d->codec = find_hardware_decoder(id); + if (d->codec) { + ret = mp_open_codec(d); + if (ret < 0) + d->codec = NULL; + } + } + + if (!d->codec) { + if (id == AV_CODEC_ID_VP8) + d->codec = avcodec_find_decoder_by_name("libvpx"); + else if (id == AV_CODEC_ID_VP9) + d->codec = avcodec_find_decoder_by_name("libvpx-vp9"); + + if (!d->codec) + d->codec = avcodec_find_decoder(id); + if (!d->codec) { + blog(LOG_WARNING, "MP: Failed to find %s codec", + av_get_media_type_string(type)); + return false; + } + + ret = mp_open_codec(d); + if (ret < 0) { + blog(LOG_WARNING, "MP: Failed to open %s decoder: %s", + av_get_media_type_string(type), + av_err2str(ret)); + return false; + } + } + + d->frame = av_frame_alloc(); + if (!d->frame) { + blog(LOG_WARNING, "MP: Failed to allocate %s frame", + av_get_media_type_string(type)); + return false; + } + + if (d->codec->capabilities & CODEC_CAP_TRUNCATED) + d->decoder->flags |= CODEC_FLAG_TRUNCATED; + return true; +} + +void mp_decode_clear_packets(struct mp_decode *d) +{ + if (d->packet_pending) { + av_packet_unref(&d->orig_pkt); + d->packet_pending = false; + } + + while (d->packets.size) { + AVPacket pkt; + circlebuf_pop_front(&d->packets, &pkt, sizeof(pkt)); + av_packet_unref(&pkt); + } +} + +void mp_decode_free(struct mp_decode *d) +{ + mp_decode_clear_packets(d); + circlebuf_free(&d->packets); + + if (d->decoder) { + avcodec_close(d->decoder); +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + av_free(d->decoder); +#endif + } + + if (d->frame) + av_free(d->frame); + + memset(d, 0, sizeof(*d)); +} + +void mp_decode_push_packet(struct mp_decode *decode, AVPacket *packet) +{ + circlebuf_push_back(&decode->packets, packet, sizeof(*packet)); +} + +static inline int64_t get_estimated_duration(struct mp_decode *d, + int64_t last_pts) +{ + if (last_pts) + return d->frame_pts - last_pts; + + if (d->audio) { + return av_rescale_q(d->frame->nb_samples, + (AVRational){1, d->frame->sample_rate}, + (AVRational){1, 1000000000}); + } else { + if (d->last_duration) + return d->last_duration; + + return av_rescale_q(d->decoder->time_base.num, + d->decoder->time_base, + (AVRational){1, 1000000000}); + } +} + +static int decode_packet(struct mp_decode *d, int *got_frame) +{ + int ret; + *got_frame = 0; + +#ifdef USE_NEW_FFMPEG_DECODE_API + ret = avcodec_receive_frame(d->decoder, d->frame); + if (ret != 0 && ret != AVERROR(EAGAIN)) { + if (ret == AVERROR_EOF) + ret = 0; + return ret; + } + + if (ret != 0) { + ret = avcodec_send_packet(d->decoder, &d->pkt); + if (ret != 0 && ret != AVERROR(EAGAIN)) { + if (ret == AVERROR_EOF) + ret = 0; + return ret; + } + + ret = avcodec_receive_frame(d->decoder, d->frame); + if (ret != 0 && ret != AVERROR(EAGAIN)) { + if (ret == AVERROR_EOF) + ret = 0; + return ret; + } + + *got_frame = (ret == 0); + ret = d->pkt.size; + } else { + ret = 0; + *got_frame = 1; + } + +#else + if (d->audio) { + ret = avcodec_decode_audio4(d->decoder, + d->frame, got_frame, &d->pkt); + } else { + ret = avcodec_decode_video2(d->decoder, + d->frame, got_frame, &d->pkt); + } +#endif + return ret; +} + +bool mp_decode_next(struct mp_decode *d) +{ + bool eof = d->m->eof; + int got_frame; + int ret; + + d->frame_ready = false; + + if (!eof && !d->packets.size) + return true; + + while (!d->frame_ready) { + if (!d->packet_pending) { + if (!d->packets.size) { + if (eof) { + d->pkt.data = NULL; + d->pkt.size = 0; + } else { + return true; + } + } else { + circlebuf_pop_front(&d->packets, &d->orig_pkt, + sizeof(d->orig_pkt)); + d->pkt = d->orig_pkt; + d->packet_pending = true; + } + } + + ret = decode_packet(d, &got_frame); + + if (!got_frame && ret == 0) { + d->eof = true; + return true; + } + if (ret < 0) { +#ifdef DETAILED_DEBUG_INFO + blog(LOG_DEBUG, "MP: decode failed: %s", + av_err2str(ret)); +#endif + + if (d->packet_pending) { + av_packet_unref(&d->orig_pkt); + av_init_packet(&d->orig_pkt); + av_init_packet(&d->pkt); + d->packet_pending = false; + } + return true; + } + + d->frame_ready = !!got_frame; + + if (d->packet_pending) { + if (d->pkt.size) { + d->pkt.data += ret; + d->pkt.size -= ret; + } + + if (d->pkt.size <= 0) { + av_packet_unref(&d->orig_pkt); + av_init_packet(&d->orig_pkt); + av_init_packet(&d->pkt); + d->packet_pending = false; + } + } + } + + if (d->frame_ready) { + int64_t last_pts = d->frame_pts; + + if (d->frame->best_effort_timestamp == AV_NOPTS_VALUE) + d->frame_pts = d->next_pts; + else + d->frame_pts = av_rescale_q( + d->frame->best_effort_timestamp, + d->stream->time_base, + (AVRational){1, 1000000000}); + + int64_t duration = d->frame->pkt_duration; + if (!duration) + duration = get_estimated_duration(d, last_pts); + else + duration = av_rescale_q(duration, + d->stream->time_base, + (AVRational){1, 1000000000}); + + d->last_duration = duration; + d->next_pts = d->frame_pts + duration; + } + + return true; +} + +void mp_decode_flush(struct mp_decode *d) +{ + avcodec_flush_buffers(d->decoder); + mp_decode_clear_packets(d); + d->eof = false; + d->frame_pts = 0; + d->frame_ready = false; +} diff --git a/deps/media-playback/media-playback/decode.h b/deps/media-playback/media-playback/decode.h new file mode 100644 index 0000000..78a7842 --- /dev/null +++ b/deps/media-playback/media-playback/decode.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#pragma warning(disable : 4204) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +struct mp_media; + +struct mp_decode { + struct mp_media *m; + AVStream *stream; + bool audio; + + AVCodecContext *decoder; + AVCodec *codec; + + int64_t last_duration; + int64_t frame_pts; + int64_t next_pts; + AVFrame *frame; + bool got_first_keyframe; + bool frame_ready; + bool eof; + + AVPacket orig_pkt; + AVPacket pkt; + bool packet_pending; + struct circlebuf packets; +}; + +extern bool mp_decode_init(struct mp_media *media, enum AVMediaType type, + bool hw); +extern void mp_decode_free(struct mp_decode *decode); + +extern void mp_decode_clear_packets(struct mp_decode *decode); + +extern void mp_decode_push_packet(struct mp_decode *decode, AVPacket *pkt); +extern bool mp_decode_next(struct mp_decode *decode); +extern void mp_decode_flush(struct mp_decode *decode); + +#ifdef __cplusplus +} +#endif diff --git a/deps/media-playback/media-playback/media.c b/deps/media-playback/media-playback/media.c new file mode 100644 index 0000000..e6922b0 --- /dev/null +++ b/deps/media-playback/media-playback/media.c @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2017 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 +#include + +#include + +#include "media.h" +#include "closest-format.h" + +#include +#include + +static int64_t base_sys_ts = 0; + +static inline enum video_format convert_pixel_format(int f) +{ + switch (f) { + case AV_PIX_FMT_NONE: return VIDEO_FORMAT_NONE; + case AV_PIX_FMT_YUV420P: return VIDEO_FORMAT_I420; + case AV_PIX_FMT_NV12: return VIDEO_FORMAT_NV12; + case AV_PIX_FMT_YUYV422: return VIDEO_FORMAT_YUY2; + case AV_PIX_FMT_UYVY422: return VIDEO_FORMAT_UYVY; + case AV_PIX_FMT_RGBA: return VIDEO_FORMAT_RGBA; + case AV_PIX_FMT_BGRA: return VIDEO_FORMAT_BGRA; + case AV_PIX_FMT_BGR0: return VIDEO_FORMAT_BGRX; + default:; + } + + return VIDEO_FORMAT_NONE; +} + +static inline enum audio_format convert_sample_format(int f) +{ + switch (f) { + case AV_SAMPLE_FMT_U8: return AUDIO_FORMAT_U8BIT; + case AV_SAMPLE_FMT_S16: return AUDIO_FORMAT_16BIT; + case AV_SAMPLE_FMT_S32: return AUDIO_FORMAT_32BIT; + case AV_SAMPLE_FMT_FLT: return AUDIO_FORMAT_FLOAT; + case AV_SAMPLE_FMT_U8P: return AUDIO_FORMAT_U8BIT_PLANAR; + case AV_SAMPLE_FMT_S16P: return AUDIO_FORMAT_16BIT_PLANAR; + case AV_SAMPLE_FMT_S32P: return AUDIO_FORMAT_32BIT_PLANAR; + case AV_SAMPLE_FMT_FLTP: return AUDIO_FORMAT_FLOAT_PLANAR; + default:; + } + + return AUDIO_FORMAT_UNKNOWN; +} + +static inline enum video_colorspace convert_color_space(enum AVColorSpace s) +{ + return s == AVCOL_SPC_BT709 ? VIDEO_CS_709 : VIDEO_CS_DEFAULT; +} + +static inline enum video_range_type convert_color_range(enum AVColorRange r) +{ + return r == AVCOL_RANGE_JPEG ? VIDEO_RANGE_FULL : VIDEO_RANGE_DEFAULT; +} + +static inline struct mp_decode *get_packet_decoder(mp_media_t *media, + AVPacket *pkt) +{ + if (media->has_audio && pkt->stream_index == media->a.stream->index) + return &media->a; + if (media->has_video && pkt->stream_index == media->v.stream->index) + return &media->v; + + return NULL; +} + +static int mp_media_next_packet(mp_media_t *media) +{ + AVPacket new_pkt; + AVPacket pkt; + av_init_packet(&pkt); + new_pkt = pkt; + + int ret = av_read_frame(media->fmt, &pkt); + if (ret < 0) { + if (ret != AVERROR_EOF) + blog(LOG_WARNING, "MP: av_read_frame failed: %s (%d)", + av_err2str(ret), ret); + return ret; + } + + struct mp_decode *d = get_packet_decoder(media, &pkt); + if (d && pkt.size) { + av_packet_ref(&new_pkt, &pkt); + mp_decode_push_packet(d, &new_pkt); + } + + av_packet_unref(&pkt); + return ret; +} + +static inline bool mp_media_ready_to_start(mp_media_t *m) +{ + if (m->has_audio && !m->a.eof && !m->a.frame_ready) + return false; + if (m->has_video && !m->v.eof && !m->v.frame_ready) + return false; + return true; +} + +static inline bool mp_decode_frame(struct mp_decode *d) +{ + return d->frame_ready || mp_decode_next(d); +} + +static inline int get_sws_colorspace(enum AVColorSpace cs) +{ + switch (cs) { + case AVCOL_SPC_BT709: + return SWS_CS_ITU709; + case AVCOL_SPC_FCC: + return SWS_CS_FCC; + case AVCOL_SPC_SMPTE170M: + return SWS_CS_SMPTE170M; + case AVCOL_SPC_SMPTE240M: + return SWS_CS_SMPTE240M; + default: + break; + } + + return SWS_CS_ITU601; +} + +static inline int get_sws_range(enum AVColorRange r) +{ + return r == AVCOL_RANGE_JPEG ? 1 : 0; +} + +#define FIXED_1_0 (1<<16) + +static bool mp_media_init_scaling(mp_media_t *m) +{ + int space = get_sws_colorspace(m->v.decoder->colorspace); + int range = get_sws_range(m->v.decoder->color_range); + const int *coeff = sws_getCoefficients(space); + + m->swscale = sws_getCachedContext(NULL, + m->v.decoder->width, m->v.decoder->height, + m->v.decoder->pix_fmt, + m->v.decoder->width, m->v.decoder->height, + m->scale_format, + SWS_FAST_BILINEAR, NULL, NULL, NULL); + if (!m->swscale) { + blog(LOG_WARNING, "MP: Failed to initialize scaler"); + return false; + } + + sws_setColorspaceDetails(m->swscale, coeff, range, coeff, range, 0, + FIXED_1_0, FIXED_1_0); + + int ret = av_image_alloc(m->scale_pic, m->scale_linesizes, + m->v.decoder->width, m->v.decoder->height, + m->scale_format, 1); + if (ret < 0) { + blog(LOG_WARNING, "MP: Failed to create scale pic data"); + return false; + } + + return true; +} + +static bool mp_media_prepare_frames(mp_media_t *m) +{ + while (!mp_media_ready_to_start(m)) { + if (!m->eof) { + int ret = mp_media_next_packet(m); + if (ret == AVERROR_EOF) + m->eof = true; + else if (ret < 0) + return false; + } + + if (m->has_video && !mp_decode_frame(&m->v)) + return false; + if (m->has_audio && !mp_decode_frame(&m->a)) + return false; + } + + if (m->has_video && m->v.frame_ready && !m->swscale) { + m->scale_format = closest_format(m->v.frame->format); + if (m->scale_format != m->v.frame->format) { + if (!mp_media_init_scaling(m)) { + return false; + } + } + } + + return true; +} + +static inline int64_t mp_media_get_next_min_pts(mp_media_t *m) +{ + int64_t min_next_ns = 0x7FFFFFFFFFFFFFFFLL; + + if (m->has_video && m->v.frame_ready) { + if (m->v.frame_pts < min_next_ns) + min_next_ns = m->v.frame_pts; + } + if (m->has_audio && m->a.frame_ready) { + if (m->a.frame_pts < min_next_ns) + min_next_ns = m->a.frame_pts; + } + + return min_next_ns; +} + +static inline int64_t mp_media_get_base_pts(mp_media_t *m) +{ + int64_t base_ts = 0; + + if (m->has_video && m->v.next_pts > base_ts) + base_ts = m->v.next_pts; + if (m->has_audio && m->a.next_pts > base_ts) + base_ts = m->a.next_pts; + + return base_ts; +} + +static inline bool mp_media_can_play_frame(mp_media_t *m, + struct mp_decode *d) +{ + return d->frame_ready && d->frame_pts <= m->next_pts_ns; +} + +static void mp_media_next_audio(mp_media_t *m) +{ + struct mp_decode *d = &m->a; + struct obs_source_audio audio = {0}; + AVFrame *f = d->frame; + + if (!mp_media_can_play_frame(m, d)) + return; + + d->frame_ready = false; + if (!m->a_cb) + return; + + for (size_t i = 0; i < MAX_AV_PLANES; i++) + audio.data[i] = f->data[i]; + + audio.samples_per_sec = f->sample_rate; + audio.speakers = (enum speaker_layout)f->channels; + audio.format = convert_sample_format(f->format); + audio.frames = f->nb_samples; + audio.timestamp = m->base_ts + d->frame_pts - m->start_ts + + m->play_sys_ts - base_sys_ts; + + if (audio.format == AUDIO_FORMAT_UNKNOWN) + return; + + m->a_cb(m->opaque, &audio); +} + +static void mp_media_next_video(mp_media_t *m, bool preload) +{ + struct mp_decode *d = &m->v; + struct obs_source_frame *frame = &m->obsframe; + enum video_format new_format; + enum video_colorspace new_space; + enum video_range_type new_range; + AVFrame *f = d->frame; + + if (!preload) { + if (!mp_media_can_play_frame(m, d)) + return; + + d->frame_ready = false; + + if (!m->v_cb) + return; + } else if (!d->frame_ready) { + return; + } + + bool flip = false; + if (m->swscale) { + int ret = sws_scale(m->swscale, + (const uint8_t *const *)f->data, f->linesize, + 0, f->height, + m->scale_pic, m->scale_linesizes); + if (ret < 0) + return; + + flip = m->scale_linesizes[0] < 0 && m->scale_linesizes[1] == 0; + for (size_t i = 0; i < 4; i++) { + frame->data[i] = m->scale_pic[i]; + frame->linesize[i] = abs(m->scale_linesizes[i]); + } + + } else { + flip = f->linesize[0] < 0 && f->linesize[1] == 0; + + for (size_t i = 0; i < MAX_AV_PLANES; i++) { + frame->data[i] = f->data[i]; + frame->linesize[i] = abs(f->linesize[i]); + } + } + + if (flip) + frame->data[0] -= frame->linesize[0] * (f->height - 1); + + new_format = convert_pixel_format(m->scale_format); + new_space = convert_color_space(f->colorspace); + new_range = m->force_range == VIDEO_RANGE_DEFAULT + ? convert_color_range(f->color_range) + : m->force_range; + + if (new_format != frame->format || + new_space != m->cur_space || + new_range != m->cur_range) { + bool success; + + frame->format = new_format; + frame->full_range = new_range == VIDEO_RANGE_FULL; + + success = video_format_get_parameters( + new_space, + new_range, + frame->color_matrix, + frame->color_range_min, + frame->color_range_max); + + frame->format = new_format; + m->cur_space = new_space; + m->cur_range = new_range; + + if (!success) { + frame->format = VIDEO_FORMAT_NONE; + return; + } + } + + if (frame->format == VIDEO_FORMAT_NONE) + return; + + frame->timestamp = m->base_ts + d->frame_pts - m->start_ts + + m->play_sys_ts - base_sys_ts; + frame->width = f->width; + frame->height = f->height; + frame->flip = flip; + + if (m->is_network && !d->got_first_keyframe) { + if (!f->key_frame) + return; + + d->got_first_keyframe = true; + } + + if (preload) + m->v_preload_cb(m->opaque, frame); + else + m->v_cb(m->opaque, frame); +} + +static void mp_media_calc_next_ns(mp_media_t *m) +{ + int64_t min_next_ns = mp_media_get_next_min_pts(m); + + int64_t delta = min_next_ns - m->next_pts_ns; +#ifdef _DEBUG + assert(delta >= 0); +#endif + if (delta < 0) + delta = 0; + if (delta > 3000000000) + delta = 0; + + m->next_ns += delta; + m->next_pts_ns = min_next_ns; +} + +static bool mp_media_reset(mp_media_t *m) +{ + AVStream *stream = m->fmt->streams[0]; + int64_t seek_pos; + int seek_flags; + bool stopping; + bool active; + + if (m->fmt->duration == AV_NOPTS_VALUE) { + seek_pos = 0; + seek_flags = AVSEEK_FLAG_FRAME; + } else { + seek_pos = m->fmt->start_time; + seek_flags = AVSEEK_FLAG_BACKWARD; + } + + int64_t seek_target = seek_flags == AVSEEK_FLAG_BACKWARD + ? av_rescale_q(seek_pos, AV_TIME_BASE_Q, stream->time_base) + : seek_pos; + + if (!m->is_network) { + int ret = av_seek_frame(m->fmt, 0, seek_target, seek_flags); + if (ret < 0) { + blog(LOG_WARNING, "MP: Failed to seek: %s", + av_err2str(ret)); + return false; + } + } + + if (m->has_video && !m->is_network) + mp_decode_flush(&m->v); + if (m->has_audio && !m->is_network) + mp_decode_flush(&m->a); + + int64_t next_ts = mp_media_get_base_pts(m); + int64_t offset = next_ts - m->next_pts_ns; + + m->eof = false; + m->base_ts += next_ts; + + pthread_mutex_lock(&m->mutex); + stopping = m->stopping; + active = m->active; + m->stopping = false; + pthread_mutex_unlock(&m->mutex); + + if (!mp_media_prepare_frames(m)) + return false; + + if (active) { + if (!m->play_sys_ts) + m->play_sys_ts = (int64_t)os_gettime_ns(); + m->start_ts = m->next_pts_ns = mp_media_get_next_min_pts(m); + if (m->next_ns) + m->next_ns += offset; + } else { + m->start_ts = m->next_pts_ns = mp_media_get_next_min_pts(m); + m->play_sys_ts = (int64_t)os_gettime_ns(); + m->next_ns = 0; + } + + if (!active && !m->is_network && m->v_preload_cb) + mp_media_next_video(m, true); + if (stopping && m->stop_cb) + m->stop_cb(m->opaque); + return true; +} + +static inline bool mp_media_sleepto(mp_media_t *m) +{ + bool timeout = false; + + if (!m->next_ns) { + m->next_ns = os_gettime_ns(); + } else { + uint64_t t = os_gettime_ns(); + const uint64_t timeout_ns = 200000000; + + if (m->next_ns > t && (m->next_ns - t) > timeout_ns) { + os_sleepto_ns(t + timeout_ns); + timeout = true; + } else { + os_sleepto_ns(m->next_ns); + } + } + + return timeout; +} + +static inline bool mp_media_eof(mp_media_t *m) +{ + bool v_ended = !m->has_video || !m->v.frame_ready; + bool a_ended = !m->has_audio || !m->a.frame_ready; + bool eof = v_ended && a_ended; + + if (eof) { + bool looping; + + pthread_mutex_lock(&m->mutex); + looping = m->looping; + if (!looping) { + m->active = false; + m->stopping = true; + } + pthread_mutex_unlock(&m->mutex); + + mp_media_reset(m); + } + + return eof; +} + +static int interrupt_callback(void *data) +{ + mp_media_t *m = data; + bool stop = false; + uint64_t ts = os_gettime_ns(); + + if ((ts - m->interrupt_poll_ts) > 20000000) { + pthread_mutex_lock(&m->mutex); + stop = m->kill || m->stopping; + pthread_mutex_unlock(&m->mutex); + + m->interrupt_poll_ts = ts; + } + + return stop; +} + +static bool init_avformat(mp_media_t *m) +{ + AVInputFormat *format = NULL; + + if (m->format_name && *m->format_name) { + format = av_find_input_format(m->format_name); + if (!format) + blog(LOG_INFO, "MP: Unable to find input format for " + "'%s'", m->path); + } + + AVDictionary *opts = NULL; + if (m->buffering && m->is_network) + av_dict_set_int(&opts, "buffer_size", m->buffering, 0); + + m->fmt = avformat_alloc_context(); + m->fmt->interrupt_callback.callback = interrupt_callback; + m->fmt->interrupt_callback.opaque = m; + + int ret = avformat_open_input(&m->fmt, m->path, format, + opts ? &opts : NULL); + av_dict_free(&opts); + + if (ret < 0) { + blog(LOG_WARNING, "MP: Failed to open media: '%s'", m->path); + return false; + } + + if (avformat_find_stream_info(m->fmt, NULL) < 0) { + blog(LOG_WARNING, "MP: Failed to find stream info for '%s'", + m->path); + return false; + } + + m->has_video = mp_decode_init(m, AVMEDIA_TYPE_VIDEO, m->hw); + m->has_audio = mp_decode_init(m, AVMEDIA_TYPE_AUDIO, m->hw); + + if (!m->has_video && !m->has_audio) { + blog(LOG_WARNING, "MP: Could not initialize audio or video: " + "'%s'", m->path); + return false; + } + + return true; +} + +static inline bool mp_media_thread(mp_media_t *m) +{ + os_set_thread_name("mp_media_thread"); + + if (!init_avformat(m)) { + return false; + } + if (!mp_media_reset(m)) { + return false; + } + + for (;;) { + bool reset, kill, is_active; + bool timeout = false; + + pthread_mutex_lock(&m->mutex); + is_active = m->active; + pthread_mutex_unlock(&m->mutex); + + if (!is_active) { + if (os_sem_wait(m->sem) < 0) + return false; + } else { + timeout = mp_media_sleepto(m); + } + + pthread_mutex_lock(&m->mutex); + + reset = m->reset; + kill = m->kill; + m->reset = false; + m->kill = false; + + pthread_mutex_unlock(&m->mutex); + + if (kill) { + break; + } + if (reset) { + mp_media_reset(m); + continue; + } + + /* frames are ready */ + if (is_active && !timeout) { + if (m->has_video) + mp_media_next_video(m, false); + if (m->has_audio) + mp_media_next_audio(m); + + if (!mp_media_prepare_frames(m)) + return false; + if (mp_media_eof(m)) + continue; + + mp_media_calc_next_ns(m); + } + } + + return true; +} + +static void *mp_media_thread_start(void *opaque) +{ + mp_media_t *m = opaque; + + if (!mp_media_thread(m)) { + if (m->stop_cb) { + m->stop_cb(m->opaque); + } + } + + return NULL; +} + +static inline bool mp_media_init_internal(mp_media_t *m, + const char *path, + const char *format_name, + bool hw) +{ + if (pthread_mutex_init(&m->mutex, NULL) != 0) { + blog(LOG_WARNING, "MP: Failed to init mutex"); + return false; + } + if (os_sem_init(&m->sem, 0) != 0) { + blog(LOG_WARNING, "MP: Failed to init semaphore"); + return false; + } + + m->path = path ? bstrdup(path) : NULL; + m->format_name = format_name ? bstrdup(format_name) : NULL; + m->hw = hw; + + if (pthread_create(&m->thread, NULL, mp_media_thread_start, m) != 0) { + blog(LOG_WARNING, "MP: Could not create media thread"); + return false; + } + + m->thread_valid = true; + return true; +} + +bool mp_media_init(mp_media_t *media, + const char *path, + const char *format, + int buffering, + void *opaque, + mp_video_cb v_cb, + mp_audio_cb a_cb, + mp_stop_cb stop_cb, + mp_video_cb v_preload_cb, + bool hw_decoding, + enum video_range_type force_range) +{ + memset(media, 0, sizeof(*media)); + pthread_mutex_init_value(&media->mutex); + media->opaque = opaque; + media->v_cb = v_cb; + media->a_cb = a_cb; + media->stop_cb = stop_cb; + media->v_preload_cb = v_preload_cb; + media->force_range = force_range; + media->buffering = buffering; + + if (path && *path) + media->is_network = !!strstr(path, "://"); + + static bool initialized = false; + if (!initialized) { + av_register_all(); + avdevice_register_all(); + avcodec_register_all(); + avformat_network_init(); + initialized = true; + } + + if (!base_sys_ts) + base_sys_ts = (int64_t)os_gettime_ns(); + + if (!mp_media_init_internal(media, path, format, hw_decoding)) { + mp_media_free(media); + return false; + } + + return true; +} + +static void mp_kill_thread(mp_media_t *m) +{ + if (m->thread_valid) { + pthread_mutex_lock(&m->mutex); + m->kill = true; + pthread_mutex_unlock(&m->mutex); + os_sem_post(m->sem); + + pthread_join(m->thread, NULL); + } +} + +void mp_media_free(mp_media_t *media) +{ + if (!media) + return; + + mp_media_stop(media); + mp_kill_thread(media); + mp_decode_free(&media->v); + mp_decode_free(&media->a); + avformat_close_input(&media->fmt); + pthread_mutex_destroy(&media->mutex); + os_sem_destroy(media->sem); + sws_freeContext(media->swscale); + av_freep(&media->scale_pic[0]); + bfree(media->path); + bfree(media->format_name); + memset(media, 0, sizeof(*media)); + pthread_mutex_init_value(&media->mutex); +} + +void mp_media_play(mp_media_t *m, bool loop) +{ + pthread_mutex_lock(&m->mutex); + + if (m->active) + m->reset = true; + + m->looping = loop; + m->active = true; + + pthread_mutex_unlock(&m->mutex); + + os_sem_post(m->sem); +} + +void mp_media_stop(mp_media_t *m) +{ + pthread_mutex_lock(&m->mutex); + if (m->active) { + m->reset = true; + m->active = false; + m->stopping = true; + os_sem_post(m->sem); + } + pthread_mutex_unlock(&m->mutex); +} diff --git a/deps/media-playback/media-playback/media.h b/deps/media-playback/media-playback/media.h new file mode 100644 index 0000000..e7c39c6 --- /dev/null +++ b/deps/media-playback/media-playback/media.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 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 + +#include +#include "decode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#pragma warning(disable : 4204) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +typedef void (*mp_video_cb)(void *opaque, struct obs_source_frame *frame); +typedef void (*mp_audio_cb)(void *opaque, struct obs_source_audio *audio); +typedef void (*mp_stop_cb)(void *opaque); + +struct mp_media { + AVFormatContext *fmt; + + mp_video_cb v_preload_cb; + mp_stop_cb stop_cb; + mp_video_cb v_cb; + mp_audio_cb a_cb; + void *opaque; + + char *path; + char *format_name; + int buffering; + + enum AVPixelFormat scale_format; + struct SwsContext *swscale; + int scale_linesizes[4]; + uint8_t *scale_pic[4]; + + struct mp_decode v; + struct mp_decode a; + bool is_network; + bool has_video; + bool has_audio; + bool is_file; + bool eof; + bool hw; + + struct obs_source_frame obsframe; + enum video_colorspace cur_space; + enum video_range_type cur_range; + enum video_range_type force_range; + + int64_t play_sys_ts; + int64_t next_pts_ns; + uint64_t next_ns; + int64_t start_ts; + int64_t base_ts; + + uint64_t interrupt_poll_ts; + + pthread_mutex_t mutex; + os_sem_t *sem; + bool stopping; + bool looping; + bool active; + bool reset; + bool kill; + + bool thread_valid; + pthread_t thread; +}; + +typedef struct mp_media mp_media_t; + +extern bool mp_media_init(mp_media_t *media, + const char *path, + const char *format, + int buffering, + void *opaque, + mp_video_cb v_cb, + mp_audio_cb a_cb, + mp_stop_cb stop_cb, + mp_video_cb v_preload_cb, + bool hardware_decoding, + enum video_range_type force_range); +extern void mp_media_free(mp_media_t *media); + +extern void mp_media_play(mp_media_t *media, bool loop); +extern void mp_media_stop(mp_media_t *media); + +/* #define DETAILED_DEBUG_INFO */ + +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) +#define USE_NEW_FFMPEG_DECODE_API +#endif + +#ifdef __cplusplus +} +#endif diff --git a/docs/doxygen/obs.png b/docs/doxygen/obs.png index 7cc702a..74e13cf 100644 Binary files a/docs/doxygen/obs.png and b/docs/doxygen/obs.png differ diff --git a/libobs-d3d11/d3d11-shader.cpp b/libobs-d3d11/d3d11-shader.cpp index fc3159f..453ff24 100644 --- a/libobs-d3d11/d3d11-shader.cpp +++ b/libobs-d3d11/d3d11-shader.cpp @@ -149,6 +149,9 @@ void gs_shader::BuildConstantBuffer() continue; } + if (param.arrayCount) + size *= param.arrayCount; + /* checks to see if this constant needs to start at a new * register */ if (size && (constantSize & 15) != 0) { @@ -287,6 +290,8 @@ void gs_shader::UploadParams() void gs_shader_destroy(gs_shader_t *shader) { + if (shader && shader->device->lastVertexShader == shader) + shader->device->lastVertexShader = nullptr; delete shader; } diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 8760a9d..340b26d 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -253,7 +253,7 @@ void gs_device::InitDevice(uint32_t adapterIdx) if (FAILED(hr)) throw UnsupportedHWError("Failed to create device", hr); - blog(LOG_INFO, "D3D11 loaded sucessfully, feature level used: %u", + blog(LOG_INFO, "D3D11 loaded successfully, feature level used: %u", (unsigned int)levelUsed); } @@ -514,7 +514,7 @@ static inline void EnumD3DAdapters( if (FAILED(hr)) continue; - /* ignore microsoft's 'basic' renderer' */ + /* ignore Microsoft's 'basic' renderer' */ if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) continue; @@ -586,7 +586,7 @@ static inline void LogD3DAdapters() if (FAILED(hr)) continue; - /* ignore microsoft's 'basic' renderer' */ + /* ignore Microsoft's 'basic' renderer' */ if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) continue; @@ -608,7 +608,7 @@ int device_create(gs_device_t **p_device, uint32_t adapter) try { blog(LOG_INFO, "---------------------------------"); - blog(LOG_INFO, "Initializing D3D11.."); + blog(LOG_INFO, "Initializing D3D11..."); LogD3DAdapters(); device = new gs_device(adapter); @@ -929,33 +929,40 @@ enum gs_texture_type device_get_texture_type(const gs_texture_t *texture) return texture->type; } -void device_load_vertexbuffer(gs_device_t *device, gs_vertbuffer_t *vertbuffer) +void gs_device::LoadVertexBufferData() { - if (device->curVertexBuffer == vertbuffer) - return; - - device->curVertexBuffer = vertbuffer; - - if (!device->curVertexShader) + if (curVertexBuffer == lastVertexBuffer && + curVertexShader == lastVertexShader) return; vector buffers; vector strides; vector offsets; - if (vertbuffer) { - vertbuffer->MakeBufferList(device->curVertexShader, + if (curVertexBuffer && curVertexShader) { + curVertexBuffer->MakeBufferList(curVertexShader, buffers, strides); } else { - size_t buffersToClear = - device->curVertexShader->NumBuffersExpected(); + size_t buffersToClear = curVertexShader + ? curVertexShader->NumBuffersExpected() : 0; buffers.resize(buffersToClear); strides.resize(buffersToClear); } offsets.resize(buffers.size()); - device->context->IASetVertexBuffers(0, (UINT)buffers.size(), + context->IASetVertexBuffers(0, (UINT)buffers.size(), buffers.data(), strides.data(), offsets.data()); + + lastVertexBuffer = curVertexBuffer; + lastVertexShader = curVertexShader; +} + +void device_load_vertexbuffer(gs_device_t *device, gs_vertbuffer_t *vertbuffer) +{ + if (device->curVertexBuffer == vertbuffer) + return; + + device->curVertexBuffer = vertbuffer; } void device_load_indexbuffer(gs_device_t *device, gs_indexbuffer_t *indexbuffer) @@ -1032,9 +1039,6 @@ void device_load_vertexshader(gs_device_t *device, gs_shader_t *vertshader) return; } - if (curVB) - device_load_vertexbuffer(device, NULL); - shader = vs->shader; layout = vs->layout; constants = vs->constants; @@ -1044,9 +1048,6 @@ void device_load_vertexshader(gs_device_t *device, gs_shader_t *vertshader) device->context->VSSetShader(shader, NULL, 0); device->context->IASetInputLayout(layout); device->context->VSSetConstantBuffers(0, 1, &constants); - - if (vertshader && curVB) - device_load_vertexbuffer(device, curVB); } static inline void clear_textures(gs_device_t *device) @@ -1348,6 +1349,7 @@ void device_draw(gs_device_t *device, enum gs_draw_mode draw_mode, if (effect) gs_effect_update_params(effect); + device->LoadVertexBufferData(); device->UpdateBlendState(); device->UpdateRasterState(); device->UpdateZStencilState(); @@ -1926,6 +1928,8 @@ void gs_samplerstate_destroy(gs_samplerstate_t *samplerstate) void gs_vertexbuffer_destroy(gs_vertbuffer_t *vertbuffer) { + if (vertbuffer && vertbuffer->device->lastVertexBuffer == vertbuffer) + vertbuffer->device->lastVertexBuffer = nullptr; delete vertbuffer; } diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index 1cec188..cf7ad6f 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -789,6 +789,9 @@ struct gs_device { gs_pixel_shader *curPixelShader = nullptr; gs_swap_chain *curSwapChain = nullptr; + gs_vertex_buffer *lastVertexBuffer = nullptr; + gs_vertex_shader *lastVertexShader = nullptr; + bool zstencilStateChanged = true; bool rasterStateChanged = true; bool blendStateChanged = true; @@ -829,6 +832,8 @@ struct gs_device { void UpdateRasterState(); void UpdateBlendState(); + void LoadVertexBufferData(); + inline void CopyTex(ID3D11Texture2D *dst, uint32_t dst_x, uint32_t dst_y, gs_texture_t *src, uint32_t src_x, uint32_t src_y, diff --git a/libobs-opengl/gl-stagesurf.c b/libobs-opengl/gl-stagesurf.c index 68f03c2..aa1637c 100644 --- a/libobs-opengl/gl-stagesurf.c +++ b/libobs-opengl/gl-stagesurf.c @@ -29,7 +29,7 @@ static bool create_pixel_pack_buffer(struct gs_stage_surface *surf) return false; size = surf->width * surf->bytes_per_pixel; - size = (size+3) & 0xFFFFFFFC; /* align width to 4-byte boundry */ + size = (size+3) & 0xFFFFFFFC; /* align width to 4-byte boundary */ size *= surf->height; glBufferData(GL_PIXEL_PACK_BUFFER, size, 0, GL_DYNAMIC_READ); @@ -109,7 +109,7 @@ static bool can_stage(struct gs_stage_surface *dst, struct gs_texture_2d *src) #ifdef __APPLE__ /* Apparently for mac, PBOs won't do an asynchronous transfer unless you use - * FBOs aong with glReadPixels, which is really dumb. */ + * FBOs along with glReadPixels, which is really dumb. */ void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, gs_texture_t *src) { diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index 32b99f0..f71f152 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -207,6 +207,9 @@ int device_create(gs_device_t **p_device, uint32_t adapter) struct gs_device *device = bzalloc(sizeof(struct gs_device)); int errorcode = GS_ERROR_FAIL; + blog(LOG_INFO, "---------------------------------"); + blog(LOG_INFO, "Initializing OpenGL..."); + device->plat = gl_platform_create(device, adapter); if (!device->plat) goto fail; @@ -215,6 +218,8 @@ int device_create(gs_device_t **p_device, uint32_t adapter) errorcode = GS_ERROR_NOT_SUPPORTED; goto fail; } + + blog(LOG_INFO, "OpenGL version: %s", glGetString(GL_VERSION)); gl_enable(GL_CULL_FACE); @@ -1227,17 +1232,21 @@ static inline uint32_t get_target_height(const struct gs_device *device) void device_set_viewport(gs_device_t *device, int x, int y, int width, int height) { - uint32_t base_height; + uint32_t base_height = 0; + int gl_y = 0; /* GL uses bottom-up coordinates for viewports. We want top-down */ if (device->cur_render_target) { base_height = get_target_height(device); - } else { + } else if (device->cur_swap) { uint32_t dw; gl_getclientsize(device->cur_swap, &dw, &base_height); } - glViewport(x, base_height - y - height, width, height); + if (base_height) + gl_y = base_height - y - height; + + glViewport(x, gl_y, width, height); if (!gl_success("glViewport")) blog(LOG_ERROR, "device_set_viewport (GL) failed"); diff --git a/libobs-opengl/gl-windows.c b/libobs-opengl/gl-windows.c index 3527455..a266930 100644 --- a/libobs-opengl/gl-windows.c +++ b/libobs-opengl/gl-windows.c @@ -69,7 +69,7 @@ static inline int get_stencil_format_bits(enum gs_zstencil_format zsformat) } } -/* would use designated initializers but microsoft sort of sucks */ +/* would use designated initializers but Microsoft sort of sucks */ static inline void init_dummy_pixel_format(PIXELFORMATDESCRIPTOR *pfd) { memset(pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); diff --git a/libobs-opengl/gl-x11.c b/libobs-opengl/gl-x11.c index e53279d..058db97 100644 --- a/libobs-opengl/gl-x11.c +++ b/libobs-opengl/gl-x11.c @@ -366,8 +366,6 @@ extern struct gl_platform *gl_platform_create(gs_device_t *device, goto fail_load_gl; } - blog(LOG_INFO, "OpenGL version: %s\n", glGetString(GL_VERSION)); - goto success; fail_make_current: diff --git a/libobs/audio-monitoring/osx/coreaudio-enum-devices.c b/libobs/audio-monitoring/osx/coreaudio-enum-devices.c index 5fd591b..52be3f4 100644 --- a/libobs/audio-monitoring/osx/coreaudio-enum-devices.c +++ b/libobs/audio-monitoring/osx/coreaudio-enum-devices.c @@ -12,8 +12,8 @@ static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size) return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8); } -static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, - void *data, AudioDeviceID id) +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; @@ -21,6 +21,7 @@ static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, char name[1024]; char uid[1024]; OSStatus stat; + bool cont = true; AudioObjectPropertyAddress addr = { kAudioDevicePropertyStreams, @@ -29,16 +30,18 @@ static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, }; /* check to see if it's a mac input device */ - AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size); - if (!size) - return; + if (!allow_inputs) { + AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size); + if (!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; + return true; addr.mSelector = kAudioDevicePropertyDeviceNameCFString; stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name); @@ -55,16 +58,18 @@ static void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, goto fail; } - cb(data, name, uid); + cont = cb(data, name, uid); fail: if (cf_name) CFRelease(cf_name); if (cf_uid) CFRelease(cf_uid); + return cont; } -void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data) +static void enum_audio_devices(obs_enum_audio_device_cb cb, void *data, + bool allow_inputs) { AudioObjectPropertyAddress addr = { kAudioHardwarePropertyDevices, @@ -88,9 +93,104 @@ void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data) stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, ids); if (success(stat, "get data")) { - for (UInt32 i = 0; i < count; i++) - obs_enum_audio_monitoring_device(cb, data, ids[i]); + for (UInt32 i = 0; i < count; i++) { + if (!obs_enum_audio_monitoring_device(cb, data, ids[i], + allow_inputs)) + break; + } } free(ids); } + +void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data) +{ + enum_audio_devices(cb, data, false); +} + +static bool alloc_default_id(void *data, const char *name, const char *id) +{ + char **p_id = data; + UNUSED_PARAMETER(name); + + *p_id = bstrdup(id); + return false; +} + +static void get_default_id(char **p_id) +{ + AudioObjectPropertyAddress addr = { + kAudioHardwarePropertyDefaultSystemOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + if (*p_id) + return; + + OSStatus stat; + AudioDeviceID id = 0; + UInt32 size = sizeof(id); + + stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, + NULL, &size, &id); + if (success(stat, "AudioObjectGetPropertyData")) + obs_enum_audio_monitoring_device(alloc_default_id, p_id, id, + true); + if (!*p_id) + *p_id = bzalloc(1); +} + +struct device_name_info { + const char *id; + char *name; +}; + +static bool enum_device_name(void *data, const char *name, const char *id) +{ + struct device_name_info *info = data; + + if (strcmp(info->id, id) == 0) { + info->name = bstrdup(name); + return false; + } + + return true; +} + +bool devices_match(const char *id1, const char *id2) +{ + struct device_name_info info = {0}; + char *default_id = NULL; + char *name1 = NULL; + char *name2 = NULL; + bool match; + + if (!id1 || !id2) + return false; + + if (strcmp(id1, "default") == 0) { + get_default_id(&default_id); + id1 = default_id; + } + if (strcmp(id2, "default") == 0) { + get_default_id(&default_id); + id2 = default_id; + } + + info.id = id1; + enum_audio_devices(enum_device_name, &info, true); + name1 = info.name; + + info.name = NULL; + info.id = id2; + enum_audio_devices(enum_device_name, &info, true); + name2 = info.name; + + match = name1 && name2 && strcmp(name1, name2) == 0; + bfree(default_id); + bfree(name1); + bfree(name2); + + return match; +} diff --git a/libobs/audio-monitoring/osx/coreaudio-output.c b/libobs/audio-monitoring/osx/coreaudio-output.c index 52ff672..3e2bcd1 100644 --- a/libobs/audio-monitoring/osx/coreaudio-output.c +++ b/libobs/audio-monitoring/osx/coreaudio-output.c @@ -27,6 +27,7 @@ struct audio_monitor { volatile bool active; bool paused; + bool ignore; }; static inline bool fill_buffer(struct audio_monitor *monitor) @@ -137,7 +138,10 @@ static void buffer_audio(void *data, AudioQueueRef aq, AudioQueueBufferRef buf) UNUSED_PARAMETER(aq); } -static bool audio_monitor_init(struct audio_monitor *monitor) +extern bool devices_match(const char *id1, const char *id2); + +static bool audio_monitor_init(struct audio_monitor *monitor, + obs_source_t *source) { const struct audio_output_info *info = audio_output_get_info( obs->audio.audio); @@ -156,6 +160,8 @@ static bool audio_monitor_init(struct audio_monitor *monitor) .mBitsPerChannel = sizeof(float) * 8 }; + monitor->source = source; + monitor->channels = channels; monitor->buffer_size = channels * sizeof(float) * info->samples_per_sec / 100 * 3; @@ -163,14 +169,26 @@ static bool audio_monitor_init(struct audio_monitor *monitor) pthread_mutex_init_value(&monitor->mutex); - stat = AudioQueueNewOutput(&desc, buffer_audio, monitor, NULL, NULL, 0, - &monitor->queue); - if (!success(stat, "AudioStreamBasicDescription")) { + const char *uid = obs->audio.monitoring_device_id; + if (!uid || !*uid) { return false; } - const char *uid = obs->audio.monitoring_device_id; - if (!uid || !*uid) { + if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) { + obs_data_t *s = obs_source_get_settings(source); + const char *s_dev_id = obs_data_get_string(s, "device_id"); + bool match = devices_match(s_dev_id, uid); + obs_data_release(s); + + if (match) { + monitor->ignore = true; + return true; + } + } + + stat = AudioQueueNewOutput(&desc, buffer_audio, monitor, NULL, NULL, 0, + &monitor->queue); + if (!success(stat, "AudioStreamBasicDescription")) { return false; } @@ -266,19 +284,20 @@ static void audio_monitor_free(struct audio_monitor *monitor) pthread_mutex_destroy(&monitor->mutex); } -static void audio_monitor_init_final(struct audio_monitor *monitor, - obs_source_t *source) +static void audio_monitor_init_final(struct audio_monitor *monitor) { - monitor->source = source; - obs_source_add_audio_capture_callback(source, on_audio_playback, - monitor); + if (monitor->ignore) + return; + + obs_source_add_audio_capture_callback(monitor->source, + on_audio_playback, monitor); } struct audio_monitor *audio_monitor_create(obs_source_t *source) { struct audio_monitor *monitor = bzalloc(sizeof(*monitor)); - if (!audio_monitor_init(monitor)) { + if (!audio_monitor_init(monitor, source)) { goto fail; } @@ -286,7 +305,7 @@ struct audio_monitor *audio_monitor_create(obs_source_t *source) da_push_back(obs->audio.monitors, &monitor); pthread_mutex_unlock(&obs->audio.monitoring_mutex); - audio_monitor_init_final(monitor, source); + audio_monitor_init_final(monitor); return monitor; fail: @@ -303,9 +322,9 @@ void audio_monitor_reset(struct audio_monitor *monitor) audio_monitor_free(monitor); memset(monitor, 0, sizeof(*monitor)); - success = audio_monitor_init(monitor); + success = audio_monitor_init(monitor, source); if (success) - audio_monitor_init_final(monitor, source); + audio_monitor_init_final(monitor); } void audio_monitor_destroy(struct audio_monitor *monitor) diff --git a/libobs/audio-monitoring/win32/wasapi-enum-devices.c b/libobs/audio-monitoring/win32/wasapi-enum-devices.c index 98ed93a..18b8094 100644 --- a/libobs/audio-monitoring/win32/wasapi-enum-devices.c +++ b/libobs/audio-monitoring/win32/wasapi-enum-devices.c @@ -103,3 +103,66 @@ fail: safe_release(enumerator); safe_release(collection); } + +static void get_default_id(char **p_id) +{ + IMMDeviceEnumerator *immde = NULL; + IMMDevice *device = NULL; + WCHAR *w_id = NULL; + HRESULT hr; + + if (*p_id) + return; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + &IID_IMMDeviceEnumerator, &immde); + if (FAILED(hr)) { + goto fail; + } + + hr = immde->lpVtbl->GetDefaultAudioEndpoint(immde, + eRender, eConsole, &device); + if (FAILED(hr)) { + goto fail; + } + + hr = device->lpVtbl->GetId(device, &w_id); + if (FAILED(hr)) { + goto fail; + } + + os_wcs_to_utf8_ptr(w_id, 0, p_id); + +fail: + if (!*p_id) + *p_id = bzalloc(1); + if (immde) + immde->lpVtbl->Release(immde); + if (device) + device->lpVtbl->Release(device); + if (w_id) + CoTaskMemFree(w_id); +} + +bool devices_match(const char *id1, const char *id2) +{ + char *default_id = NULL; + bool match; + + if (!id1 || !id2) + return false; + + if (strcmp(id1, "default") == 0) { + get_default_id(&default_id); + id1 = default_id; + } + if (strcmp(id2, "default") == 0) { + get_default_id(&default_id); + id2 = default_id; + } + + match = strcmp(id1, id2) == 0; + bfree(default_id); + + return match; +} diff --git a/libobs/audio-monitoring/win32/wasapi-output.c b/libobs/audio-monitoring/win32/wasapi-output.c index c1ffd4e..750f9b5 100644 --- a/libobs/audio-monitoring/win32/wasapi-output.c +++ b/libobs/audio-monitoring/win32/wasapi-output.c @@ -35,7 +35,8 @@ struct audio_monitor { audio_resampler_t *resampler; uint32_t sample_rate; uint32_t channels; - bool source_has_video : 1; + bool source_has_video; + bool ignore; int64_t lowest_audio_offset; struct circlebuf delay_buffer; @@ -203,6 +204,9 @@ unlock: static inline void audio_monitor_free(struct audio_monitor *monitor) { + if (monitor->ignore) + return; + if (monitor->source) { obs_source_remove_audio_capture_callback( monitor->source, on_audio_playback, monitor); @@ -235,7 +239,10 @@ static enum speaker_layout convert_speaker_layout(DWORD layout, WORD channels) return (enum speaker_layout)channels; } -static bool audio_monitor_init(struct audio_monitor *monitor) +extern bool devices_match(const char *id1, const char *id2); + +static bool audio_monitor_init(struct audio_monitor *monitor, + obs_source_t *source) { IMMDeviceEnumerator *immde = NULL; WAVEFORMATEX *wfex = NULL; @@ -243,12 +250,26 @@ static bool audio_monitor_init(struct audio_monitor *monitor) UINT32 frames; HRESULT hr; + pthread_mutex_init_value(&monitor->playback_mutex); + + monitor->source = source; + const char *id = obs->audio.monitoring_device_id; if (!id) { return false; } - pthread_mutex_init_value(&monitor->playback_mutex); + if (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) { + obs_data_t *s = obs_source_get_settings(source); + const char *s_dev_id = obs_data_get_string(s, "device_id"); + bool match = devices_match(s_dev_id, id); + obs_data_release(s); + + if (match) { + monitor->ignore = true; + return true; + } + } /* ------------------------------------------ * * Init device */ @@ -352,14 +373,15 @@ fail: return success; } -static void audio_monitor_init_final(struct audio_monitor *monitor, - obs_source_t *source) +static void audio_monitor_init_final(struct audio_monitor *monitor) { - monitor->source = source; + if (monitor->ignore) + return; + monitor->source_has_video = - (source->info.output_flags & OBS_SOURCE_VIDEO) != 0; - obs_source_add_audio_capture_callback(source, on_audio_playback, - monitor); + (monitor->source->info.output_flags & OBS_SOURCE_VIDEO) != 0; + obs_source_add_audio_capture_callback(monitor->source, + on_audio_playback, monitor); } struct audio_monitor *audio_monitor_create(obs_source_t *source) @@ -367,7 +389,7 @@ struct audio_monitor *audio_monitor_create(obs_source_t *source) struct audio_monitor monitor = {0}; struct audio_monitor *out; - if (!audio_monitor_init(&monitor)) { + if (!audio_monitor_init(&monitor, source)) { goto fail; } @@ -377,7 +399,7 @@ struct audio_monitor *audio_monitor_create(obs_source_t *source) da_push_back(obs->audio.monitors, &out); pthread_mutex_unlock(&obs->audio.monitoring_mutex); - audio_monitor_init_final(out, source); + audio_monitor_init_final(out); return out; fail: @@ -391,14 +413,14 @@ void audio_monitor_reset(struct audio_monitor *monitor) bool success; pthread_mutex_lock(&monitor->playback_mutex); - success = audio_monitor_init(&new_monitor); + success = audio_monitor_init(&new_monitor, monitor->source); pthread_mutex_unlock(&monitor->playback_mutex); if (success) { obs_source_t *source = monitor->source; audio_monitor_free(monitor); *monitor = new_monitor; - audio_monitor_init_final(monitor, source); + audio_monitor_init_final(monitor); } else { audio_monitor_free(&new_monitor); } diff --git a/libobs/callback/calldata.h b/libobs/callback/calldata.h index 0aa8e5b..8f81eb4 100644 --- a/libobs/callback/calldata.h +++ b/libobs/callback/calldata.h @@ -89,7 +89,7 @@ static inline void calldata_clear(struct calldata *data) } /* ------------------------------------------------------------------------- */ -/* NOTE: 'get' functions return true only if paramter exists, and is the +/* NOTE: 'get' functions return true only if parameter exists, and is the * same type. They return false otherwise. */ static inline bool calldata_get_int(const calldata_t *data, const char *name, diff --git a/libobs/callback/decl.c b/libobs/callback/decl.c index b0d3d13..a7e2c94 100644 --- a/libobs/callback/decl.c +++ b/libobs/callback/decl.c @@ -109,7 +109,7 @@ static int parse_param(struct cf_parser *cfp, struct decl_info *decl) int code; struct decl_param param = {0}; - /* get stprage specifiers */ + /* get storage specifiers */ code = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, ","); if (code != PARSE_SUCCESS) return code; @@ -120,7 +120,7 @@ static int parse_param(struct cf_parser *cfp, struct decl_info *decl) return code; } - /* parameters not marked with specifers are input parameters */ + /* parameters not marked with specifiers are input parameters */ if (param.flags == 0) param.flags = CALL_PARAM_IN; @@ -198,7 +198,7 @@ bool parse_decl_string(struct decl_info *decl, const char *decl_string) ret_param.flags = CALL_PARAM_OUT; cf_parser_init(&cfp); - if (!cf_parser_parse(&cfp, decl_string, "declaraion")) + if (!cf_parser_parse(&cfp, decl_string, "declaration")) goto fail; code = cf_get_name_ref(&cfp, &ret_type, "return type", NULL); diff --git a/libobs/data/format_conversion.effect b/libobs/data/format_conversion.effect index 360c245..6af8b08 100644 --- a/libobs/data/format_conversion.effect +++ b/libobs/data/format_conversion.effect @@ -37,6 +37,11 @@ uniform float input_height_i; uniform float input_width_i_d2; uniform float input_height_i_d2; +uniform int int_width; +uniform int int_input_width; +uniform int int_u_plane_offset; +uniform int int_v_plane_offset; + uniform texture2d image; sampler_state def_sampler { @@ -235,6 +240,13 @@ float4 PSPlanar444(VertInOut vert_in) : TARGET return out_val[2]; } +float GetIntOffsetColor(int offset) +{ + return image.Load(int3(offset % int_input_width, + offset / int_input_width, + 0)).r; +} + float4 PSPacked422_Reverse(VertInOut vert_in, int u_pos, int v_pos, int y0_pos, int y1_pos) : TARGET { @@ -250,59 +262,37 @@ float4 PSPacked422_Reverse(VertInOut vert_in, int u_pos, int v_pos, texel[u_pos], texel[v_pos], 1.0); } -float GetOffsetColor(float offset) -{ - float2 uv; - - offset += PRECISION_OFFSET; - uv.x = floor(fmod(offset, input_width)) * input_width_i; - uv.y = floor(offset * input_width_i) * input_height_i; - - uv.xy += float2(input_width_i_d2, input_height_i_d2); - - return image.Sample(def_sampler, uv).r; -} - float4 PSPlanar420_Reverse(VertInOut vert_in) : TARGET { - float x = vert_in.uv.x; - float y = vert_in.uv.y; - float x_offset = floor(x * width + PRECISION_OFFSET); - float y_offset = floor(y * height + PRECISION_OFFSET); + int x = int(vert_in.uv.x * width + PRECISION_OFFSET); + int y = int(vert_in.uv.y * height + PRECISION_OFFSET); - float lum_offset = y_offset * width + x_offset + PRECISION_OFFSET; - lum_offset = floor(lum_offset); - - float ch_offset = floor(y_offset * 0.5 + PRECISION_OFFSET) * width_d2 + - (x_offset * 0.5) + PRECISION_OFFSET; - ch_offset = floor(ch_offset); + int lum_offset = y * int_width + x; + int chroma_offset = (y / 2) * (int_width / 2) + x / 2; + int chroma1 = int_u_plane_offset + chroma_offset; + int chroma2 = int_v_plane_offset + chroma_offset; return float4( - GetOffsetColor(lum_offset), - GetOffsetColor(u_plane_offset + ch_offset), - GetOffsetColor(v_plane_offset + ch_offset), + GetIntOffsetColor(lum_offset), + GetIntOffsetColor(chroma1), + GetIntOffsetColor(chroma2), 1.0 ); } float4 PSNV12_Reverse(VertInOut vert_in) : TARGET { - float x = vert_in.uv.x; - float y = vert_in.uv.y; - float x_offset = floor(x * width + PRECISION_OFFSET); - float y_offset = floor(y * height + PRECISION_OFFSET); + int x = int(vert_in.uv.x * width + PRECISION_OFFSET); + int y = int(vert_in.uv.y * height + PRECISION_OFFSET); - float lum_offset = y_offset * width + x_offset + PRECISION_OFFSET; - lum_offset = floor(lum_offset); - - float ch_offset = floor(y_offset * 0.5 + PRECISION_OFFSET) * width_d2 + - (x_offset * 0.5); - ch_offset = floor(ch_offset * 2.0 + PRECISION_OFFSET); + int lum_offset = y * int_width + x; + int chroma_offset = (y / 2) * (int_width / 2) + x / 2; + int chroma = int_u_plane_offset + chroma_offset * 2; return float4( - GetOffsetColor(lum_offset), - GetOffsetColor(u_plane_offset + ch_offset), - GetOffsetColor(u_plane_offset + ch_offset + 1.0), + GetIntOffsetColor(lum_offset), + GetIntOffsetColor(chroma), + GetIntOffsetColor(chroma + 1), 1.0 ); } diff --git a/libobs/data/solid.effect b/libobs/data/solid.effect index 18a210f..5758e92 100644 --- a/libobs/data/solid.effect +++ b/libobs/data/solid.effect @@ -1,6 +1,10 @@ uniform float4x4 ViewProj; uniform float4 color = {1.0, 1.0, 1.0, 1.0}; +uniform float4 randomvals1; +uniform float4 randomvals2; +uniform float4 randomvals3; + struct SolidVertInOut { float4 pos : POSITION; }; @@ -17,6 +21,19 @@ float4 PSSolid(SolidVertInOut vert_in) : TARGET return color; } +float rand(float4 pos, float4 rand_vals) +{ + return 0.5 + 0.5 * frac(sin(dot(pos.xy, float2(rand_vals.x, rand_vals.y))) * rand_vals.z); +} + +float4 PSRandom(SolidVertInOut vert_in) : TARGET +{ + return float4(rand(vert_in.pos, randomvals1), + rand(vert_in.pos, randomvals2), + rand(vert_in.pos, randomvals3), + 1.0); +} + struct SolidColoredVertInOut { float4 pos : POSITION; float4 color : COLOR; @@ -52,3 +69,12 @@ technique SolidColored pixel_shader = PSSolidColored(vert_in); } } + +technique Random +{ + pass + { + vertex_shader = VSSolid(vert_in); + pixel_shader = PSRandom(vert_in); + } +} diff --git a/libobs/graphics/effect-parser.c b/libobs/graphics/effect-parser.c index 2a6697f..a2a931b 100644 --- a/libobs/graphics/effect-parser.c +++ b/libobs/graphics/effect-parser.c @@ -228,12 +228,14 @@ static void ep_parse_struct(struct effect_parser *ep) case PARSE_UNEXPECTED_CONTINUE: cf_adderror_syntax_error(&ep->cfp); + /* Falls through. */ case PARSE_CONTINUE: ep_var_free(&var); continue; case PARSE_UNEXPECTED_BREAK: cf_adderror_syntax_error(&ep->cfp); + /* Falls through. */ case PARSE_BREAK: ep_var_free(&var); do_break = true; diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index bd79fa8..ff11661 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -519,7 +519,7 @@ EXPORT uint8_t *gs_create_texture_file_data(const char *file, * Draws a 2D sprite * * If width or height is 0, the width or height of the texture will be used. - * The flip value specifies whether the texture shoudl be flipped on the U or V + * The flip value specifies whether the texture should be flipped on the U or V * axis with GS_FLIP_U and GS_FLIP_V. */ EXPORT void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, @@ -534,7 +534,7 @@ EXPORT void gs_draw_cube_backdrop(gs_texture_t *cubetex, const struct quat *rot, /** sets the viewport to current swap chain size */ EXPORT void gs_reset_viewport(void); -/** sets default screen-sized orthographich mode */ +/** sets default screen-sized orthographic mode */ EXPORT void gs_set_2d_mode(void); /** sets default screen-sized perspective mode */ EXPORT void gs_set_3d_mode(double fovy, double znear, double zvar); diff --git a/libobs/graphics/shader-parser.c b/libobs/graphics/shader-parser.c index 6771d4c..5b17333 100644 --- a/libobs/graphics/shader-parser.c +++ b/libobs/graphics/shader-parser.c @@ -277,12 +277,14 @@ static void sp_parse_struct(struct shader_parser *sp) case PARSE_UNEXPECTED_CONTINUE: cf_adderror_syntax_error(&sp->cfp); + /* Falls through. */ case PARSE_CONTINUE: shader_var_free(&var); continue; case PARSE_UNEXPECTED_BREAK: cf_adderror_syntax_error(&sp->cfp); + /* Falls through. */ case PARSE_BREAK: shader_var_free(&var); do_break = true; diff --git a/libobs/media-io/video-io.c b/libobs/media-io/video-io.c index 63de9df..b33ef91 100644 --- a/libobs/media-io/video-io.c +++ b/libobs/media-io/video-io.c @@ -16,6 +16,7 @@ ******************************************************************************/ #include +#include #include "../util/bmem.h" #include "../util/platform.h" #include "../util/profiler.h" @@ -34,6 +35,7 @@ extern profiler_name_store_t *obs_get_profiler_name_store(void); struct cached_frame_info { struct video_data frame; + int skipped; int count; }; @@ -114,6 +116,7 @@ static inline bool video_output_cur_frame(struct video_output *video) { struct cached_frame_info *frame_info; bool complete; + bool skipped; /* -------------------------------- */ @@ -143,6 +146,7 @@ static inline bool video_output_cur_frame(struct video_output *video) frame_info->frame.timestamp += video->frame_time; complete = --frame_info->count == 0; + skipped = frame_info->skipped > 0; if (complete) { if (++video->first_added == video->info.cache_size) @@ -150,6 +154,9 @@ static inline bool video_output_cur_frame(struct video_output *video) if (++video->available_frames == video->info.cache_size) video->last_added = video->first_added; + } else if (skipped) { + --frame_info->skipped; + ++video->skipped_frames; } pthread_mutex_unlock(&video->data_mutex); @@ -333,6 +340,11 @@ bool video_output_connect(video_t *video, pthread_mutex_lock(&video->input_mutex); + if (video->inputs.num == 0) { + video->skipped_frames = 0; + video->total_frames = 0; + } + if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) { struct video_input input; memset(&input, 0, sizeof(input)); @@ -378,6 +390,20 @@ void video_output_disconnect(video_t *video, da_erase(video->inputs, idx); } + if (video->inputs.num == 0) { + double percentage_skipped = (double)video->skipped_frames / + (double)video->total_frames * 100.0; + + if (video->skipped_frames) + blog(LOG_INFO, "Video stopped, number of " + "skipped frames due " + "to encoding lag: " + "%"PRIu32"/%"PRIu32" (%0.1f%%)", + video->skipped_frames, + video->total_frames, + percentage_skipped); + } + pthread_mutex_unlock(&video->input_mutex); } @@ -403,8 +429,8 @@ bool video_output_lock_frame(video_t *video, struct video_frame *frame, pthread_mutex_lock(&video->data_mutex); if (video->available_frames == 0) { - video->skipped_frames += count; video->cache[video->last_added].count += count; + video->cache[video->last_added].skipped += count; locked = false; } else { @@ -416,6 +442,7 @@ bool video_output_lock_frame(video_t *video, struct video_frame *frame, cfi = &video->cache[video->last_added]; cfi->frame.timestamp = timestamp; cfi->count = count; + cfi->skipped = 0; memcpy(frame, &cfi->frame, sizeof(*frame)); diff --git a/libobs/obs-audio-controls.h b/libobs/obs-audio-controls.h index 89020af..fbf7481 100644 --- a/libobs/obs-audio-controls.h +++ b/libobs/obs-audio-controls.h @@ -200,14 +200,6 @@ EXPORT bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, */ EXPORT void obs_volmeter_detach_source(obs_volmeter_t *volmeter); -/** - * @brief Get signal handler for the volume meter object - * @param volmeter pointer to the volume meter object - * @return signal handler - */ -EXPORT signal_handler_t *obs_volmeter_get_signal_handler( - obs_volmeter_t *volmeter); - /** * @brief Set the update interval for the volume meter * @param volmeter pointer to the volume meter object diff --git a/libobs/obs-audio.c b/libobs/obs-audio.c index e4932c3..d644ea7 100644 --- a/libobs/obs-audio.c +++ b/libobs/obs-audio.c @@ -108,10 +108,20 @@ static bool discard_if_stopped(obs_source_t *source, size_t channels) /* if perpetually pending data, it means the audio has stopped, * so clear the audio data */ if (last_size == size) { + if (!source->pending_stop) { + source->pending_stop = true; +#if DEBUG_AUDIO == 1 + blog(LOG_DEBUG, "doing pending stop trick: '%s'", + source->context.name); +#endif + return true; + } + for (size_t ch = 0; ch < channels; ch++) circlebuf_pop_front(&source->audio_input_buf[ch], NULL, source->audio_input_buf[ch].size); + source->pending_stop = false; source->audio_ts = 0; source->last_audio_input_buf_size = 0; #if DEBUG_AUDIO == 1 @@ -179,7 +189,7 @@ static inline void discard_audio(struct obs_core_audio *audio, if (start_point == AUDIO_OUTPUT_FRAMES) { #if DEBUG_AUDIO == 1 if (is_audio_source) - blog(LOG_DEBUG, "can't dicard, start point is " + blog(LOG_DEBUG, "can't discard, start point is " "at audio frame count"); #endif return; @@ -212,6 +222,7 @@ static inline void discard_audio(struct obs_core_audio *audio, ts->end); #endif + source->pending_stop = false; source->audio_ts = ts->end; } diff --git a/libobs/obs-cocoa.c b/libobs/obs-cocoa.c index a1755c3..33a2110 100644 --- a/libobs/obs-cocoa.c +++ b/libobs/obs-cocoa.c @@ -94,23 +94,8 @@ static void log_processor_speed(void) static void log_processor_cores(void) { - size_t size; - int physical_cores = 0, logical_cores = 0; - int ret; - - size = sizeof(physical_cores); - ret = sysctlbyname("machdep.cpu.core_count", &physical_cores, - &size, NULL, 0); - if (ret != 0) - return; - - ret = sysctlbyname("machdep.cpu.thread_count", &logical_cores, - &size, NULL, 0); - if (ret != 0) - return; - blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d", - physical_cores, logical_cores); + os_get_physical_cores(), os_get_logical_cores()); } static void log_available_memory(void) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index c1e05b2..505f839 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 18 +#define LIBOBS_API_MAJOR_VER 19 /* * Increment if backward-compatible additions @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 1 +#define LIBOBS_API_PATCH_VER 3 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | \ diff --git a/libobs/obs-data.c b/libobs/obs-data.c index 5f94b32..efd406a 100644 --- a/libobs/obs-data.c +++ b/libobs/obs-data.c @@ -682,7 +682,6 @@ obs_data_t *obs_data_create_from_json_file_safe(const char *json_file, /* delete current file if corrupt to prevent it from * being backed up again */ - os_unlink(json_file); os_rename(backup_file.array, json_file); file_data = obs_data_create_from_json_file(json_file); diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h index bb393f5..2220c5a 100644 --- a/libobs/obs-encoder.h +++ b/libobs/obs-encoder.h @@ -184,7 +184,7 @@ struct obs_encoder_info { /** * Updates the settings for this encoder (usually used for things like - * changeing birate while active) + * changing bitrate while active) * * @param data Data associated with this encoder context * @param settings New settings for this encoder diff --git a/libobs/obs-hotkey.c b/libobs/obs-hotkey.c index fdf9e75..8ccc50c 100644 --- a/libobs/obs-hotkey.c +++ b/libobs/obs-hotkey.c @@ -1197,8 +1197,8 @@ reset: struct obs_hotkey_internal_inject { obs_key_combination_t hotkey; - bool pressed : 1; - bool strict_modifiers : 1; + bool pressed; + bool strict_modifiers; }; static inline bool inject_hotkey(void *data, @@ -1251,8 +1251,8 @@ void obs_hotkey_enable_strict_modifiers(bool enable) struct obs_query_hotkeys_helper { uint32_t modifiers; - bool no_press : 1; - bool strict_modifiers : 1; + bool no_press; + bool strict_modifiers; }; static inline bool query_hotkey(void *data, diff --git a/libobs/obs-hotkey.h b/libobs/obs-hotkey.h index d5e62ce..7ff2bae 100644 --- a/libobs/obs-hotkey.h +++ b/libobs/obs-hotkey.h @@ -112,7 +112,7 @@ struct obs_hotkeys_translations { * that may not have translations. If the operating system can provide * translations for these keys, it will use the operating system's translation * over these translations. If no translations are specified, it will use - * the default english translations for that specific operating system. */ + * the default English translations for that specific operating system. */ EXPORT void obs_hotkeys_set_translations_s( struct obs_hotkeys_translations *translations, size_t size); diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index f6f2450..4d3a75c 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -140,8 +140,8 @@ struct obs_hotkey_pair { obs_hotkey_pair_id pair_id; obs_hotkey_id id[2]; obs_hotkey_active_func func[2]; - bool pressed0 : 1; - bool pressed1 : 1; + bool pressed0; + bool pressed1; void *data[2]; }; @@ -166,8 +166,8 @@ void obs_hotkeys_free(void); struct obs_hotkey_binding { obs_key_combination_t key; - bool pressed : 1; - bool modifiers_match : 1; + bool pressed; + bool modifiers_match; obs_hotkey_id hotkey_id; obs_hotkey_t *hotkey; @@ -244,6 +244,7 @@ struct obs_core_video { int cur_texture; uint64_t video_time; + uint64_t video_avg_frame_time_ns; double video_fps; video_t *video; pthread_t video_thread; @@ -275,6 +276,8 @@ struct obs_core_video { gs_effect_t *deinterlace_blend_2x_effect; gs_effect_t *deinterlace_yadif_effect; gs_effect_t *deinterlace_yadif_2x_effect; + + struct obs_video_info ovi; }; struct audio_monitor; @@ -313,6 +316,8 @@ struct obs_core_data { pthread_mutex_t encoders_mutex; pthread_mutex_t services_mutex; pthread_mutex_t audio_sources_mutex; + pthread_mutex_t draw_callbacks_mutex; + DARRAY(struct draw_callback) draw_callbacks; struct obs_view main_view; @@ -332,9 +337,9 @@ struct obs_core_hotkeys { pthread_t hotkey_thread; bool hotkey_thread_initialized; os_event_t *stop_event; - bool thread_disable_press : 1; - bool strict_modifiers : 1; - bool reroute_hotkeys : 1; + bool thread_disable_press; + bool strict_modifiers; + bool reroute_hotkeys; DARRAY(obs_hotkey_binding_t) bindings; obs_hotkey_callback_router_func router_func; @@ -562,6 +567,7 @@ struct obs_source { /* audio */ bool audio_failed; bool audio_pending; + bool pending_stop; bool user_muted; bool muted; struct obs_source *next_audio_source; @@ -602,6 +608,8 @@ struct obs_source { bool async_flip; bool async_active; bool async_update_texture; + bool async_unbuffered; + struct obs_source_frame *async_preload_frame; DARRAY(struct async_frame) async_cache; DARRAY(struct obs_source_frame*)async_frames; pthread_mutex_t async_mutex; @@ -637,12 +645,12 @@ struct obs_source { obs_hotkey_pair_id mute_unmute_key; obs_hotkey_id push_to_mute_key; obs_hotkey_id push_to_talk_key; - bool push_to_mute_enabled : 1; - bool push_to_mute_pressed : 1; - bool user_push_to_mute_pressed : 1; - bool push_to_talk_enabled : 1; - bool push_to_talk_pressed : 1; - bool user_push_to_talk_pressed : 1; + bool push_to_mute_enabled; + bool push_to_mute_pressed; + bool user_push_to_mute_pressed; + bool push_to_talk_enabled; + bool push_to_talk_pressed; + bool user_push_to_talk_pressed; uint64_t push_to_mute_delay; uint64_t push_to_mute_stop_time; uint64_t push_to_talk_delay; @@ -664,7 +672,7 @@ struct obs_source { uint32_t transition_cx; uint32_t transition_cy; uint32_t transition_fixed_duration; - bool transition_use_fixed_duration : 1; + bool transition_use_fixed_duration; enum obs_transition_mode transition_mode; enum obs_transition_scale_type transition_scale_type; struct matrix4 transition_matrices[2]; @@ -828,7 +836,6 @@ struct obs_output { uint32_t starting_drawn_count; uint32_t starting_lagged_count; uint32_t starting_frame_count; - uint32_t starting_skipped_frame_count; int total_frames; @@ -865,6 +872,8 @@ struct obs_output { volatile long delay_restart_refs; volatile bool delay_active; volatile bool delay_capturing; + + char *last_error_message; }; static inline void do_output_signal(struct obs_output *output, diff --git a/libobs/obs-module.h b/libobs/obs-module.h index aa8c1d7..cf449f3 100644 --- a/libobs/obs-module.h +++ b/libobs/obs-module.h @@ -90,7 +90,7 @@ bool obs_module_load(void) * may need loading. * * @return Return true to continue loading the module, otherwise - * false to indcate failure and unload the module + * false to indicate failure and unload the module */ MODULE_EXPORT bool obs_module_load(void); diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 0d73665..81e64e2 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -212,6 +212,8 @@ void obs_output_destroy(obs_output_t *output) circlebuf_free(&output->delay_data); if (output->owns_info_id) bfree((void*)output->info.id); + if (output->last_error_message) + bfree(output->last_error_message); bfree(output); } } @@ -228,6 +230,10 @@ bool obs_output_actual_start(obs_output_t *output) os_event_wait(output->stopping_event); output->stop_code = 0; + if (output->last_error_message) { + bfree(output->last_error_message); + output->last_error_message = NULL; + } if (output->context.data) success = output->info.start(output->context.data); @@ -235,8 +241,6 @@ bool obs_output_actual_start(obs_output_t *output) if (success && output->video) { output->starting_frame_count = video_output_get_total_frames(output->video); - output->starting_skipped_frame_count = - video_output_get_skipped_frames(output->video); output->starting_drawn_count = obs->video.total_frames; output->starting_lagged_count = obs->video.lagged_frames; } @@ -280,24 +284,19 @@ static void log_frame_info(struct obs_output *output) struct obs_core_video *video = &obs->video; uint32_t video_frames = video_output_get_total_frames(output->video); - uint32_t video_skipped = video_output_get_skipped_frames(output->video); uint32_t total = video_frames - output->starting_frame_count; - uint32_t skipped = video_skipped - output->starting_skipped_frame_count; uint32_t drawn = video->total_frames - output->starting_drawn_count; uint32_t lagged = video->lagged_frames - output->starting_lagged_count; int dropped = obs_output_get_frames_dropped(output); - double percentage_skipped = 0.0f; double percentage_lagged = 0.0f; double percentage_dropped = 0.0f; - if (total) { - percentage_skipped = (double)skipped / (double)total * 100.0; + if (total) percentage_dropped = (double)dropped / (double)total * 100.0; - } if (drawn) percentage_lagged = (double)lagged / (double)drawn * 100.0; @@ -307,11 +306,6 @@ static void log_frame_info(struct obs_output *output) blog(LOG_INFO, "Output '%s': Total drawn frames: %"PRIu32, output->context.name, drawn); - if (total && skipped) - blog(LOG_INFO, "Output '%s': Number of skipped frames due " - "to encoding lag: %"PRIu32" (%0.1f%%)", - output->context.name, - skipped, percentage_skipped); if (drawn && lagged) blog(LOG_INFO, "Output '%s': Number of lagged frames due " "to rendering lag/stalls: %"PRIu32" (%0.1f%%)", @@ -352,10 +346,10 @@ void obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts) obs_output_end_data_capture(output); os_event_signal(output->stopping_event); } else { - call_stop = data_active(output); + call_stop = true; } } else { - call_stop = data_active(output); + call_stop = true; } if (output->context.data && call_stop) { @@ -1013,7 +1007,7 @@ static inline void send_interleaved(struct obs_output *output) struct encoder_packet out = output->interleaved_packets.array[0]; /* do not send an interleaved packet if there's no packet of the - * opposing type of a higher timstamp in the interleave buffer. + * opposing type of a higher timestamp in the interleave buffer. * this ensures that the timestamps are monotonic */ if (!has_higher_opposing_ts(output, &out)) return; @@ -1570,12 +1564,15 @@ static inline void signal_reconnect_success(struct obs_output *output) static inline void signal_stop(struct obs_output *output) { struct calldata params; - uint8_t stack[128]; - calldata_init_fixed(¶ms, stack, sizeof(stack)); + calldata_init(¶ms); + calldata_set_string(¶ms, "last_error", output->last_error_message); calldata_set_int(¶ms, "code", output->stop_code); calldata_set_ptr(¶ms, "output", output); + signal_handler_signal(output->context.signals, "stop", ¶ms); + + calldata_free(¶ms); } static inline void convert_flags(const struct obs_output *output, @@ -1822,6 +1819,7 @@ static void obs_output_end_data_capture_internal(obs_output_t *output, if (signal) { signal_stop(output); output->stop_code = OBS_OUTPUT_SUCCESS; + os_event_signal(output->stopping_event); } return; } @@ -2069,7 +2067,7 @@ void obs_output_output_caption_text1(obs_output_t *output, const char *text) if (!active(output)) return; - // split text into 32 charcter strings + // split text into 32 character strings int size = (int)strlen(text); int r; size_t char_count; @@ -2117,3 +2115,43 @@ float obs_output_get_congestion(obs_output_t *output) } return 0; } + +int obs_output_get_connect_time_ms(obs_output_t *output) +{ + if (!obs_output_valid(output, "obs_output_get_connect_time_ms")) + return -1; + + if (output->info.get_connect_time_ms) + return output->info.get_connect_time_ms(output->context.data); + return -1; +} + +const char *obs_output_get_last_error(obs_output_t *output) +{ + if (!obs_output_valid(output, "obs_output_get_last_error")) + return NULL; + + return output->last_error_message; +} + +void obs_output_set_last_error(obs_output_t *output, const char *message) +{ + if (!obs_output_valid(output, "obs_output_set_last_error")) + return; + + if (output->last_error_message) + bfree(output->last_error_message); + + if (message) + output->last_error_message = bstrdup(message); + else + output->last_error_message = NULL; +} + +bool obs_output_reconnecting(const obs_output_t *output) +{ + if (!obs_output_valid(output, "obs_output_reconnecting")) + return false; + + return reconnecting(output); +} diff --git a/libobs/obs-output.h b/libobs/obs-output.h index 9d00108..6f79cc9 100644 --- a/libobs/obs-output.h +++ b/libobs/obs-output.h @@ -66,6 +66,7 @@ struct obs_output_info { void (*free_type_data)(void *type_data); float (*get_congestion)(void *data); + int (*get_connect_time_ms)(void *data); }; EXPORT void obs_register_output_s(const struct obs_output_info *info, diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 28babb3..3294021 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -71,6 +71,8 @@ static void *scene_create(obs_data_t *settings, struct obs_source *source) 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) @@ -608,6 +610,9 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) obs_data_set_default_int(item_data, "align", OBS_ALIGN_TOP | OBS_ALIGN_LEFT); + if (obs_data_has_user_value(item_data, "id")) + item->id = obs_data_get_int(item_data, "id"); + item->rot = (float)obs_data_get_double(item_data, "rot"); item->align = (uint32_t)obs_data_get_int(item_data, "align"); visible = obs_data_get_bool(item_data, "visible"); @@ -659,8 +664,9 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) update_item_transform(item); } -static void scene_load(void *scene, obs_data_t *settings) +static void scene_load(void *data, obs_data_t *settings) { + struct obs_scene *scene = data; obs_data_array_t *items = obs_data_get_array(settings, "items"); size_t count, i; @@ -676,6 +682,9 @@ static void scene_load(void *scene, obs_data_t *settings) obs_data_release(item_data); } + if (obs_data_has_user_value(settings, "id_counter")) + scene->id_counter = obs_data_get_int(settings, "id_counter"); + obs_data_array_release(items); } @@ -699,6 +708,7 @@ static void scene_save_item(obs_data_array_t *array, obs_data_set_int (item_data, "crop_top", (int)item->crop.top); 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); if (item->scale_filter == OBS_SCALE_POINT) scale_filter = "point"; @@ -731,6 +741,8 @@ static void scene_save(void *data, obs_data_t *settings) item = item->next; } + obs_data_set_int(settings, "id_counter", scene->id_counter); + full_unlock(scene); obs_data_set_array(settings, "items", array); @@ -1160,6 +1172,28 @@ obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name) return item; } +obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id) +{ + struct obs_scene_item *item; + + if (!scene) + return NULL; + + full_lock(scene); + + item = scene->first_item; + while (item) { + if (item->id == id) + break; + + item = item->next; + } + + full_unlock(scene); + + return item; +} + void obs_scene_enum_items(obs_scene_t *scene, bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), void *param) @@ -1307,6 +1341,7 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source) item = bzalloc(sizeof(struct obs_scene_item)); item->source = source; + item->id = ++scene->id_counter; item->parent = scene; item->ref = 1; item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT; @@ -1919,3 +1954,11 @@ void obs_sceneitem_defer_update_end(obs_sceneitem_t *item) if (os_atomic_dec_long(&item->defer_update) == 0) update_item_transform(item); } + +int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item) +{ + if (!obs_ptr_valid(item, "obs_sceneitem_get_id")) + return 0; + + return item->id; +} diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 8dbe8b8..d460221 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -32,6 +32,8 @@ struct obs_scene_item { volatile long ref; volatile bool removed; + int64_t id; + struct obs_scene *parent; struct obs_source *source; volatile long active_refs; @@ -49,7 +51,7 @@ struct obs_scene_item { uint32_t align; /* last width/height of the source, this is used to check whether - * ths transform needs updating */ + * the transform needs updating */ uint32_t last_width; uint32_t last_height; @@ -76,6 +78,8 @@ struct obs_scene_item { struct obs_scene { struct obs_source *source; + int64_t id_counter; + pthread_mutex_t video_mutex; pthread_mutex_t audio_mutex; struct obs_scene_item *first_item; diff --git a/libobs/obs-source-deinterlace.c b/libobs/obs-source-deinterlace.c index c655405..1fce870 100644 --- a/libobs/obs-source-deinterlace.c +++ b/libobs/obs-source-deinterlace.c @@ -27,7 +27,7 @@ static bool ready_deinterlace_frames(obs_source_t *source, uint64_t sys_time) uint64_t frame_offset = 0; size_t idx = 1; - if ((source->flags & OBS_SOURCE_FLAG_UNBUFFERED) != 0) { + if (source->async_unbuffered) { while (source->async_frames.num > 2) { da_erase(source->async_frames, 0); remove_async_frame(source, next_frame); diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 4b3539a..d76ce23 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -365,6 +365,27 @@ obs_source_t *obs_source_create_private(const char *id, const char *name, return obs_source_create_internal(id, name, settings, NULL, true); } +static char *get_new_filter_name(obs_source_t *dst, const char *name) +{ + struct dstr new_name = {0}; + int inc = 0; + + dstr_copy(&new_name, name); + + for (;;) { + obs_source_t *existing_filter = obs_source_get_filter_by_name( + dst, new_name.array); + if (!existing_filter) + break; + + obs_source_release(existing_filter); + + dstr_printf(&new_name, "%s %d", name, ++inc + 1); + } + + return new_name.array; +} + static void duplicate_filters(obs_source_t *dst, obs_source_t *src, bool private) { @@ -380,9 +401,13 @@ static void duplicate_filters(obs_source_t *dst, obs_source_t *src, for (size_t i = filters.num; i > 0; i--) { obs_source_t *src_filter = filters.array[i - 1]; - obs_source_t *dst_filter = obs_source_duplicate(src_filter, - src_filter->context.name, private); + char *new_name = get_new_filter_name(dst, + src_filter->context.name); + obs_source_t *dst_filter = obs_source_duplicate(src_filter, + new_name, private); + + bfree(new_name); obs_source_filter_add(dst, dst_filter); obs_source_release(dst_filter); obs_source_release(src_filter); @@ -391,6 +416,15 @@ static void duplicate_filters(obs_source_t *dst, obs_source_t *src, da_free(filters); } +void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src) +{ + duplicate_filters(dst, src, dst->context.private ? + OBS_SCENE_DUP_PRIVATE_COPY : + OBS_SCENE_DUP_COPY); + + obs_source_release(src); +} + obs_source_t *obs_source_duplicate(obs_source_t *source, const char *new_name, bool create_private) { @@ -532,6 +566,8 @@ void obs_source_destroy(struct obs_source *source) audio_resampler_destroy(source->resampler); bfree(source->audio_output_buf[0][0]); + obs_source_frame_destroy(source->async_preload_frame); + if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) obs_transition_free(source); @@ -1037,6 +1073,7 @@ static void reset_audio_data(obs_source_t *source, uint64_t os_time) source->last_audio_input_buf_size = 0; source->audio_ts = os_time; + source->next_audio_sys_ts_min = os_time; } static void handle_ts_jump(obs_source_t *source, uint64_t expected, @@ -1452,6 +1489,12 @@ static inline void set_eparam(gs_effect_t *effect, const char *name, float val) gs_effect_set_float(param, val); } +static inline void set_eparami(gs_effect_t *effect, const char *name, int val) +{ + gs_eparam_t *param = gs_effect_get_param_by_name(effect, name); + gs_effect_set_int(param, val); +} + static bool update_async_texrender(struct obs_source *source, const struct obs_source_frame *frame, gs_texture_t *tex, gs_texrender_t *texrender) @@ -1479,22 +1522,16 @@ static bool update_async_texrender(struct obs_source *source, gs_effect_set_texture(gs_effect_get_param_by_name(conv, "image"), tex); set_eparam(conv, "width", (float)cx); set_eparam(conv, "height", (float)cy); - set_eparam(conv, "width_i", 1.0f / cx); - set_eparam(conv, "height_i", 1.0f / cy); set_eparam(conv, "width_d2", cx * 0.5f); - set_eparam(conv, "height_d2", cy * 0.5f); set_eparam(conv, "width_d2_i", 1.0f / (cx * 0.5f)); - set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f)); - set_eparam(conv, "input_width", convert_width); - set_eparam(conv, "input_height", convert_height); - set_eparam(conv, "input_width_i", 1.0f / convert_width); - set_eparam(conv, "input_height_i", 1.0f / convert_height); set_eparam(conv, "input_width_i_d2", (1.0f / convert_width) * 0.5f); - set_eparam(conv, "input_height_i_d2", (1.0f / convert_height) * 0.5f); - set_eparam(conv, "u_plane_offset", - (float)source->async_plane_offset[0]); - set_eparam(conv, "v_plane_offset", - (float)source->async_plane_offset[1]); + + set_eparami(conv, "int_width", (int)cx); + set_eparami(conv, "int_input_width", (int)source->async_convert_width); + set_eparami(conv, "int_u_plane_offset", + (int)source->async_plane_offset[0]); + set_eparami(conv, "int_v_plane_offset", + (int)source->async_plane_offset[1]); gs_ortho(0.f, (float)cx, 0.f, (float)cy, -100.f, 100.f); @@ -1658,7 +1695,7 @@ static inline void obs_source_render_filters(obs_source_t *source) source->rendering_filter = false; } -static void obs_source_default_render(obs_source_t *source) +void obs_source_default_render(obs_source_t *source) { gs_effect_t *effect = obs->video.default_effect; gs_technique_t *tech = gs_effect_get_technique(effect, "Draw"); @@ -1849,6 +1886,18 @@ obs_source_t *obs_filter_get_target(const obs_source_t *filter) filter->filter_target : NULL; } +static bool filter_compatible(obs_source_t *source, obs_source_t *filter) +{ + uint32_t s_caps = source->info.output_flags; + uint32_t f_caps = filter->info.output_flags; + + if ((f_caps & OBS_SOURCE_AUDIO) != 0 && + (f_caps & OBS_SOURCE_VIDEO) == 0) + f_caps &= ~OBS_SOURCE_ASYNC; + + return (s_caps & f_caps) == f_caps; +} + void obs_source_filter_add(obs_source_t *source, obs_source_t *filter) { struct calldata cd; @@ -1868,6 +1917,11 @@ void obs_source_filter_add(obs_source_t *source, obs_source_t *filter) return; } + if (!filter_compatible(source, filter)) { + pthread_mutex_unlock(&source->filter_mutex); + return; + } + obs_source_addref(filter); filter->filter_parent = source; @@ -2313,6 +2367,62 @@ void obs_source_output_video(obs_source_t *source, } } +static inline bool preload_frame_changed(obs_source_t *source, + const struct obs_source_frame *in) +{ + if (!source->async_preload_frame) + return true; + + return in->width != source->async_preload_frame->width || + in->height != source->async_preload_frame->height || + in->format != source->async_preload_frame->format; +} + +void obs_source_preload_video(obs_source_t *source, + const struct obs_source_frame *frame) +{ + if (!obs_source_valid(source, "obs_source_preload_video")) + return; + if (!frame) + return; + + obs_enter_graphics(); + + if (preload_frame_changed(source, frame)) { + obs_source_frame_destroy(source->async_preload_frame); + source->async_preload_frame = obs_source_frame_create( + frame->format, + frame->width, + frame->height); + } + + copy_frame_data(source->async_preload_frame, frame); + set_async_texture_size(source, source->async_preload_frame); + update_async_texture(source, source->async_preload_frame, + source->async_texture, + source->async_texrender); + + source->last_frame_ts = frame->timestamp; + + obs_leave_graphics(); +} + +void obs_source_show_preloaded_video(obs_source_t *source) +{ + uint64_t sys_ts; + + if (!obs_source_valid(source, "obs_source_show_preloaded_video")) + return; + + source->async_active = true; + + pthread_mutex_lock(&source->audio_buf_mutex); + sys_ts = os_gettime_ns(); + reset_audio_timing(source, source->last_frame_ts, sys_ts); + reset_audio_data(source, sys_ts); + pthread_mutex_unlock(&source->audio_buf_mutex); +} + static inline struct obs_audio_data *filter_async_audio(obs_source_t *source, struct obs_audio_data *in) { @@ -2509,7 +2619,7 @@ static bool ready_async_frame(obs_source_t *source, uint64_t sys_time) uint64_t frame_time = next_frame->timestamp; uint64_t frame_offset = 0; - if ((source->flags & OBS_SOURCE_FLAG_UNBUFFERED) != 0) { + if (source->async_unbuffered) { while (source->async_frames.num > 1) { da_erase(source->async_frames, 0); remove_async_frame(source, next_frame); @@ -3903,8 +4013,6 @@ void obs_source_set_monitoring_type(obs_source_t *source, if (!obs_source_valid(source, "obs_source_set_monitoring_type")) return; - if (source->info.output_flags & OBS_SOURCE_DO_NOT_MONITOR) - return; if (source->monitoring_type == type) return; @@ -3929,3 +4037,17 @@ enum obs_monitoring_type obs_source_get_monitoring_type( return obs_source_valid(source, "obs_source_get_monitoring_type") ? source->monitoring_type : OBS_MONITORING_TYPE_NONE; } + +void obs_source_set_async_unbuffered(obs_source_t *source, bool unbuffered) +{ + if (!obs_source_valid(source, "obs_source_set_async_unbuffered")) + return; + + source->async_unbuffered = unbuffered; +} + +bool obs_source_async_unbuffered(const obs_source_t *source) +{ + return obs_source_valid(source, "obs_source_async_unbuffered") ? + source->async_unbuffered : false; +} diff --git a/libobs/obs-source.h b/libobs/obs-source.h index bb3ae3c..8b189c5 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -123,10 +123,12 @@ enum obs_source_type { /** * Source cannot have its audio monitored * - * Specifies that this source may cause a feedback loop if audio is monitored. + * Specifies that this source may cause a feedback loop if audio is monitored + * with a device selected as desktop audio. + * * This is used primarily with desktop audio capture sources. */ -#define OBS_SOURCE_DO_NOT_MONITOR (1<<9) +#define OBS_SOURCE_DO_NOT_SELF_MONITOR (1<<9) /** @} */ @@ -171,7 +173,7 @@ struct obs_source_info { * Creates the source data for the source * * @param settings Settings to initialize the source with - * @param source Source that this data is assoicated with + * @param source Source that this data is associated with * @return The data associated with this source */ void *(*create)(obs_data_t *settings, obs_source_t *source); @@ -257,7 +259,7 @@ struct obs_source_info { * If the source output flags do not include SOURCE_CUSTOM_DRAW, all * a source needs to do is set the "image" parameter of the effect to * the desired texture, and then draw. If the output flags include - * SOURCE_COLOR_MATRIX, you may optionally set the the "color_matrix" + * SOURCE_COLOR_MATRIX, you may optionally set the "color_matrix" * parameter of the effect to a custom 4x4 conversion matrix (by * default it will be set to an YUV->RGB conversion matrix) * @@ -429,7 +431,7 @@ EXPORT void obs_register_source_s(const struct obs_source_info *info, size_t size); /** - * Regsiters a source definition to the current obs context. This should be + * Registers a source definition to the current obs context. This should be * used in obs_module_load. * * @param info Pointer to the source definition structure diff --git a/libobs/obs-ui.h b/libobs/obs-ui.h index f5947b4..5944505 100644 --- a/libobs/obs-ui.h +++ b/libobs/obs-ui.h @@ -71,7 +71,7 @@ struct obs_modal_ui { }; /** - * Regsiters a modal UI definition to the current obs context. This should be + * Registers a modal UI definition to the current obs context. This should be * used in obs_module_load. * * @param info Pointer to the modal definition structure diff --git a/libobs/obs-video.c b/libobs/obs-video.c index cdf83f2..8bff747 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -105,6 +105,19 @@ static inline void render_main_texture(struct obs_core_video *video, gs_clear(GS_CLEAR_COLOR, &clear_color, 1.0f, 0); set_render_size(video->base_width, video->base_height); + + pthread_mutex_lock(&obs->data.draw_callbacks_mutex); + + for (size_t i = 0; i < obs->data.draw_callbacks.num; i++) { + struct draw_callback *callback; + callback = obs->data.draw_callbacks.array+i; + + callback->draw(callback->param, + video->base_width, video->base_height); + } + + pthread_mutex_unlock(&obs->data.draw_callbacks_mutex); + obs_view_render(&obs->data.main_view); video->textures_rendered[cur_texture] = true; @@ -573,6 +586,7 @@ void *obs_video_thread(void *param) { uint64_t last_time = 0; uint64_t interval = video_output_get_frame_time(obs->video.video); + uint64_t frame_time_total_ns = 0; uint64_t fps_total_ns = 0; uint32_t fps_total_frames = 0; @@ -586,6 +600,9 @@ void *obs_video_thread(void *param) profile_register_root(video_thread_name, interval); while (!video_output_stopped(obs->video.video)) { + uint64_t frame_start = os_gettime_ns(); + uint64_t frame_time_ns; + profile_start(video_thread_name); profile_start(tick_sources_name); @@ -600,18 +617,25 @@ void *obs_video_thread(void *param) output_frame(); profile_end(output_frame_name); + frame_time_ns = os_gettime_ns() - frame_start; + profile_end(video_thread_name); profile_reenable_thread(); video_sleep(&obs->video, &obs->video.video_time, interval); + frame_time_total_ns += frame_time_ns; fps_total_ns += (obs->video.video_time - last_time); fps_total_frames++; if (fps_total_ns >= 1000000000ULL) { obs->video.video_fps = (double)fps_total_frames / ((double)fps_total_ns / 1000000000.0); + obs->video.video_avg_frame_time_ns = + frame_time_total_ns / (uint64_t)fps_total_frames; + + frame_time_total_ns = 0; fps_total_ns = 0; fps_total_frames = 0; } diff --git a/libobs/obs-windows.c b/libobs/obs-windows.c index e2c19c8..b8a139e 100644 --- a/libobs/obs-windows.c +++ b/libobs/obs-windows.c @@ -105,54 +105,10 @@ static void log_processor_info(void) RegCloseKey(key); } -static DWORD num_logical_cores(ULONG_PTR mask) -{ - DWORD left_shift = sizeof(ULONG_PTR) * 8 - 1; - DWORD bit_set_count = 0; - ULONG_PTR bit_test = (ULONG_PTR)1 << left_shift; - - for (DWORD i = 0; i <= left_shift; ++i) { - bit_set_count += ((mask & bit_test) ? 1 : 0); - bit_test /= 2; - } - - return bit_set_count; -} - static void log_processor_cores(void) { - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = NULL, temp = NULL; - DWORD len = 0; - - GetLogicalProcessorInformation(info, &len); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - return; - - info = malloc(len); - - if (GetLogicalProcessorInformation(info, &len)) { - DWORD num = len / sizeof(*info); - int physical_cores = 0; - int logical_cores = 0; - - temp = info; - - for (DWORD i = 0; i < num; i++) { - if (temp->Relationship == RelationProcessorCore) { - ULONG_PTR mask = temp->ProcessorMask; - - physical_cores++; - logical_cores += num_logical_cores(mask); - } - - temp++; - } - - blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d", - physical_cores, logical_cores); - } - - free(info); + blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d", + os_get_physical_cores(), os_get_logical_cores()); } static void log_available_memory(void) diff --git a/libobs/obs.c b/libobs/obs.c index de01ed7..b41c12a 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -390,6 +390,7 @@ static int obs_init_video(struct obs_video_info *ovi) return OBS_VIDEO_FAIL; video->thread_initialized = true; + video->ovi = *ovi; return OBS_VIDEO_SUCCESS; } @@ -541,6 +542,7 @@ static bool obs_init_data(void) assert(data != NULL); pthread_mutex_init_value(&obs->data.displays_mutex); + pthread_mutex_init_value(&obs->data.draw_callbacks_mutex); if (pthread_mutexattr_init(&attr) != 0) return false; @@ -558,6 +560,8 @@ static bool obs_init_data(void) goto fail; if (pthread_mutex_init(&data->services_mutex, &attr) != 0) goto fail; + if (pthread_mutex_init(&obs->data.draw_callbacks_mutex, NULL) != 0) + goto fail; if (!obs_view_init(&data->main_view)) goto fail; @@ -613,6 +617,8 @@ static void obs_free_data(void) pthread_mutex_destroy(&data->outputs_mutex); pthread_mutex_destroy(&data->encoders_mutex); pthread_mutex_destroy(&data->services_mutex); + pthread_mutex_destroy(&data->draw_callbacks_mutex); + da_free(data->draw_callbacks); } static const char *obs_signals[] = { @@ -818,9 +824,6 @@ void obs_shutdown(void) } while (false) FREE_REGISTERED_TYPES(obs_source_info, obs->source_types); - FREE_REGISTERED_TYPES(obs_source_info, obs->input_types); - FREE_REGISTERED_TYPES(obs_source_info, obs->filter_types); - FREE_REGISTERED_TYPES(obs_source_info, obs->transition_types); FREE_REGISTERED_TYPES(obs_output_info, obs->output_types); FREE_REGISTERED_TYPES(obs_encoder_info, obs->encoder_types); FREE_REGISTERED_TYPES(obs_service_info, obs->service_types); @@ -829,6 +832,10 @@ void obs_shutdown(void) #undef FREE_REGISTERED_TYPES + da_free(obs->input_types); + da_free(obs->filter_types); + da_free(obs->transition_types); + stop_video(); stop_hotkeys(); @@ -1007,28 +1014,11 @@ bool obs_reset_audio(const struct obs_audio_info *oai) bool obs_get_video_info(struct obs_video_info *ovi) { struct obs_core_video *video = &obs->video; - const struct video_output_info *info; if (!obs || !video->graphics) return false; - info = video_output_get_info(video->video); - if (!info) - return false; - - memset(ovi, 0, sizeof(struct obs_video_info)); - ovi->base_width = video->base_width; - ovi->base_height = video->base_height; - ovi->gpu_conversion= video->gpu_conversion; - ovi->scale_type = video->scale_type; - ovi->colorspace = info->colorspace; - ovi->range = info->range; - ovi->output_width = info->width; - ovi->output_height = info->height; - ovi->output_format = info->format; - ovi->fps_num = info->fps_num; - ovi->fps_den = info->fps_den; - + *ovi = video->ovi; return true; } @@ -1866,6 +1856,11 @@ double obs_get_active_fps(void) return obs ? obs->video.video_fps : 0.0; } +uint64_t obs_get_average_frame_time_ns(void) +{ + return obs ? obs->video.video_avg_frame_time_ns : 0; +} + enum obs_obj_type obs_obj_get_type(void *obj) { struct obs_context_data *context = obj; @@ -1941,3 +1936,41 @@ void obs_get_audio_monitoring_device(const char **name, const char **id) if (id) *id = obs->audio.monitoring_device_id; } + +void obs_add_main_render_callback( + void (*draw)(void *param, uint32_t cx, uint32_t cy), + void *param) +{ + if (!obs) + return; + + struct draw_callback data = {draw, param}; + + pthread_mutex_lock(&obs->data.draw_callbacks_mutex); + da_push_back(obs->data.draw_callbacks, &data); + pthread_mutex_unlock(&obs->data.draw_callbacks_mutex); +} + +void obs_remove_main_render_callback( + void (*draw)(void *param, uint32_t cx, uint32_t cy), + void *param) +{ + if (!obs) + return; + + struct draw_callback data = {draw, param}; + + pthread_mutex_lock(&obs->data.draw_callbacks_mutex); + da_erase_item(obs->data.draw_callbacks, &data); + pthread_mutex_unlock(&obs->data.draw_callbacks_mutex); +} + +uint32_t obs_get_total_frames(void) +{ + return obs ? obs->video.total_frames : 0; +} + +uint32_t obs_get_lagged_frames(void) +{ + return obs ? obs->video.lagged_frames : 0; +} diff --git a/libobs/obs.h b/libobs/obs.h index cf9cd36..f9a5540 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -279,15 +279,15 @@ EXPORT const char *obs_get_locale(void); EXPORT profiler_name_store_t *obs_get_profiler_name_store(void); /** - * Sets base video ouput base resolution/fps/format. + * Sets base video output base resolution/fps/format. * - * @note This data cannot be changed if an output is corrently active. + * @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 if sucessful + * @return OBS_VIDEO_SUCCESS if successful * OBS_VIDEO_NOT_SUPPORTED if the adapter lacks capabilities * OBS_VIDEO_INVALID_PARAM if a parameter is invalid * OBS_VIDEO_CURRENTLY_ACTIVE if video is currently active @@ -335,7 +335,7 @@ EXPORT int obs_open_module(obs_module_t **module, const char *path, /** * Initializes the module, which calls its obs_module_load export. If the - * module is alrady loaded, then this function does nothing and returns + * module is already loaded, then this function does nothing and returns * successful. */ EXPORT bool obs_init_module(obs_module_t *module); @@ -591,6 +591,13 @@ EXPORT void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, EXPORT bool obs_set_audio_monitoring_device(const char *name, const char *id); EXPORT void obs_get_audio_monitoring_device(const char **name, const char **id); +EXPORT void obs_add_main_render_callback( + void (*draw)(void *param, uint32_t cx, uint32_t cy), + void *param); +EXPORT void obs_remove_main_render_callback( + void (*draw)(void *param, uint32_t cx, uint32_t cy), + void *param); + /* ------------------------------------------------------------------------- */ /* View context */ @@ -620,6 +627,10 @@ 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); /* ------------------------------------------------------------------------- */ @@ -760,6 +771,9 @@ EXPORT obs_source_t *obs_filter_get_parent(const obs_source_t *filter); */ EXPORT obs_source_t *obs_filter_get_target(const obs_source_t *filter); +/** Used to directly render a non-async source without any filter processing */ +EXPORT void obs_source_default_render(obs_source_t *source); + /** Adds a filter to the source (which is used whenever the source is used) */ EXPORT void obs_source_filter_add(obs_source_t *source, obs_source_t *filter); @@ -823,8 +837,8 @@ EXPORT bool obs_source_active(const obs_source_t *source); */ EXPORT bool obs_source_showing(const obs_source_t *source); -/** Specifies that async video frames should be played as soon as possible */ -#define OBS_SOURCE_FLAG_UNBUFFERED (1<<0) +/** Unused flag */ +#define OBS_SOURCE_FLAG_UNUSED_1 (1<<0) /** Specifies to force audio to mono */ #define OBS_SOURCE_FLAG_FORCE_MONO (1<<1) @@ -869,6 +883,8 @@ EXPORT void obs_source_enum_filters(obs_source_t *source, EXPORT obs_source_t *obs_source_get_filter_by_name(obs_source_t *source, const char *name); +EXPORT void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src); + EXPORT bool obs_source_enabled(const obs_source_t *source); EXPORT void obs_source_set_enabled(obs_source_t *source, bool enabled); @@ -974,6 +990,13 @@ EXPORT void obs_source_draw(gs_texture_t *image, int x, int y, EXPORT void obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame); +/** Preloads asynchronous video data to allow instantaneous playback */ +EXPORT void obs_source_preload_video(obs_source_t *source, + const struct obs_source_frame *frame); + +/** Shows any preloaded video data */ +EXPORT void obs_source_show_preloaded_video(obs_source_t *source); + /** Outputs audio data (always asynchronous) */ EXPORT void obs_source_output_audio(obs_source_t *source, const struct obs_source_audio *audio); @@ -1080,6 +1103,10 @@ EXPORT uint64_t obs_source_get_audio_timestamp(const obs_source_t *source); EXPORT void obs_source_get_audio_mix(const obs_source_t *source, struct obs_source_audio_mix *audio); +EXPORT void obs_source_set_async_unbuffered(obs_source_t *source, + bool unbuffered); +EXPORT bool obs_source_async_unbuffered(const obs_source_t *source); + /* ------------------------------------------------------------------------- */ /* Transition-specific functions */ enum obs_transition_target { @@ -1162,7 +1189,7 @@ EXPORT void obs_transition_swap_end(obs_source_t *tr_dest, * Creates a scene. * * A scene is a source which is a container of other sources with specific - * display oriantations. Scenes can also be used like any other source. + * display orientations. Scenes can also be used like any other source. */ EXPORT obs_scene_t *obs_scene_create(const char *name); @@ -1194,6 +1221,9 @@ EXPORT obs_scene_t *obs_scene_from_source(const obs_source_t *source); EXPORT obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name); +EXPORT obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, + int64_t id); + /** Enumerates sources within a scene */ EXPORT void obs_scene_enum_items(obs_scene_t *scene, bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), @@ -1224,7 +1254,7 @@ EXPORT obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item); EXPORT void obs_sceneitem_select(obs_sceneitem_t *item, bool select); EXPORT bool obs_sceneitem_selected(const obs_sceneitem_t *item); -/* Functions for gettings/setting specific orientation of a scene item */ +/* Functions for getting/setting specific orientation of a scene item */ EXPORT void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos); EXPORT void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg); EXPORT void obs_sceneitem_set_scale(obs_sceneitem_t *item, @@ -1242,6 +1272,8 @@ EXPORT void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item, EXPORT void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds); +EXPORT int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item); + EXPORT void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos); EXPORT float obs_sceneitem_get_rot(const obs_sceneitem_t *item); @@ -1388,12 +1420,6 @@ EXPORT signal_handler_t *obs_output_get_signal_handler( /** Returns the procedure handler for an output */ EXPORT proc_handler_t *obs_output_get_proc_handler(const obs_output_t *output); -/** - * Sets the current video media context associated with this output, - * required for non-encoded outputs - */ -EXPORT void obs_output_set_video(obs_output_t *output, video_t *video); - /** * Sets the current audio/video media contexts associated with this output, * required for non-encoded outputs. Can be null. @@ -1486,6 +1512,13 @@ EXPORT void obs_output_output_caption_text1(obs_output_t *output, #endif EXPORT float obs_output_get_congestion(obs_output_t *output); +EXPORT int obs_output_get_connect_time_ms(obs_output_t *output); + +EXPORT bool obs_output_reconnecting(const obs_output_t *output); + +/** Pass a string of the last output error, for UI use */ +EXPORT void obs_output_set_last_error(obs_output_t *output, + const char *message); /* ------------------------------------------------------------------------- */ /* Functions used by outputs */ diff --git a/libobs/util/base.h b/libobs/util/base.h index c528fee..7d64b45 100644 --- a/libobs/util/base.h +++ b/libobs/util/base.h @@ -49,13 +49,13 @@ enum { * Use if a problem occurs that doesn't affect the program and is * recoverable. * - * Use in places where where failure isn't entirely unexpected, and can + * Use in places where failure isn't entirely unexpected, and can * be handled safely. */ LOG_WARNING = 200, /** - * Informative essage to be displayed in the log. + * Informative message to be displayed in the log. */ LOG_INFO = 300, diff --git a/libobs/util/c99defs.h b/libobs/util/c99defs.h index 43d52c8..c4ee627 100644 --- a/libobs/util/c99defs.h +++ b/libobs/util/c99defs.h @@ -39,7 +39,7 @@ * incredibly inept moron could possibly be managing the visual C compiler * project. They should be fired, and legally forbidden to have a job in * ANYTHING even REMOTELY related to programming. FOREVER. This should also - * apply to the next 10 generations all of their descendents. */ + * apply to the next 10 generations all of their descendants. */ #ifndef __cplusplus #define inline __inline #endif diff --git a/libobs/util/cf-lexer.c b/libobs/util/cf-lexer.c index b62308c..8258582 100644 --- a/libobs/util/cf-lexer.c +++ b/libobs/util/cf-lexer.c @@ -123,6 +123,7 @@ static bool cf_is_token_break(struct base_token *start_token, start_token->type = BASETOKEN_DIGIT; break; } + /* Falls through. */ case BASETOKEN_NONE: return true; diff --git a/libobs/util/cf-lexer.h b/libobs/util/cf-lexer.h index 66de27f..d33a94c 100644 --- a/libobs/util/cf-lexer.h +++ b/libobs/util/cf-lexer.h @@ -162,7 +162,7 @@ static inline void cf_def_free(struct cf_def *cfd) * + option to exclude features such as #import, variadic macros, and other * features for certain language implementations * + macro parameter string operator # - * + macro parameter token concactenation operator ## + * + macro parameter token concatenation operator ## * + predefined macros * + restricted macros */ diff --git a/libobs/util/config-file.c b/libobs/util/config-file.c index 98d8807..226b65c 100644 --- a/libobs/util/config-file.c +++ b/libobs/util/config-file.c @@ -445,14 +445,10 @@ int config_save_safe(config_t *config, const char *temp_ext, if (*backup_ext != '.') dstr_cat(&backup_file, "."); dstr_cat(&backup_file, backup_ext); - - os_unlink(backup_file.array); - os_rename(file, backup_file.array); - } else { - os_unlink(file); } - os_rename(temp_file.array, file); + if (os_safe_replace(file, temp_file.array, backup_file.array) != 0) + ret = CONFIG_ERROR; cleanup: pthread_mutex_unlock(&config->mutex); diff --git a/libobs/util/config-file.h b/libobs/util/config-file.h index c8bc31a..7c61b6a 100644 --- a/libobs/util/config-file.h +++ b/libobs/util/config-file.h @@ -88,7 +88,7 @@ EXPORT bool config_remove_value(config_t *config, const char *section, * These do *not* actually set any values, they only set what values will be * returned for config_get_* if the specified variable does not exist. * - * You can initialize the defaults programmitically using config_set_default_* + * You can initialize the defaults programmatically using config_set_default_* * functions (recommended for most cases), or you can initialize it via a file * with config_open_defaults. */ diff --git a/libobs/util/darray.h b/libobs/util/darray.h index e448bcf..99f35d6 100644 --- a/libobs/util/darray.h +++ b/libobs/util/darray.h @@ -33,7 +33,7 @@ extern "C" { * NOTE: Not type-safe when using directly. * Specifying size per call with inline maximizes compiler optimizations * - * See DARRAY macro at the bottom of thhe file for slightly safer usage. + * See DARRAY macro at the bottom of the file for slightly safer usage. */ #define DARRAY_INVALID ((size_t)-1) @@ -438,7 +438,7 @@ static inline void darray_swap(const size_t element_size, * Makes it a little easier to use as well. * * I did -not- want to use a gigantic macro to generate a crapload of - * typsafe inline functions per type. It just feels like a mess to me. + * typesafe inline functions per type. It just feels like a mess to me. */ #define DARRAY(type) \ diff --git a/libobs/util/dstr.c b/libobs/util/dstr.c index 73cf052..ac56be7 100644 --- a/libobs/util/dstr.c +++ b/libobs/util/dstr.c @@ -197,6 +197,11 @@ wchar_t *wstrstri(const wchar_t *str, const wchar_t *find) return NULL; } +static inline bool is_padding(char ch) +{ + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; +} + char *strdepad(char *str) { char *temp; @@ -210,7 +215,7 @@ char *strdepad(char *str) temp = str; /* remove preceding spaces/tabs */ - while (*temp == ' ' || *temp == '\t') + while (is_padding(*temp)) ++temp; len = strlen(str); @@ -219,7 +224,7 @@ char *strdepad(char *str) if (len) { temp = str + (len-1); - while (*temp == ' ' || *temp == '\t') + while (is_padding(*temp)) *(temp--) = 0; } diff --git a/libobs/util/platform-cocoa.m b/libobs/util/platform-cocoa.m index 29076d6..b9f392c 100644 --- a/libobs/util/platform-cocoa.m +++ b/libobs/util/platform-cocoa.m @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -312,3 +314,41 @@ void os_inhibit_sleep_destroy(os_inhibit_t *info) bfree(info); } } + +static int physical_cores = 0; +static int logical_cores = 0; +static bool core_count_initialized = false; + +static void os_get_cores_internal(void) +{ + if (core_count_initialized) + return; + + core_count_initialized = true; + + size_t size; + int ret; + + size = sizeof(physical_cores); + ret = sysctlbyname("machdep.cpu.core_count", &physical_cores, + &size, NULL, 0); + if (ret != 0) + return; + + ret = sysctlbyname("machdep.cpu.thread_count", &logical_cores, + &size, NULL, 0); +} + +int os_get_physical_cores(void) +{ + if (!core_count_initialized) + os_get_cores_internal(); + return physical_cores; +} + +int os_get_logical_cores(void) +{ + if (!core_count_initialized) + os_get_cores_internal(); + return logical_cores; +} diff --git a/libobs/util/platform-nix.c b/libobs/util/platform-nix.c index 6c7b4ba..26f606d 100644 --- a/libobs/util/platform-nix.c +++ b/libobs/util/platform-nix.c @@ -432,6 +432,13 @@ int os_rename(const char *old_path, const char *new_path) return rename(old_path, new_path); } +int os_safe_replace(const char *target, const char *from, const char *backup) +{ + if (backup && os_file_exists(target) && rename(target, backup) != 0) + return -1; + return rename(from, target); +} + #if !defined(__APPLE__) os_performance_token_t *os_request_high_performance(const char *reason) { @@ -615,3 +622,69 @@ void os_breakpoint() { raise(SIGTRAP); } + +#ifndef __APPLE__ +static int physical_cores = 0; +static int logical_cores = 0; +static bool core_count_initialized = false; + +/* return sysconf(_SC_NPROCESSORS_ONLN); */ + +static void os_get_cores_internal(void) +{ + if (core_count_initialized) + return; + + core_count_initialized = true; + + logical_cores = sysconf(_SC_NPROCESSORS_ONLN); + +#ifndef __linux__ + physical_cores = logical_cores; +#else + char *text = os_quick_read_utf8_file("/proc/cpuinfo"); + char *core_id = text; + + if (!text || !*text) { + physical_cores = logical_cores; + return; + } + + for (;;) { + core_id = strstr(core_id, "\ncore id"); + if (!core_id) + break; + physical_cores++; + core_id++; + } + + if (physical_cores == 0) + physical_cores = logical_cores; + + bfree(text); +#endif +} + +int os_get_physical_cores(void) +{ + if (!core_count_initialized) + os_get_cores_internal(); + return physical_cores; +} + +int os_get_logical_cores(void) +{ + if (!core_count_initialized) + os_get_cores_internal(); + return logical_cores; +} +#endif + +uint64_t os_get_free_disk_space(const char *dir) +{ + struct statvfs info; + if (statvfs(dir, &info) != 0) + return 0; + + return (uint64_t)info.f_frsize * (uint64_t)info.f_bavail; +} diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 74c9fa9..78ab492 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -549,7 +549,8 @@ int os_rename(const char *old_path, const char *new_path) goto error; } - code = MoveFileW(old_path_utf16, new_path_utf16) ? 0 : -1; + code = MoveFileExW(old_path_utf16, new_path_utf16, + MOVEFILE_REPLACE_EXISTING) ? 0 : -1; error: bfree(old_path_utf16); @@ -557,6 +558,36 @@ error: return code; } +int os_safe_replace(const char *target, const char *from, const char *backup) +{ + wchar_t *wtarget = NULL; + wchar_t *wfrom = NULL; + wchar_t *wbackup = NULL; + int code = -1; + + if (!target || !from) + return -1; + if (!os_utf8_to_wcs_ptr(target, 0, &wtarget)) + return -1; + if (!os_utf8_to_wcs_ptr(from, 0, &wfrom)) + goto fail; + if (backup && !os_utf8_to_wcs_ptr(backup, 0, &wbackup)) + goto fail; + + if (ReplaceFileW(wtarget, wfrom, wbackup, 0, NULL, NULL)) { + code = 0; + } else if (GetLastError() == ERROR_FILE_NOT_FOUND) { + code = MoveFileExW(wfrom, wtarget, MOVEFILE_REPLACE_EXISTING) + ? 0 : -1; + } + +fail: + bfree(wtarget); + bfree(wfrom); + bfree(wbackup); + return code; +} + BOOL WINAPI DllMain(HINSTANCE hinst_dll, DWORD reason, LPVOID reserved) { switch (reason) { @@ -848,3 +879,83 @@ void os_breakpoint(void) { __debugbreak(); } + +DWORD num_logical_cores(ULONG_PTR mask) +{ + DWORD left_shift = sizeof(ULONG_PTR) * 8 - 1; + DWORD bit_set_count = 0; + ULONG_PTR bit_test = (ULONG_PTR)1 << left_shift; + + for (DWORD i = 0; i <= left_shift; ++i) { + bit_set_count += ((mask & bit_test) ? 1 : 0); + bit_test /= 2; + } + + return bit_set_count; +} + +static int physical_cores = 0; +static int logical_cores = 0; +static bool core_count_initialized = false; + +static void os_get_cores_internal(void) +{ + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = NULL, temp = NULL; + DWORD len = 0; + + if (core_count_initialized) + return; + + core_count_initialized = true; + + GetLogicalProcessorInformation(info, &len); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return; + + info = malloc(len); + + if (GetLogicalProcessorInformation(info, &len)) { + DWORD num = len / sizeof(*info); + temp = info; + + for (DWORD i = 0; i < num; i++) { + if (temp->Relationship == RelationProcessorCore) { + ULONG_PTR mask = temp->ProcessorMask; + + physical_cores++; + logical_cores += num_logical_cores(mask); + } + + temp++; + } + } + + free(info); +} + +int os_get_physical_cores(void) +{ + if (!core_count_initialized) + os_get_cores_internal(); + return physical_cores; +} + +int os_get_logical_cores(void) +{ + if (!core_count_initialized) + os_get_cores_internal(); + return logical_cores; +} + +uint64_t os_get_free_disk_space(const char *dir) +{ + wchar_t *wdir = NULL; + if (!os_utf8_to_wcs_ptr(dir, 0, &wdir)) + return 0; + + ULARGE_INTEGER free; + bool success = !!GetDiskFreeSpaceExW(wdir, &free, NULL, NULL); + bfree(wdir); + + return success ? free.QuadPart : 0; +} diff --git a/libobs/util/platform.c b/libobs/util/platform.c index cfed355..008cbbb 100644 --- a/libobs/util/platform.c +++ b/libobs/util/platform.c @@ -248,6 +248,7 @@ bool os_quick_write_mbs_file(const char *path, const char *str, size_t len) if (mbs_len) fwrite(mbs, 1, mbs_len, f); bfree(mbs); + fflush(f); fclose(f); return true; @@ -264,6 +265,7 @@ bool os_quick_write_utf8_file(const char *path, const char *str, size_t len, fwrite("\xEF\xBB\xBF", 1, 3, f); if (len) fwrite(str, 1, len, f); + fflush(f); fclose(f); return true; @@ -297,17 +299,10 @@ bool os_quick_write_utf8_file_safe(const char *path, const char *str, if (*backup_ext != '.') dstr_cat(&backup_path, "."); dstr_cat(&backup_path, backup_ext); - - os_unlink(backup_path.array); - os_rename(path, backup_path.array); - - dstr_free(&backup_path); - } else { - os_unlink(path); } - os_rename(temp_path.array, path); - success = true; + if (os_safe_replace(path, temp_path.array, backup_path.array) == 0) + success = true; cleanup: dstr_free(&backup_path); diff --git a/libobs/util/platform.h b/libobs/util/platform.h index 77d0ea6..bf5e2cf 100644 --- a/libobs/util/platform.h +++ b/libobs/util/platform.h @@ -153,6 +153,8 @@ EXPORT int os_rmdir(const char *path); EXPORT char *os_getcwd(char *path, size_t size); EXPORT int os_chdir(const char *path); +EXPORT uint64_t os_get_free_disk_space(const char *dir); + #define MKDIR_EXISTS 1 #define MKDIR_SUCCESS 0 #define MKDIR_ERROR -1 @@ -161,6 +163,8 @@ EXPORT int os_mkdir(const char *path); EXPORT int os_mkdirs(const char *path); EXPORT int os_rename(const char *old_path, const char *new_path); EXPORT int os_copyfile(const char *file_in, const char *file_out); +EXPORT int os_safe_replace(const char *target_path, const char *from_path, + const char *backup_path); EXPORT char *os_generate_formatted_filename(const char *extension, bool space, const char *format); @@ -174,6 +178,9 @@ EXPORT void os_inhibit_sleep_destroy(os_inhibit_t *info); EXPORT void os_breakpoint(void); +EXPORT int os_get_physical_cores(void); +EXPORT int os_get_logical_cores(void); + #ifdef _MSC_VER #define strtoll _strtoi64 #if _MSC_VER < 1900 diff --git a/libobs/util/text-lookup.h b/libobs/util/text-lookup.h index 306e601..9531652 100644 --- a/libobs/util/text-lookup.h +++ b/libobs/util/text-lookup.h @@ -19,7 +19,7 @@ /* * Text Lookup interface * - * Used for storing and looking up localized strings. Stores locazation + * Used for storing and looking up localized strings. Stores localization * strings in a radix/trie tree to efficiently look up associated strings via a * unique string identifier name. */ @@ -30,7 +30,7 @@ extern "C" { #endif -/* opaque typdef */ +/* opaque typedef */ struct text_lookup; typedef struct text_lookup lookup_t; diff --git a/libobs/util/utf8.c b/libobs/util/utf8.c index 5153309..d0b5c34 100644 --- a/libobs/util/utf8.c +++ b/libobs/util/utf8.c @@ -362,7 +362,7 @@ size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out, /* * NOTE: do not check here for forbidden UTF-8 characters. - * They cannot appear here because we do proper convertion. + * They cannot appear here because we do proper conversion. */ p += n; diff --git a/libobs/util/util.hpp b/libobs/util/util.hpp index d7b3a6d..94c5823 100644 --- a/libobs/util/util.hpp +++ b/libobs/util/util.hpp @@ -46,6 +46,8 @@ public: inline bool operator!() {return ptr == NULL;} inline bool operator==(T p) {return ptr == p;} inline bool operator!=(T p) {return ptr != p;} + + inline T *Get() const {return ptr;} }; class ConfigFile { diff --git a/libobs/util/vc/vc_stdint.h b/libobs/util/vc/vc_stdint.h index bfc8f5a..3ae4832 100644 --- a/libobs/util/vc/vc_stdint.h +++ b/libobs/util/vc/vc_stdint.h @@ -172,7 +172,7 @@ typedef unsigned __int64 uintmax_t; /* 7.18.4.1 Macros for minimum-width integer constants - Accoding to Douglas Gwyn : + According to Douglas Gwyn : "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC 9899:1999 as initially published, the expansion was required to be an integer constant of precisely matching type, which diff --git a/plugins/coreaudio-encoder/data/locale/bn-BD.ini b/plugins/coreaudio-encoder/data/locale/bn-BD.ini new file mode 100644 index 0000000..f20d9da --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/bn-BD.ini @@ -0,0 +1,5 @@ +CoreAudioAAC="CoreAudio AAC এনকোডার" +Bitrate="বিটরেট" +OutputSamplerate="আউটপুট নমুনার হার" +UseInputSampleRate="ইনপুট (OBS) নমুনা রেট (অসমর্থিত bitrates তালিকা হতে পারে) ব্যবহার করুন" + diff --git a/plugins/decklink/audio-repack.c b/plugins/decklink/audio-repack.c new file mode 100644 index 0000000..f4bedfe --- /dev/null +++ b/plugins/decklink/audio-repack.c @@ -0,0 +1,110 @@ +#include "audio-repack.h" + +#include + +int check_buffer(struct audio_repack *repack, + uint32_t frame_count) +{ + const uint32_t new_size = frame_count * repack->base_dst_size + + repack->extra_dst_size; + + if (repack->packet_size < new_size) { + repack->packet_buffer = brealloc( + repack->packet_buffer, new_size); + if (!repack->packet_buffer) + return -1; + + repack->packet_size = new_size; + } + + return 0; +} + +/* + Swap channel between LFE and FC, and + squash data array + + | FL | FR |LFE | FC | BL | BR |emp |emp | + | | x | | + | FL | FR | FC |LFE | BL | BR | + */ +int repack_8to6ch_swap23(struct audio_repack *repack, + const uint8_t *bsrc, uint32_t frame_count) +{ + if (check_buffer(repack, frame_count) < 0) + return -1; + + const __m128i *src = (__m128i *)bsrc; + const __m128i *esrc = src + frame_count; + uint32_t *dst = (uint32_t *)repack->packet_buffer; + while (src != esrc) { + __m128i target = _mm_load_si128(src++); + __m128i buf = _mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0)); + _mm_storeu_si128((__m128i *)dst, buf); + dst += 3; + } + + return 0; +} + +/* + Swap channel between LFE and FC + + | FL | FR |LFE | FC | BL | BR |SBL |SBR | + | | x | | | | + | FL | FR | FC |LFE | BL | BR |SBL |SBR | + */ +int repack_8ch_swap23(struct audio_repack *repack, + const uint8_t *bsrc, uint32_t frame_count) +{ + if (check_buffer(repack, frame_count) < 0) + return -1; + + const __m128i *src = (__m128i *)bsrc; + const __m128i *esrc = src + frame_count; + __m128i *dst = (__m128i *)repack->packet_buffer; + while (src != esrc) { + __m128i target = _mm_load_si128(src++); + __m128i buf = _mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0)); + _mm_store_si128(dst++, buf); + } + + return 0; +} + +int audio_repack_init(struct audio_repack *repack, + audio_repack_mode_t repack_mode, uint8_t sample_bit) +{ + memset(repack, 0, sizeof(*repack)); + + if (sample_bit != 16) + return -1; + + switch (repack_mode) { + 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_8to6ch_swap23; + break; + + case repack_mode_8ch_swap23: + repack->base_src_size = 8 * (16 / 8); + repack->base_dst_size = 8 * (16 / 8); + repack->extra_dst_size = 0; + repack->repack_func = &repack_8ch_swap23; + break; + + default: return -1; + } + + return 0; +} + +void audio_repack_free(struct audio_repack *repack) +{ + if (repack->packet_buffer) + bfree(repack->packet_buffer); + + memset(repack, 0, sizeof(*repack)); +} diff --git a/plugins/decklink/audio-repack.h b/plugins/decklink/audio-repack.h new file mode 100644 index 0000000..b5116e7 --- /dev/null +++ b/plugins/decklink/audio-repack.h @@ -0,0 +1,41 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +struct audio_repack; + +typedef int (*audio_repack_func_t)(struct audio_repack *, + const uint8_t *, uint32_t); + +struct audio_repack { + uint8_t *packet_buffer; + uint32_t packet_size; + + uint32_t base_src_size; + uint32_t base_dst_size; + uint32_t extra_dst_size; + + audio_repack_func_t repack_func; +}; + +enum _audio_repack_mode { + repack_mode_8to6ch_swap23, + repack_mode_8ch_swap23, +}; + +typedef enum _audio_repack_mode audio_repack_mode_t; + +extern int audio_repack_init(struct audio_repack *repack, + audio_repack_mode_t repack_mode, uint8_t sample_bit); +extern void audio_repack_free(struct audio_repack *repack); + +#ifdef __cplusplus +} +#endif diff --git a/plugins/decklink/audio-repack.hpp b/plugins/decklink/audio-repack.hpp new file mode 100644 index 0000000..d495172 --- /dev/null +++ b/plugins/decklink/audio-repack.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "audio-repack.h" + +class AudioRepacker { + struct audio_repack arepack; + +public: + inline AudioRepacker(audio_repack_mode_t repack_mode) + { + audio_repack_init(&arepack, repack_mode, 16); + } + inline ~AudioRepacker() + { + audio_repack_free(&arepack); + } + + inline int repack(const uint8_t *src, uint32_t frame_size) + { + return (*arepack.repack_func)(&arepack, src, frame_size); + } + + inline operator struct audio_repack*() {return &arepack;} + inline struct audio_repack *operator->() {return &arepack;} +}; diff --git a/plugins/decklink/data/locale/bn-BD.ini b/plugins/decklink/data/locale/bn-BD.ini new file mode 100644 index 0000000..f13caa4 --- /dev/null +++ b/plugins/decklink/data/locale/bn-BD.ini @@ -0,0 +1,4 @@ +BlackmagicDevice="Blackmagic যন্ত্র" +Device="ডিভাইস" +PixelFormat="পিক্সেল বিন্যাস" + diff --git a/plugins/decklink/data/locale/ca-ES.ini b/plugins/decklink/data/locale/ca-ES.ini index 70e3732..38f07f8 100644 --- a/plugins/decklink/data/locale/ca-ES.ini +++ b/plugins/decklink/data/locale/ca-ES.ini @@ -3,4 +3,10 @@ Device="Dispositiu" Mode="Mode" Buffering="Usa memòria intermèdia" PixelFormat="Format de píxel" +ChannelFormat="Canal" +ChannelFormat.None="Cap" +ChannelFormat.2_0ch="Estèreo " +ChannelFormat.5_1ch="5.1" +ChannelFormat.5_1chBack="5.1 (posterior)" +ChannelFormat.7_1ch="7.1" diff --git a/plugins/decklink/data/locale/cs-CZ.ini b/plugins/decklink/data/locale/cs-CZ.ini index 519282f..ae74b40 100644 --- a/plugins/decklink/data/locale/cs-CZ.ini +++ b/plugins/decklink/data/locale/cs-CZ.ini @@ -3,4 +3,10 @@ Device="Zařízení" Mode="Mód" Buffering="Použít vyrovnávací paměť" PixelFormat="Formát pixelů" +ChannelFormat="Kanály" +ChannelFormat.None="Žádný" +ChannelFormat.2_0ch="Stereo" +ChannelFormat.5_1ch="5.1" +ChannelFormat.5_1chBack="5.1 (zadní)" +ChannelFormat.7_1ch="7.1" diff --git a/plugins/decklink/data/locale/da-DK.ini b/plugins/decklink/data/locale/da-DK.ini index 1ebc715..66d9480 100644 --- a/plugins/decklink/data/locale/da-DK.ini +++ b/plugins/decklink/data/locale/da-DK.ini @@ -1,6 +1,12 @@ -BlackmagicDevice="Blackmagic enhed" +BlackmagicDevice="Blackmagic-enhed" Device="Enhed" Mode="Tilstand" Buffering="Brug buffering" PixelFormat="Pixel format" +ChannelFormat="Kanal" +ChannelFormat.None="Intet" +ChannelFormat.2_0ch="2kan" +ChannelFormat.5_1ch="5.1kan" +ChannelFormat.5_1chBack="5.1kan (bag)" +ChannelFormat.7_1ch="7.1kan" diff --git a/plugins/decklink/data/locale/de-DE.ini b/plugins/decklink/data/locale/de-DE.ini index b0ccc1e..d9e7f2b 100644 --- a/plugins/decklink/data/locale/de-DE.ini +++ b/plugins/decklink/data/locale/de-DE.ini @@ -3,4 +3,10 @@ Device="Gerät" Mode="Modus" Buffering="Buffering benutzen" PixelFormat="Pixelformat" +ChannelFormat="Kanal" +ChannelFormat.None="Keins" +ChannelFormat.2_0ch="2 Kanal" +ChannelFormat.5_1ch="5.1 Kanal" +ChannelFormat.5_1chBack="5.1 Kanal (hinten)" +ChannelFormat.7_1ch="7.1 Kanal" diff --git a/plugins/decklink/data/locale/en-US.ini b/plugins/decklink/data/locale/en-US.ini index e19dea4..202070d 100644 --- a/plugins/decklink/data/locale/en-US.ini +++ b/plugins/decklink/data/locale/en-US.ini @@ -3,3 +3,9 @@ Device="Device" Mode="Mode" Buffering="Use Buffering" PixelFormat="Pixel Format" +ChannelFormat="Channel" +ChannelFormat.None="None" +ChannelFormat.2_0ch="2ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.5_1chBack="5.1ch (Back)" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/es-ES.ini b/plugins/decklink/data/locale/es-ES.ini index 4639b2b..abbe8d6 100644 --- a/plugins/decklink/data/locale/es-ES.ini +++ b/plugins/decklink/data/locale/es-ES.ini @@ -3,4 +3,10 @@ Device="Dispositivo" Mode="Modo" Buffering="Utilizar el almacenamiento en búfer" PixelFormat="Formato de píxel" +ChannelFormat="Canal" +ChannelFormat.None="Ninguno" +ChannelFormat.2_0ch="Estéreo " +ChannelFormat.5_1ch="5.1" +ChannelFormat.5_1chBack="5.1 (posterior)" +ChannelFormat.7_1ch="7.1" diff --git a/plugins/decklink/data/locale/eu-ES.ini b/plugins/decklink/data/locale/eu-ES.ini index 0e2c79d..f1883a1 100644 --- a/plugins/decklink/data/locale/eu-ES.ini +++ b/plugins/decklink/data/locale/eu-ES.ini @@ -3,4 +3,10 @@ Device="Gailua" Mode="Modua" Buffering="Erabili Bufferreratzea" PixelFormat="Pixel formatua" +ChannelFormat="Kanala" +ChannelFormat.None="Ezer ez" +ChannelFormat.2_0ch="2k" +ChannelFormat.5_1ch="5.1k" +ChannelFormat.5_1chBack="5.1k (Atzera)" +ChannelFormat.7_1ch="7.1k" diff --git a/plugins/decklink/data/locale/fi-FI.ini b/plugins/decklink/data/locale/fi-FI.ini index 120ffe2..c1061e6 100644 --- a/plugins/decklink/data/locale/fi-FI.ini +++ b/plugins/decklink/data/locale/fi-FI.ini @@ -3,4 +3,10 @@ Device="Laite" Mode="Tila" Buffering="Käytä puskurointia" PixelFormat="Pikselimuoto" +ChannelFormat="Kanava" +ChannelFormat.None="Ei mitään" +ChannelFormat.2_0ch="2ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.5_1chBack="5.1ch (Taka)" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/fr-FR.ini b/plugins/decklink/data/locale/fr-FR.ini index a6f1d20..b356b1b 100644 --- a/plugins/decklink/data/locale/fr-FR.ini +++ b/plugins/decklink/data/locale/fr-FR.ini @@ -3,4 +3,10 @@ Device="Périphérique" Mode="Mode" Buffering="Utiliser la mise en mémoire tampon" PixelFormat="Format de pixel" +ChannelFormat="Canaux audio" +ChannelFormat.None="Aucun" +ChannelFormat.2_0ch="canal 2" +ChannelFormat.5_1ch="canal 5.1" +ChannelFormat.5_1chBack="canal (arrière) 5.1" +ChannelFormat.7_1ch="canal 7.1" diff --git a/plugins/decklink/data/locale/hu-HU.ini b/plugins/decklink/data/locale/hu-HU.ini index 18bf1bf..a81c7b0 100644 --- a/plugins/decklink/data/locale/hu-HU.ini +++ b/plugins/decklink/data/locale/hu-HU.ini @@ -3,4 +3,10 @@ Device="Eszköz" Mode="Mód" Buffering="Pufferelés használata" PixelFormat="Képpont formátum" +ChannelFormat="Csatorna" +ChannelFormat.None="Nincs" +ChannelFormat.2_0ch="2cs" +ChannelFormat.5_1ch="5.1cs" +ChannelFormat.5_1chBack="5.1cs (Hátsó)" +ChannelFormat.7_1ch="7.1cs" diff --git a/plugins/decklink/data/locale/it-IT.ini b/plugins/decklink/data/locale/it-IT.ini index 37fde86..1f794d9 100644 --- a/plugins/decklink/data/locale/it-IT.ini +++ b/plugins/decklink/data/locale/it-IT.ini @@ -1,6 +1,12 @@ BlackmagicDevice="Blackmagic Device" Device="Dispositivo" Mode="Modalità" -Buffering="Usa Buffer" -PixelFormat="Formato Pixel" +Buffering="Usa buffer" +PixelFormat="Formato pixel" +ChannelFormat="Canale" +ChannelFormat.None="Nessuno" +ChannelFormat.2_0ch="2 canali" +ChannelFormat.5_1ch="5.1 canali" +ChannelFormat.5_1chBack="5.1 canali (retro)" +ChannelFormat.7_1ch="7.1 canali" diff --git a/plugins/decklink/data/locale/ja-JP.ini b/plugins/decklink/data/locale/ja-JP.ini index 1ab0f66..82c05b9 100644 --- a/plugins/decklink/data/locale/ja-JP.ini +++ b/plugins/decklink/data/locale/ja-JP.ini @@ -3,4 +3,10 @@ Device="デバイス" Mode="モード" Buffering="バッファリングを使用する" PixelFormat="ピクセルフォーマット" +ChannelFormat="チャンネル" +ChannelFormat.None="未設定" +ChannelFormat.2_0ch="2ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.5_1chBack="5.1ch (背部)" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/ko-KR.ini b/plugins/decklink/data/locale/ko-KR.ini index 7247470..44e842c 100644 --- a/plugins/decklink/data/locale/ko-KR.ini +++ b/plugins/decklink/data/locale/ko-KR.ini @@ -3,4 +3,10 @@ Device="장치" Mode="방식" Buffering="버퍼링 사용" PixelFormat="픽셀 형식" +ChannelFormat="채널" +ChannelFormat.None="없음" +ChannelFormat.2_0ch="2채널" +ChannelFormat.5_1ch="5.1채널" +ChannelFormat.5_1chBack="5.1채널 (후면)" +ChannelFormat.7_1ch="7.1채널" diff --git a/plugins/decklink/data/locale/nl-NL.ini b/plugins/decklink/data/locale/nl-NL.ini index ff35c27..a430c05 100644 --- a/plugins/decklink/data/locale/nl-NL.ini +++ b/plugins/decklink/data/locale/nl-NL.ini @@ -3,4 +3,10 @@ Device="Apparaat" Mode="Modus" Buffering="Buffering Gebruiken" PixelFormat="Pixelindeling" +ChannelFormat="Kanaal" +ChannelFormat.None="Geen" +ChannelFormat.2_0ch="2ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.5_1chBack="5.1ch (Achter)" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/pl-PL.ini b/plugins/decklink/data/locale/pl-PL.ini index c14df90..eeb8ef7 100644 --- a/plugins/decklink/data/locale/pl-PL.ini +++ b/plugins/decklink/data/locale/pl-PL.ini @@ -3,4 +3,10 @@ Device="Urządzenie" Mode="Tryb" Buffering="Użyj buforowania" PixelFormat="Format pikseli" +ChannelFormat="Kanały" +ChannelFormat.None="Brak" +ChannelFormat.2_0ch="2.0" +ChannelFormat.5_1ch="5.1" +ChannelFormat.5_1chBack="5.1 (tylne)" +ChannelFormat.7_1ch="7.1" diff --git a/plugins/decklink/data/locale/pt-BR.ini b/plugins/decklink/data/locale/pt-BR.ini index b17a47d..e42a738 100644 --- a/plugins/decklink/data/locale/pt-BR.ini +++ b/plugins/decklink/data/locale/pt-BR.ini @@ -3,4 +3,10 @@ Device="Dispositivo" Mode="Modo" Buffering="Utilizar Buffering" PixelFormat="Formato de Pixel" +ChannelFormat="Canal" +ChannelFormat.None="Nenhum" +ChannelFormat.2_0ch="2.0" +ChannelFormat.5_1ch="5.1" +ChannelFormat.5_1chBack="5.1 (Traseiro)" +ChannelFormat.7_1ch="7.1" diff --git a/plugins/decklink/data/locale/ru-RU.ini b/plugins/decklink/data/locale/ru-RU.ini index 03e0026..aeed3b2 100644 --- a/plugins/decklink/data/locale/ru-RU.ini +++ b/plugins/decklink/data/locale/ru-RU.ini @@ -3,4 +3,10 @@ Device="Устройство" Mode="Режим" Buffering="Использовать буферизацию" PixelFormat="Формат пикселей" +ChannelFormat="Конфигурация каналов" +ChannelFormat.None="Нет" +ChannelFormat.2_0ch="2-канальный" +ChannelFormat.5_1ch="5.1-канальный" +ChannelFormat.5_1chBack="5.1-канальный (Тыловой)" +ChannelFormat.7_1ch="7.1-канальный" diff --git a/plugins/decklink/data/locale/sv-SE.ini b/plugins/decklink/data/locale/sv-SE.ini index acd4e1b..738984f 100644 --- a/plugins/decklink/data/locale/sv-SE.ini +++ b/plugins/decklink/data/locale/sv-SE.ini @@ -3,4 +3,10 @@ Device="Enhet" Mode="Läge" Buffering="Använd buffert" PixelFormat="Bildpunktsformat" +ChannelFormat="Kanal" +ChannelFormat.None="Ingen" +ChannelFormat.2_0ch="2ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.5_1chBack="5.1ch (bakom)" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/tr-TR.ini b/plugins/decklink/data/locale/tr-TR.ini index 4520346..95c5452 100644 --- a/plugins/decklink/data/locale/tr-TR.ini +++ b/plugins/decklink/data/locale/tr-TR.ini @@ -3,4 +3,10 @@ Device="Aygıt" Mode="Mod" Buffering="Arabelleği Kullan" PixelFormat="Piksel Biçimi" +ChannelFormat="Kanal" +ChannelFormat.None="Hiçbiri" +ChannelFormat.2_0ch="2ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.5_1chBack="5.1ch (Arka)" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/uk-UA.ini b/plugins/decklink/data/locale/uk-UA.ini index 0a8a8bd..7664abc 100644 --- a/plugins/decklink/data/locale/uk-UA.ini +++ b/plugins/decklink/data/locale/uk-UA.ini @@ -3,4 +3,10 @@ Device="Пристрій" Mode="Режим" Buffering="Увімкнути буферизацію" PixelFormat="Формат пікселів" +ChannelFormat="Звук (канали)" +ChannelFormat.None="Немає" +ChannelFormat.2_0ch="2-канальний" +ChannelFormat.5_1ch="5.1-канальний" +ChannelFormat.5_1chBack="5.1-канальний (Back)" +ChannelFormat.7_1ch="7.1-канальний" diff --git a/plugins/decklink/data/locale/zh-CN.ini b/plugins/decklink/data/locale/zh-CN.ini index d3edc95..e0c5cdb 100644 --- a/plugins/decklink/data/locale/zh-CN.ini +++ b/plugins/decklink/data/locale/zh-CN.ini @@ -3,4 +3,10 @@ Device="设备" Mode="模式" Buffering="使用缓冲" PixelFormat="像素格式" +ChannelFormat="频道" +ChannelFormat.None="无" +ChannelFormat.2_0ch="2ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.5_1chBack="5.1ch (后)" +ChannelFormat.7_1ch="7.1ch" diff --git a/plugins/decklink/data/locale/zh-TW.ini b/plugins/decklink/data/locale/zh-TW.ini index c6c34b5..d39629c 100644 --- a/plugins/decklink/data/locale/zh-TW.ini +++ b/plugins/decklink/data/locale/zh-TW.ini @@ -3,4 +3,10 @@ Device="裝置" Mode="模式" Buffering="使用緩衝" PixelFormat="像素格式" +ChannelFormat="聲道" +ChannelFormat.None="無" +ChannelFormat.2_0ch="雙聲道" +ChannelFormat.5_1ch="5.1聲道" +ChannelFormat.5_1chBack="5.1聲道(後置環繞喇叭)" +ChannelFormat.7_1ch="7.1聲道" diff --git a/plugins/decklink/decklink-device-instance.cpp b/plugins/decklink/decklink-device-instance.cpp index 6185b12..557713a 100644 --- a/plugins/decklink/decklink-device-instance.cpp +++ b/plugins/decklink/decklink-device-instance.cpp @@ -1,4 +1,5 @@ #include "decklink-device-instance.hpp" +#include "audio-repack.hpp" #include #include @@ -8,6 +9,8 @@ #define LOG(level, message, ...) blog(level, "%s: " message, \ obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__) +#define ISSTEREO(flag) ((flag) == SPEAKERS_STEREO) + static inline enum video_format ConvertPixelFormat(BMDPixelFormat format) { switch (format) { @@ -20,6 +23,36 @@ static inline enum video_format ConvertPixelFormat(BMDPixelFormat format) return VIDEO_FORMAT_UYVY; } +static inline int ConvertChannelFormat(speaker_layout format) +{ + switch (format) { + case SPEAKERS_5POINT1: + case SPEAKERS_5POINT1_SURROUND: + case SPEAKERS_7POINT1: + return 8; + + default: + case SPEAKERS_STEREO: + return 2; + } +} + +static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format) +{ + switch (format) { + case SPEAKERS_5POINT1: + case SPEAKERS_5POINT1_SURROUND: + return repack_mode_8to6ch_swap23; + + case SPEAKERS_7POINT1: + return repack_mode_8ch_swap23; + + default: + assert(false && "No repack requested"); + return (audio_repack_mode_t)-1; + } +} + DeckLinkDeviceInstance::DeckLinkDeviceInstance(DeckLink *decklink_, DeckLinkDevice *device_) : currentFrame(), currentPacket(), decklink(decklink_), device(device_) @@ -46,9 +79,23 @@ void DeckLinkDeviceInstance::HandleAudioPacket( return; } - currentPacket.data[0] = (uint8_t *)bytes; - currentPacket.frames = (uint32_t)audioPacket->GetSampleFrameCount(); - currentPacket.timestamp = timestamp; + const uint32_t frameCount = (uint32_t)audioPacket->GetSampleFrameCount(); + currentPacket.frames = frameCount; + currentPacket.timestamp = timestamp; + + if (!ISSTEREO(channelFormat)) { + if (audioRepacker->repack((uint8_t *)bytes, frameCount) < 0) { + LOG(LOG_ERROR, "Failed to convert audio packet data"); + return; + } + + currentPacket.data[0] = (*audioRepacker)->packet_buffer; + } else { + currentPacket.data[0] = (uint8_t *)bytes; + } + + nextAudioTS = timestamp + + ((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1; obs_source_output_audio(decklink->GetSource(), ¤tPacket); } @@ -78,6 +125,19 @@ void DeckLinkDeviceInstance::HandleVideoFrame( obs_source_output_video(decklink->GetSource(), ¤tFrame); } +void DeckLinkDeviceInstance::FinalizeStream() +{ + input->SetCallback(nullptr); + + if (audioRepacker != nullptr) + { + delete audioRepacker; + audioRepacker = nullptr; + } + + mode = nullptr; +} + bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) { if (mode != nullptr) @@ -93,8 +153,6 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) pixelFormat = decklink->GetPixelFormat(); currentFrame.format = ConvertPixelFormat(pixelFormat); - input->SetCallback(this); - const BMDDisplayMode displayMode = mode_->GetDisplayMode(); const HRESULT videoResult = input->EnableVideoInput(displayMode, @@ -102,22 +160,36 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) if (videoResult != S_OK) { LOG(LOG_ERROR, "Failed to enable video input"); - input->SetCallback(nullptr); return false; } - const HRESULT audioResult = input->EnableAudioInput( - bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, - 2); + channelFormat = decklink->GetChannelFormat(); + currentPacket.speakers = channelFormat; - if (audioResult != S_OK) - LOG(LOG_WARNING, "Failed to enable audio input; continuing..."); + if (channelFormat != SPEAKERS_UNKNOWN) { + const int channel = ConvertChannelFormat(channelFormat); + const HRESULT audioResult = input->EnableAudioInput( + bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, + channel); + + if (audioResult != S_OK) + LOG(LOG_WARNING, "Failed to enable audio input; continuing..."); + + if (!ISSTEREO(channelFormat)) { + const audio_repack_mode_t repack_mode = ConvertRepackFormat(channelFormat); + audioRepacker = new AudioRepacker(repack_mode); + } + } + + if (input->SetCallback(this) != S_OK) { + LOG(LOG_ERROR, "Failed to set callback"); + FinalizeStream(); + return false; + } if (input->StartStreams() != S_OK) { LOG(LOG_ERROR, "Failed to start streams"); - input->SetCallback(nullptr); - input->DisableVideoInput(); - input->DisableAudioInput(); + FinalizeStream(); return false; } @@ -135,11 +207,7 @@ bool DeckLinkDeviceInstance::StopCapture(void) GetDevice()->GetDisplayName().c_str()); input->StopStreams(); - input->SetCallback(nullptr); - input->DisableVideoInput(); - input->DisableAudioInput(); - - mode = nullptr; + FinalizeStream(); return true; } @@ -154,10 +222,27 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived( BMDTimeValue videoDur = 0; BMDTimeValue audioTS = 0; - if (videoFrame) + if (videoFrame) { videoFrame->GetStreamTime(&videoTS, &videoDur, TIME_BASE); - if (audioPacket) - audioPacket->GetPacketTime(&audioTS, TIME_BASE); + lastVideoTS = (uint64_t)videoTS; + } + if (audioPacket) { + BMDTimeValue newAudioTS = 0; + int64_t diff; + + audioPacket->GetPacketTime(&newAudioTS, TIME_BASE); + audioTS = newAudioTS + audioOffset; + + diff = (int64_t)audioTS - (int64_t)nextAudioTS; + if (diff > 10000000LL) { + audioOffset -= diff; + audioTS = newAudioTS + audioOffset; + + } else if (diff < -1000000) { + audioOffset = 0; + audioTS = newAudioTS; + } + } if (videoFrame && videoTS >= 0) HandleVideoFrame(videoFrame, (uint64_t)videoTS); diff --git a/plugins/decklink/decklink-device-instance.hpp b/plugins/decklink/decklink-device-instance.hpp index b2c6052..c0af8f5 100644 --- a/plugins/decklink/decklink-device-instance.hpp +++ b/plugins/decklink/decklink-device-instance.hpp @@ -2,6 +2,8 @@ #include "decklink-device.hpp" +class AudioRepacker; + class DeckLinkDeviceInstance : public IDeckLinkInputCallback { protected: struct obs_source_frame currentFrame; @@ -12,6 +14,13 @@ protected: BMDPixelFormat pixelFormat = bmdFormat8BitYUV; ComPtr input; volatile long refCount = 1; + int64_t audioOffset = 0; + uint64_t nextAudioTS = 0; + uint64_t lastVideoTS = 0; + AudioRepacker *audioRepacker = nullptr; + speaker_layout channelFormat = SPEAKERS_STEREO; + + void FinalizeStream(); void HandleAudioPacket(IDeckLinkAudioInputPacket *audioPacket, const uint64_t timestamp); @@ -29,6 +38,7 @@ public: } inline BMDPixelFormat GetActivePixelFormat() const {return pixelFormat;} + inline speaker_layout GetActiveChannelFormat() const {return channelFormat;} inline DeckLinkDeviceMode *GetMode() const {return mode;} diff --git a/plugins/decklink/decklink-device.cpp b/plugins/decklink/decklink-device.cpp index 8dc109b..3203247 100644 --- a/plugins/decklink/decklink-device.cpp +++ b/plugins/decklink/decklink-device.cpp @@ -72,6 +72,15 @@ bool DeckLinkDevice::Init() if (result != S_OK) return true; + int64_t channels; + /* Intensity Shuttle for Thunderbolt return 2; however, it supports 8 channels */ + if (name == "Intensity Shuttle Thunderbolt") + maxChannel = 8; + else if (attributes->GetInt(BMDDeckLinkMaximumAudioChannels, &channels) == S_OK) + maxChannel = (int32_t)channels; + else + maxChannel = 2; + /* http://forum.blackmagicdesign.com/viewtopic.php?f=12&t=33967 * BMDDeckLinkTopologicalID for older devices * BMDDeckLinkPersistentID for newer ones */ @@ -118,3 +127,8 @@ const std::string& DeckLinkDevice::GetName(void) const { return name; } + +int32_t DeckLinkDevice::GetMaxChannel(void) const +{ + return maxChannel; +} diff --git a/plugins/decklink/decklink-device.hpp b/plugins/decklink/decklink-device.hpp index bf1a79b..9825ad8 100644 --- a/plugins/decklink/decklink-device.hpp +++ b/plugins/decklink/decklink-device.hpp @@ -14,6 +14,7 @@ class DeckLinkDevice { std::string name; std::string displayName; std::string hash; + int32_t maxChannel; volatile long refCount = 1; public: @@ -30,6 +31,7 @@ public: const std::string& GetHash(void) const; const std::vector& GetModes(void) const; const std::string& GetName(void) const; + int32_t GetMaxChannel(void) const; bool GetInput(IDeckLinkInput **input); diff --git a/plugins/decklink/decklink.cpp b/plugins/decklink/decklink.cpp index e435e03..c20ff32 100644 --- a/plugins/decklink/decklink.cpp +++ b/plugins/decklink/decklink.cpp @@ -65,7 +65,8 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) if (!isActive) return false; if (instance->GetActiveModeId() == modeId && - instance->GetActivePixelFormat() == pixelFormat) + instance->GetActivePixelFormat() == pixelFormat && + instance->GetActiveChannelFormat() == channelFormat) return false; } diff --git a/plugins/decklink/decklink.hpp b/plugins/decklink/decklink.hpp index daa0df7..217637a 100644 --- a/plugins/decklink/decklink.hpp +++ b/plugins/decklink/decklink.hpp @@ -22,6 +22,7 @@ protected: volatile long activateRefs = 0; std::recursive_mutex deviceMutex; BMDPixelFormat pixelFormat = bmdFormat8BitYUV; + speaker_layout channelFormat = SPEAKERS_STEREO; void SaveSettings(); static void DevicesChanged(void *param, DeckLinkDevice *device, @@ -41,6 +42,11 @@ public: { pixelFormat = format; } + inline speaker_layout GetChannelFormat() const {return channelFormat;} + inline void SetChannelFormat(speaker_layout format) + { + channelFormat = format; + } bool Activate(DeckLinkDevice *device, long long modeId); void Deactivate(); diff --git a/plugins/decklink/linux/CMakeLists.txt b/plugins/decklink/linux/CMakeLists.txt index e51cb95..1ed5ae4 100644 --- a/plugins/decklink/linux/CMakeLists.txt +++ b/plugins/decklink/linux/CMakeLists.txt @@ -1,5 +1,10 @@ project(linux-decklink) +if(DISABLE_DECKLINK) + message(STATUS "decklink plugin disabled") + return() +endif() + set(linux-decklink-sdk_HEADERS decklink-sdk/DeckLinkAPI.h decklink-sdk/DeckLinkAPIConfiguration.h @@ -22,6 +27,8 @@ set(linux-decklink_HEADERS ../decklink-device-discovery.hpp ../decklink-device.hpp ../decklink-device-mode.hpp + ../audio-repack.h + ../audio-repack.hpp ) set(linux-decklink_SOURCES @@ -31,6 +38,7 @@ set(linux-decklink_SOURCES ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp + ../audio-repack.c platform.cpp) add_library(linux-decklink MODULE diff --git a/plugins/decklink/mac/CMakeLists.txt b/plugins/decklink/mac/CMakeLists.txt index ce0b9bd..d016470 100644 --- a/plugins/decklink/mac/CMakeLists.txt +++ b/plugins/decklink/mac/CMakeLists.txt @@ -1,5 +1,10 @@ project(mac-decklink) +if(DISABLE_DECKLINK) + message(STATUS "decklink plugin disabled") + return() +endif() + find_library(COREFOUNDATION CoreFoundation) include_directories(${COREFOUNDATION}) @@ -26,6 +31,8 @@ set(mac-decklink_HEADERS ../decklink-device-discovery.hpp ../decklink-device.hpp ../decklink-device-mode.hpp + ../audio-repack.h + ../audio-repack.hpp ) set(mac-decklink_SOURCES @@ -35,6 +42,7 @@ set(mac-decklink_SOURCES ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp + ../audio-repack.c platform.cpp) add_library(mac-decklink MODULE diff --git a/plugins/decklink/plugin-main.cpp b/plugins/decklink/plugin-main.cpp index 13523f8..465eda0 100644 --- a/plugins/decklink/plugin-main.cpp +++ b/plugins/decklink/plugin-main.cpp @@ -7,17 +7,31 @@ 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 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_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_5_1CH obs_module_text("ChannelFormat.5_1ch") +#define TEXT_CHANNEL_FORMAT_5_1CH_BACK obs_module_text("ChannelFormat.5_1chBack") +#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(); - uint32_t flags = obs_source_get_flags(source); - if (enabled) - flags &= ~OBS_SOURCE_FLAG_UNBUFFERED; - else - flags |= OBS_SOURCE_FLAG_UNBUFFERED; - obs_source_set_flags(source, flags); + obs_source_set_async_unbuffered(source, !enabled); } static void *decklink_create(obs_data_t *settings, obs_source_t *source) @@ -25,7 +39,7 @@ static void *decklink_create(obs_data_t *settings, obs_source_t *source) DeckLink *decklink = new DeckLink(source, deviceEnum); decklink_enable_buffering(decklink, - obs_data_get_bool(settings, "buffering")); + obs_data_get_bool(settings, BUFFERING)); obs_source_update(source, settings); return decklink; @@ -40,25 +54,29 @@ static void decklink_destroy(void *data) 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 format = (BMDPixelFormat)obs_data_get_int(settings, - "pixel_format"); + 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); + speaker_layout channelFormat = (speaker_layout)obs_data_get_int(settings, + CHANNEL_FORMAT); decklink_enable_buffering(decklink, - obs_data_get_bool(settings, "buffering")); + obs_data_get_bool(settings, BUFFERING)); ComPtr device; device.Set(deviceEnum->FindByHash(hash)); - decklink->SetPixelFormat(format); + decklink->SetPixelFormat(pixelFormat); + decklink->SetChannelFormat(channelFormat); decklink->Activate(device, id); } static void decklink_get_defaults(obs_data_t *settings) { - obs_data_set_default_bool(settings, "buffering", true); - obs_data_set_default_int(settings, "pixel_format", bmdFormat8BitYUV); + obs_data_set_default_bool(settings, BUFFERING, true); + obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV); + obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO); } static const char *decklink_get_name(void*) @@ -69,10 +87,10 @@ static const char *decklink_get_name(void*) 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"); + 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; @@ -90,25 +108,41 @@ static bool decklink_device_changed(obs_properties_t *props, obs_property_list_item_disable(list, 0, true); } - list = obs_properties_get(props, "mode_id"); + obs_property_t *modeList = obs_properties_get(props, MODE_ID); + obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); - obs_property_list_clear(list); + 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(list, mode, modeId); - obs_property_list_item_disable(list, 0, true); + obs_property_list_add_int(modeList, mode, modeId); + obs_property_list_item_disable(modeList, 0, true); } else { const std::vector &modes = device->GetModes(); for (DeckLinkDeviceMode *mode : modes) { - obs_property_list_add_int(list, + 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_5_1CH, + SPEAKERS_5POINT1); + obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_5_1CH_BACK, + SPEAKERS_5POINT1_SURROUND); + obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_7_1CH, + SPEAKERS_7POINT1); + } } return true; @@ -132,26 +166,30 @@ 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", - obs_module_text("Device"), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_STRING); + 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", - obs_module_text("Mode"), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); + list = obs_properties_add_list(props, MODE_ID, TEXT_MODE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - list = obs_properties_add_list(props, "pixel_format", - obs_module_text("PixelFormat"), OBS_COMBO_TYPE_LIST, + list = obs_properties_add_list(props, PIXEL_FORMAT, + TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); - obs_properties_add_bool(props, "buffering", - obs_module_text("Buffering")); + 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_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); UNUSED_PARAMETER(data); return props; diff --git a/plugins/decklink/win/CMakeLists.txt b/plugins/decklink/win/CMakeLists.txt index 2b4ecb8..af94645 100644 --- a/plugins/decklink/win/CMakeLists.txt +++ b/plugins/decklink/win/CMakeLists.txt @@ -1,5 +1,10 @@ project(win-decklink) +if(DISABLE_DECKLINK) + message(STATUS "decklink plugin disabled") + return() +endif() + include(IDLFileHelper) set(win-decklink-sdk_IDLS @@ -17,6 +22,8 @@ set(win-decklink_HEADERS ../decklink-device-discovery.hpp ../decklink-device.hpp ../decklink-device-mode.hpp + ../audio-repack.h + ../audio-repack.hpp ) set(win-decklink_SOURCES @@ -26,6 +33,7 @@ set(win-decklink_SOURCES ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp + ../audio-repack.c platform.cpp) add_idl_files(win-decklink-sdk_GENERATED_FILES diff --git a/plugins/image-source/data/locale/bn-BD.ini b/plugins/image-source/data/locale/bn-BD.ini new file mode 100644 index 0000000..30260a0 --- /dev/null +++ b/plugins/image-source/data/locale/bn-BD.ini @@ -0,0 +1,13 @@ +ImageInput="ছবি" +File="ফাইল ছবি" + +SlideShow="ছবি স্লাইড শো" +SlideShow.TransitionSpeed="কোনও পরিবর্তন ঘটলে স্থানীয় গতি (মিলিসেকেন্ড)" +SlideShow.Transition="স্থানান্তর" +SlideShow.Transition.Cut="ছেদন" + +ColorSource="রঙের উৎস" +ColorSource.Color="রং" +ColorSource.Width="প্রস্থ" +ColorSource.Height="উচ্চতা" + diff --git a/plugins/image-source/data/locale/cs-CZ.ini b/plugins/image-source/data/locale/cs-CZ.ini index 66415cf..e3dc169 100644 --- a/plugins/image-source/data/locale/cs-CZ.ini +++ b/plugins/image-source/data/locale/cs-CZ.ini @@ -6,6 +6,8 @@ SlideShow="Obrázková prezentace" SlideShow.TransitionSpeed="Rychlost přechodu (milisekundy)" SlideShow.SlideTime="Čas mezi snímky (milisekundy)" SlideShow.Files="Soubory obrázků" +SlideShow.CustomSize="Poměr stran" +SlideShow.CustomSize.Auto="Automatický" SlideShow.Randomize="Náhodné přehrávání" SlideShow.Transition="Přechod" SlideShow.Transition.Cut="Střih" diff --git a/plugins/image-source/data/locale/da-DK.ini b/plugins/image-source/data/locale/da-DK.ini index 4995f7e..ef3ff4f 100644 --- a/plugins/image-source/data/locale/da-DK.ini +++ b/plugins/image-source/data/locale/da-DK.ini @@ -6,6 +6,8 @@ SlideShow="Billede diasshow" SlideShow.TransitionSpeed="Overgangshastighed (millisekunder)" SlideShow.SlideTime="Tid mellem dias (millisekunder)" SlideShow.Files="Billedfiler" +SlideShow.CustomSize="Afgrænsningsstørrelse/Formatforhold" +SlideShow.CustomSize.Auto="Automatisk" SlideShow.Randomize="Tilfældig afspilning" SlideShow.Transition="Overgang" SlideShow.Transition.Cut="Klip" @@ -13,4 +15,8 @@ SlideShow.Transition.Fade="Overgang" SlideShow.Transition.Swipe="Stryg" SlideShow.Transition.Slide="Glide" +ColorSource="Farvekilde" +ColorSource.Color="Farve" +ColorSource.Width="Bredde" +ColorSource.Height="Højde" diff --git a/plugins/image-source/data/locale/de-DE.ini b/plugins/image-source/data/locale/de-DE.ini index 47c37da..9c7220c 100644 --- a/plugins/image-source/data/locale/de-DE.ini +++ b/plugins/image-source/data/locale/de-DE.ini @@ -6,6 +6,8 @@ SlideShow="Diashow" SlideShow.TransitionSpeed="Geschwindigkeit des Übergangs (Millisekunden)" SlideShow.SlideTime="Zeit zwischen Bildern (Millisekunden)" SlideShow.Files="Bilddateien" +SlideShow.CustomSize="Rahmen Größe/Seitenverhältnis" +SlideShow.CustomSize.Auto="Automatisch" SlideShow.Randomize="Zufällige Wiedergabe" SlideShow.Transition="Übergang" SlideShow.Transition.Cut="Schnitt" diff --git a/plugins/image-source/data/locale/en-US.ini b/plugins/image-source/data/locale/en-US.ini index afec159..da74269 100644 --- a/plugins/image-source/data/locale/en-US.ini +++ b/plugins/image-source/data/locale/en-US.ini @@ -6,6 +6,8 @@ SlideShow="Image Slide Show" SlideShow.TransitionSpeed="Transition Speed (milliseconds)" SlideShow.SlideTime="Time Between Slides (milliseconds)" SlideShow.Files="Image Files" +SlideShow.CustomSize="Bounding Size/Aspect Ratio" +SlideShow.CustomSize.Auto="Automatic" SlideShow.Randomize="Randomize Playback" SlideShow.Transition="Transition" SlideShow.Transition.Cut="Cut" diff --git a/plugins/image-source/data/locale/es-ES.ini b/plugins/image-source/data/locale/es-ES.ini index 2bd466d..3c64264 100644 --- a/plugins/image-source/data/locale/es-ES.ini +++ b/plugins/image-source/data/locale/es-ES.ini @@ -6,6 +6,8 @@ SlideShow="Galería de imágenes" SlideShow.TransitionSpeed="Velocidad de la transición (milisegundos)" SlideShow.SlideTime="Tiempo entre diapositivas (milisegundos)" SlideShow.Files="Archivo de imagen" +SlideShow.CustomSize="Relación de aspecto" +SlideShow.CustomSize.Auto="Automático" SlideShow.Randomize="Reproducción aleatoria" SlideShow.Transition="Transición" SlideShow.Transition.Cut="Corte" diff --git a/plugins/image-source/data/locale/eu-ES.ini b/plugins/image-source/data/locale/eu-ES.ini index 0e7f773..e954f17 100644 --- a/plugins/image-source/data/locale/eu-ES.ini +++ b/plugins/image-source/data/locale/eu-ES.ini @@ -6,6 +6,8 @@ SlideShow="Irudien diaporama" SlideShow.TransitionSpeed="Trantsizioaren abiadura (milisegundotan)" SlideShow.SlideTime="Diapositiben arteko denbora (milisegundotan)" SlideShow.Files="Irudi fitxategiak" +SlideShow.CustomSize="Markoaren tamaina/Aspektu-erlazioa" +SlideShow.CustomSize.Auto="Automatikoa" SlideShow.Randomize="Ausazko erreprodukzioa" SlideShow.Transition="Trantsizioa" SlideShow.Transition.Cut="Ebaki" diff --git a/plugins/image-source/data/locale/fi-FI.ini b/plugins/image-source/data/locale/fi-FI.ini index fc989cf..64fdea6 100644 --- a/plugins/image-source/data/locale/fi-FI.ini +++ b/plugins/image-source/data/locale/fi-FI.ini @@ -6,6 +6,8 @@ SlideShow="Diaesitys" SlideShow.TransitionSpeed="Siirtymän nopeus (millisekuntia)" SlideShow.SlideTime="Kesto kuvien välissä (millisekunteina)" SlideShow.Files="Kuvatiedostot" +SlideShow.CustomSize="Rajauskoko/Kuvasuhde" +SlideShow.CustomSize.Auto="Automaattinen" SlideShow.Randomize="Toista satunnaisesti" SlideShow.Transition="Siirtymä" SlideShow.Transition.Cut="Leikkaa" diff --git a/plugins/image-source/data/locale/fr-FR.ini b/plugins/image-source/data/locale/fr-FR.ini index ba2b10b..fdf7713 100644 --- a/plugins/image-source/data/locale/fr-FR.ini +++ b/plugins/image-source/data/locale/fr-FR.ini @@ -6,6 +6,8 @@ SlideShow="Diaporama" SlideShow.TransitionSpeed="Vitesse de transition (millisecondes)" SlideShow.SlideTime="Temps entre chaque diapositive (millisecondes)" SlideShow.Files="Fichiers image" +SlideShow.CustomSize="Taille Limite/Ratio d'aspect" +SlideShow.CustomSize.Auto="Automatique" SlideShow.Randomize="Lecture aléatoire" SlideShow.Transition="Transition" SlideShow.Transition.Cut="Coupure" diff --git a/plugins/image-source/data/locale/hi-IN.ini b/plugins/image-source/data/locale/hi-IN.ini new file mode 100644 index 0000000..e6d5043 --- /dev/null +++ b/plugins/image-source/data/locale/hi-IN.ini @@ -0,0 +1,4 @@ +ImageInput="छवि" + + + diff --git a/plugins/image-source/data/locale/hu-HU.ini b/plugins/image-source/data/locale/hu-HU.ini index 0f84dcc..2b223ba 100644 --- a/plugins/image-source/data/locale/hu-HU.ini +++ b/plugins/image-source/data/locale/hu-HU.ini @@ -6,6 +6,8 @@ SlideShow="Képvetítő" SlideShow.TransitionSpeed="Áttűnési sebesség (ezredmásodperc)" SlideShow.SlideTime="Diák közti idő (ezredmásodperc)" SlideShow.Files="Képfájlok" +SlideShow.CustomSize="Befoglaló méret/Képarány" +SlideShow.CustomSize.Auto="Automatikus" SlideShow.Randomize="Véletlenszerű lejátszás" SlideShow.Transition="Átmenet" SlideShow.Transition.Cut="Kivágás" diff --git a/plugins/image-source/data/locale/ja-JP.ini b/plugins/image-source/data/locale/ja-JP.ini index 5392d81..de0bb8a 100644 --- a/plugins/image-source/data/locale/ja-JP.ini +++ b/plugins/image-source/data/locale/ja-JP.ini @@ -6,6 +6,8 @@ SlideShow="画像スライドショー" SlideShow.TransitionSpeed="画面切替速度 (ミリ秒)" SlideShow.SlideTime="スライド時間間隔 (ミリ秒)" SlideShow.Files="画像ファイル" +SlideShow.CustomSize="バウンディングサイズ/アスペクト比" +SlideShow.CustomSize.Auto="自動" SlideShow.Randomize="ランダム再生" SlideShow.Transition="トランジション" SlideShow.Transition.Cut="カット" diff --git a/plugins/image-source/data/locale/ko-KR.ini b/plugins/image-source/data/locale/ko-KR.ini index f73bb20..942e92c 100644 --- a/plugins/image-source/data/locale/ko-KR.ini +++ b/plugins/image-source/data/locale/ko-KR.ini @@ -6,6 +6,8 @@ SlideShow="이미지 슬라이드 쇼" SlideShow.TransitionSpeed="전환 속도 (밀리초)" SlideShow.SlideTime="슬라이드 간격 (밀리초)" SlideShow.Files="이미지 파일 형식" +SlideShow.CustomSize="경계 크기/화면 비율" +SlideShow.CustomSize.Auto="자동" SlideShow.Randomize="무작위 재생" SlideShow.Transition="전환 방식" SlideShow.Transition.Cut="자르기" diff --git a/plugins/image-source/data/locale/nl-NL.ini b/plugins/image-source/data/locale/nl-NL.ini index 89aa6be..3b34c75 100644 --- a/plugins/image-source/data/locale/nl-NL.ini +++ b/plugins/image-source/data/locale/nl-NL.ini @@ -6,6 +6,8 @@ SlideShow="Diashow" SlideShow.TransitionSpeed="Overgangssnelheid (milliseconden)" SlideShow.SlideTime="Tijd Tussen Dia's (milliseconden)" SlideShow.Files="Afbeeldingsbestanden" +SlideShow.CustomSize="Randgrootte/Beeldverhouding" +SlideShow.CustomSize.Auto="Automatisch" SlideShow.Randomize="Willekeurige Volgorde" SlideShow.Transition="Overgang" SlideShow.Transition.Cut="Knippen" @@ -13,4 +15,8 @@ SlideShow.Transition.Fade="Vervagen" SlideShow.Transition.Swipe="Vegen" SlideShow.Transition.Slide="Slide" +ColorSource="Kleurbron" +ColorSource.Color="Kleur" +ColorSource.Width="Breedte" +ColorSource.Height="Hoogte" diff --git a/plugins/image-source/data/locale/pl-PL.ini b/plugins/image-source/data/locale/pl-PL.ini index 27c1a8d..d947d35 100644 --- a/plugins/image-source/data/locale/pl-PL.ini +++ b/plugins/image-source/data/locale/pl-PL.ini @@ -6,6 +6,8 @@ SlideShow="Pokaz slajdów" SlideShow.TransitionSpeed="Prędkość efektu przejścia (ms)" SlideShow.SlideTime="Czas wyświetlania slajdu (ms)" SlideShow.Files="Pliki graficzne" +SlideShow.CustomSize="Ograniczenie rozmiaru/Proporcje" +SlideShow.CustomSize.Auto="Automatycznie" SlideShow.Randomize="Odtwarzanie losowe" SlideShow.Transition="Efekt przejścia" SlideShow.Transition.Cut="Cięcie" diff --git a/plugins/image-source/data/locale/pt-BR.ini b/plugins/image-source/data/locale/pt-BR.ini index acfbe7e..da5885e 100644 --- a/plugins/image-source/data/locale/pt-BR.ini +++ b/plugins/image-source/data/locale/pt-BR.ini @@ -6,6 +6,8 @@ SlideShow="Apresentação de Slides" SlideShow.TransitionSpeed="Velocidade de Transição (em milissegundos)" SlideShow.SlideTime="Tempo Entre cada Slide (em milissegundos)" SlideShow.Files="Arquivos de Imagem" +SlideShow.CustomSize="Tamanho Delimitador/Proporção" +SlideShow.CustomSize.Auto="Automático" SlideShow.Randomize="Reprodução aleatória" SlideShow.Transition="Transição" SlideShow.Transition.Cut="Corte" @@ -13,4 +15,8 @@ SlideShow.Transition.Fade="Esmaecer" SlideShow.Transition.Swipe="Arrastar" SlideShow.Transition.Slide="Deslizar" +ColorSource="Fonte de Cor" +ColorSource.Color="Cor" +ColorSource.Width="Largura" +ColorSource.Height="Altura" diff --git a/plugins/image-source/data/locale/ru-RU.ini b/plugins/image-source/data/locale/ru-RU.ini index c9a257b..35f708e 100644 --- a/plugins/image-source/data/locale/ru-RU.ini +++ b/plugins/image-source/data/locale/ru-RU.ini @@ -6,6 +6,8 @@ SlideShow="Слайдшоу" SlideShow.TransitionSpeed="Скорость перехода (миллисекунды)" SlideShow.SlideTime="Время между слайдами (миллисекунды)" SlideShow.Files="Файлы изображений" +SlideShow.CustomSize="Ограничение размера/Соотношение сторон" +SlideShow.CustomSize.Auto="Автоматически" SlideShow.Randomize="Случайное воспроизведение" SlideShow.Transition="Переход" SlideShow.Transition.Cut="Обрезать" diff --git a/plugins/image-source/data/locale/sv-SE.ini b/plugins/image-source/data/locale/sv-SE.ini index 73c09b7..a4bf1c5 100644 --- a/plugins/image-source/data/locale/sv-SE.ini +++ b/plugins/image-source/data/locale/sv-SE.ini @@ -6,6 +6,7 @@ SlideShow="Bildspel" SlideShow.TransitionSpeed="Övergångshastighet (millisekunder)" SlideShow.SlideTime="Tid mellan bilder (millisekunder)" SlideShow.Files="Bildfiler" +SlideShow.CustomSize.Auto="Automatisk" SlideShow.Randomize="Slumpa uppspelning" SlideShow.Transition="Övergång" SlideShow.Transition.Cut="Klipp" diff --git a/plugins/image-source/data/locale/tr-TR.ini b/plugins/image-source/data/locale/tr-TR.ini index e99d115..c300a74 100644 --- a/plugins/image-source/data/locale/tr-TR.ini +++ b/plugins/image-source/data/locale/tr-TR.ini @@ -6,6 +6,8 @@ SlideShow="Resim Slayt Gösterisi" SlideShow.TransitionSpeed="Geçiş Hızı (milisaniye)" SlideShow.SlideTime="Slaytlar Arası Süre (milisaniye)" SlideShow.Files="Görüntü Dosyaları" +SlideShow.CustomSize="Sınırlayıcı Boyut/En-Boy Oranı" +SlideShow.CustomSize.Auto="Otomatik" SlideShow.Randomize="Rastgele Gösterim" SlideShow.Transition="Geçiş" SlideShow.Transition.Cut="Cut" diff --git a/plugins/image-source/data/locale/uk-UA.ini b/plugins/image-source/data/locale/uk-UA.ini index 870f96a..9d38613 100644 --- a/plugins/image-source/data/locale/uk-UA.ini +++ b/plugins/image-source/data/locale/uk-UA.ini @@ -6,6 +6,8 @@ SlideShow="Слайд-шоу" SlideShow.TransitionSpeed="Тривалість відео-переходу (мілісекунд)" SlideShow.SlideTime="Час між слайдами (мілісекунд)" SlideShow.Files="Файли зображень" +SlideShow.CustomSize="Розмір рамки/пропорції" +SlideShow.CustomSize.Auto="Автоматично" SlideShow.Randomize="Випадкове відтворення" SlideShow.Transition="Відео-перехід" SlideShow.Transition.Cut="Cut" diff --git a/plugins/image-source/data/locale/zh-CN.ini b/plugins/image-source/data/locale/zh-CN.ini index b9dae01..c3eb2c6 100644 --- a/plugins/image-source/data/locale/zh-CN.ini +++ b/plugins/image-source/data/locale/zh-CN.ini @@ -6,6 +6,8 @@ SlideShow="图像幻灯片放映" SlideShow.TransitionSpeed="过渡速度(毫秒)" SlideShow.SlideTime="幻灯片之间时间(毫秒)" SlideShow.Files="图像文件" +SlideShow.CustomSize="边框大小/高宽比" +SlideShow.CustomSize.Auto="自动" SlideShow.Randomize="随机播放" SlideShow.Transition="转换" SlideShow.Transition.Cut="剪切" diff --git a/plugins/image-source/data/locale/zh-TW.ini b/plugins/image-source/data/locale/zh-TW.ini index b56eaa2..87d7ff9 100644 --- a/plugins/image-source/data/locale/zh-TW.ini +++ b/plugins/image-source/data/locale/zh-TW.ini @@ -6,6 +6,8 @@ SlideShow="投影片放映" SlideShow.TransitionSpeed="變更速度 (毫秒)" SlideShow.SlideTime="圖片間隔 (毫秒)" SlideShow.Files="圖片檔案" +SlideShow.CustomSize="邊框大小長寬比" +SlideShow.CustomSize.Auto="自動" SlideShow.Randomize="隨機播放" SlideShow.Transition="變更特效" SlideShow.Transition.Cut="直接變更" diff --git a/plugins/image-source/image-source.c b/plugins/image-source/image-source.c index ed87504..b464325 100644 --- a/plugins/image-source/image-source.c +++ b/plugins/image-source/image-source.c @@ -162,6 +162,17 @@ static void image_source_tick(void *data, float seconds) struct image_source *context = data; uint64_t frame_time = obs_get_video_frame_time(); + context->update_time_elapsed += seconds; + + if (context->update_time_elapsed >= 1.0f) { + time_t t = get_modified_timestamp(context->file); + context->update_time_elapsed = 0.0f; + + if (context->file_timestamp != t) { + image_source_load(context); + } + } + if (obs_source_active(context->source)) { if (!context->active) { if (context->image.is_animated_gif) @@ -199,17 +210,6 @@ static void image_source_tick(void *data, float seconds) } context->last_time = frame_time; - - context->update_time_elapsed += seconds; - - if (context->update_time_elapsed >= 1.0f) { - time_t t = get_modified_timestamp(context->file); - context->update_time_elapsed = 0.0f; - - if (context->file_timestamp != t) { - image_source_load(context); - } - } } diff --git a/plugins/image-source/obs-slideshow.c b/plugins/image-source/obs-slideshow.c index 3fa7b67..cc57d1a 100644 --- a/plugins/image-source/obs-slideshow.c +++ b/plugins/image-source/obs-slideshow.c @@ -11,6 +11,7 @@ #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) #define S_TR_SPEED "transition_speed" +#define S_CUSTOM_SIZE "use_custom_size" #define S_SLIDE_TIME "slide_time" #define S_TRANSITION "transition" #define S_RANDOMIZE "randomize" @@ -23,6 +24,8 @@ #define T_(text) obs_module_text("SlideShow." text) #define T_TR_SPEED T_("TransitionSpeed") +#define T_CUSTOM_SIZE T_("CustomSize") +#define T_CUSTOM_SIZE_AUTO T_("CustomSize.Auto") #define T_SLIDE_TIME T_("SlideTime") #define T_TRANSITION T_("Transition") #define T_RANDOMIZE T_("Randomize") @@ -288,6 +291,48 @@ static void ss_update(void *data, obs_data_t *settings) obs_source_release(old_tr); free_files(&old_files.da); + /* ------------------------- */ + + const char *res_str = obs_data_get_string(settings, S_CUSTOM_SIZE); + bool aspect_only = false, use_auto = true; + int cx_in = 0, cy_in = 0; + + if (strcmp(res_str, T_CUSTOM_SIZE_AUTO) != 0) { + int ret = sscanf(res_str, "%dx%d", &cx_in, &cy_in); + if (ret == 2) { + aspect_only = false; + use_auto = false; + } else { + ret = sscanf(res_str, "%d:%d", &cx_in, &cy_in); + if (ret == 2) { + aspect_only = true; + use_auto = false; + } + } + } + + if (!use_auto) { + double cx_f = (double)cx; + double cy_f = (double)cy; + + double old_aspect = cx_f / cy_f; + double new_aspect = (double)cx_in / (double)cy_in; + + if (aspect_only) { + if (fabs(old_aspect - new_aspect) > EPSILON) { + if (new_aspect > old_aspect) + cx = (uint32_t)(cy_f * new_aspect); + else + cy = (uint32_t)(cx_f / new_aspect); + } + } else { + cx = (uint32_t)cx_in; + cy = (uint32_t)cy_in; + } + } + + /* ------------------------- */ + ss->cx = cx; ss->cy = cy; ss->cur_item = 0; @@ -460,17 +505,38 @@ static void ss_defaults(obs_data_t *settings) obs_data_set_default_string(settings, S_TRANSITION, "fade"); obs_data_set_default_int(settings, S_SLIDE_TIME, 8000); obs_data_set_default_int(settings, S_TR_SPEED, 700); + obs_data_set_default_string(settings, S_CUSTOM_SIZE, T_CUSTOM_SIZE_AUTO); } static const char *file_filter = "Image files (*.bmp *.tga *.png *.jpeg *.jpg *.gif)"; +static const char *aspects[] = { + "16:9", + "16:10", + "4:3", + "1:1" +}; + +#define NUM_ASPECTS (sizeof(aspects) / sizeof(const char *)) + static obs_properties_t *ss_properties(void *data) { obs_properties_t *ppts = obs_properties_create(); struct slideshow *ss = data; + struct obs_video_info ovi; struct dstr path = {0}; obs_property_t *p; + int cx; + int cy; + + /* ----------------- */ + + obs_get_video_info(&ovi); + cx = (int)ovi.base_width; + cy = (int)ovi.base_height; + + /* ----------------- */ p = obs_properties_add_list(ppts, S_TRANSITION, T_TRANSITION, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); @@ -485,6 +551,18 @@ static obs_properties_t *ss_properties(void *data) 0, 3600000, 50); obs_properties_add_bool(ppts, S_RANDOMIZE, T_RANDOMIZE); + p = obs_properties_add_list(ppts, S_CUSTOM_SIZE, T_CUSTOM_SIZE, + OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); + + obs_property_list_add_string(p, T_CUSTOM_SIZE_AUTO, T_CUSTOM_SIZE_AUTO); + + for (size_t i = 0; i < NUM_ASPECTS; i++) + obs_property_list_add_string(p, aspects[i], aspects[i]); + + char str[32]; + snprintf(str, 32, "%dx%d", cx, cy); + obs_property_list_add_string(p, str, str); + if (ss) { pthread_mutex_lock(&ss->mutex); if (ss->files.num) { diff --git a/plugins/linux-alsa/data/locale/bn-BD.ini b/plugins/linux-alsa/data/locale/bn-BD.ini new file mode 100644 index 0000000..55b9d71 --- /dev/null +++ b/plugins/linux-alsa/data/locale/bn-BD.ini @@ -0,0 +1,2 @@ +AlsaInput="অডিও ক্যাপচার ডিভাইস (ALSA)" + diff --git a/plugins/linux-capture/data/locale/bn-BD.ini b/plugins/linux-capture/data/locale/bn-BD.ini new file mode 100644 index 0000000..65e0f73 --- /dev/null +++ b/plugins/linux-capture/data/locale/bn-BD.ini @@ -0,0 +1,9 @@ +X11SharedMemoryScreenInput="স্ক্রিন ক্যাপচার (XSHM)" +Screen="পর্দা" +XServer="X সার্ভার" +XCCapture="উইন্ডো ক্যাপচার (Xcomposite)" +CropLeft="ফসল বাম (পিক্সেল)" +CropRight="ফসল অধিকার (পিক্সেল)" +CropBottom="ফসল তলায় (পিক্সেল)" +ExcludeAlpha="আলফা-কম গঠন বিন্যাস (মেসা পরামর্শ) ব্যবহার করুন" + diff --git a/plugins/linux-capture/data/locale/et-EE.ini b/plugins/linux-capture/data/locale/et-EE.ini index c9d3cc7..3fcde5a 100644 --- a/plugins/linux-capture/data/locale/et-EE.ini +++ b/plugins/linux-capture/data/locale/et-EE.ini @@ -12,4 +12,5 @@ CropBottom="Kärbi alt (pikslit)" SwapRedBlue="Vaheta punane ja sinine" LockX="Lukusta X server salvestamise ajal" IncludeXBorder="Kaasa X piirjoon" +ExcludeAlpha="Kasuta alpha-kanalita tekstuuri formaati (Mesa lahendus)" diff --git a/plugins/linux-capture/data/locale/hi-IN.ini b/plugins/linux-capture/data/locale/hi-IN.ini new file mode 100644 index 0000000..a911810 --- /dev/null +++ b/plugins/linux-capture/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +X11SharedMemoryScreenInput="स्क्रीन कैप्चर (XSHM)" + diff --git a/plugins/linux-jack/data/locale/bn-BD.ini b/plugins/linux-jack/data/locale/bn-BD.ini new file mode 100644 index 0000000..39ddeea --- /dev/null +++ b/plugins/linux-jack/data/locale/bn-BD.ini @@ -0,0 +1,3 @@ +StartJACKServer="জ্যাক সার্ভার চালুকরন্" +Channels="চ্যানেল সংখ্যা" + diff --git a/plugins/linux-pulseaudio/data/locale/bn-BD.ini b/plugins/linux-pulseaudio/data/locale/bn-BD.ini new file mode 100644 index 0000000..ba6e37e --- /dev/null +++ b/plugins/linux-pulseaudio/data/locale/bn-BD.ini @@ -0,0 +1,4 @@ +PulseInput="অডিও ইনপুট ক্যাপচার (PulseAudio)" +PulseOutput="অডিও ইনপুট ক্যাপচার (PulseAudio)" +Device="ডিভাইস" + diff --git a/plugins/linux-pulseaudio/data/locale/hi-IN.ini b/plugins/linux-pulseaudio/data/locale/hi-IN.ini new file mode 100644 index 0000000..abd2a34 --- /dev/null +++ b/plugins/linux-pulseaudio/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +PulseInput="ऑडियो इनपुट पर कब्जा (पल्सऑडियो)" + diff --git a/plugins/linux-pulseaudio/pulse-input.c b/plugins/linux-pulseaudio/pulse-input.c index 26a44fc..15f0ed2 100644 --- a/plugins/linux-pulseaudio/pulse-input.c +++ b/plugins/linux-pulseaudio/pulse-input.c @@ -538,7 +538,7 @@ struct obs_source_info pulse_output_capture = { .type = OBS_SOURCE_TYPE_INPUT, .output_flags = OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE | - OBS_SOURCE_DO_NOT_MONITOR, + OBS_SOURCE_DO_NOT_SELF_MONITOR, .get_name = pulse_output_getname, .create = pulse_create, .destroy = pulse_destroy, diff --git a/plugins/linux-v4l2/data/locale/bn-BD.ini b/plugins/linux-v4l2/data/locale/bn-BD.ini new file mode 100644 index 0000000..479142c --- /dev/null +++ b/plugins/linux-v4l2/data/locale/bn-BD.ini @@ -0,0 +1,11 @@ +V4L2Input="ভিডিও ক্যাপচার ডিভাইস (V4L2)" +Device="ডিভাইস" +Input="ইনপুট" +VideoFormat="ভিডিও ফরম্যাট" +VideoStandard="ভিডিও মান" +DVTiming="DV টাইমিং" +Resolution="রেজল্যুশন" +FrameRate="ফ্রেমের হার" +LeaveUnchanged="ফরাসী" +UseBuffering="বাফারিং ব্যবহার করুন" + diff --git a/plugins/linux-v4l2/data/locale/hi-IN.ini b/plugins/linux-v4l2/data/locale/hi-IN.ini new file mode 100644 index 0000000..a64ec14 --- /dev/null +++ b/plugins/linux-v4l2/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +V4L2Input="वीडियो कैप्चर डिवाइस (V4L2)" + diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index bc33f49..46726ee 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -912,11 +912,8 @@ fail: static void v4l2_update_source_flags(struct v4l2_data *data, obs_data_t *settings) { - uint32_t flags = obs_source_get_flags(data->source); - flags = (obs_data_get_bool(settings, "buffering")) - ? flags & ~OBS_SOURCE_FLAG_UNBUFFERED - : flags | OBS_SOURCE_FLAG_UNBUFFERED; - obs_source_set_flags(data->source, flags); + obs_source_set_async_unbuffered(data->source, + !obs_data_get_bool(settings, "buffering")); } /** diff --git a/plugins/mac-avcapture/CMakeLists.txt b/plugins/mac-avcapture/CMakeLists.txt index f3fc11a..8cb6ef6 100644 --- a/plugins/mac-avcapture/CMakeLists.txt +++ b/plugins/mac-avcapture/CMakeLists.txt @@ -6,12 +6,14 @@ find_library(COREFOUNDATION CoreFoundation) find_library(COREMEDIA CoreMedia) find_library(COREVIDEO CoreVideo) find_library(COCOA Cocoa) +find_library(COREMEDIAIO CoreMediaIO) include_directories(${AVFOUNDATION} ${COCOA} ${COREFOUNDATION} ${COREMEDIA} ${COREVIDEO} + ${COREMEDIAIO} ${COCOA}) set(mac-avcapture_HEADERS @@ -36,6 +38,7 @@ target_link_libraries(mac-avcapture ${COREFOUNDATION} ${COREMEDIA} ${COREVIDEO} + ${COREMEDIAIO} ${COCOA}) install_obs_plugin_with_data(mac-avcapture data) diff --git a/plugins/mac-avcapture/av-capture.mm b/plugins/mac-avcapture/av-capture.mm index 3048316..2e2a591 100644 --- a/plugins/mac-avcapture/av-capture.mm +++ b/plugins/mac-avcapture/av-capture.mm @@ -2,6 +2,7 @@ #import #import #import +#import #include #include @@ -644,13 +645,7 @@ static inline bool update_frame(av_capture *capture, static void av_capture_enable_buffering(av_capture *capture, bool enabled) { - obs_source_t *source = capture->source; - uint32_t flags = obs_source_get_flags(source); - if (enabled) - flags &= ~OBS_SOURCE_FLAG_UNBUFFERED; - else - flags |= OBS_SOURCE_FLAG_UNBUFFERED; - obs_source_set_flags(source, flags); + obs_source_set_async_unbuffered(capture->source, !enabled); } static const char *av_capture_getname(void*) @@ -780,7 +775,7 @@ static bool init_format(av_capture *capture, AVCaptureDevice *dev) CMMediaType mtype = CMFormatDescriptionGetMediaType( format.formatDescription); // TODO: support other media types - if (mtype != kCMMediaType_Video) { + if (mtype != kCMMediaType_Video && mtype != kCMMediaType_Muxed) { AVLOG(LOG_ERROR, "CMMediaType '%s' is unsupported", AV_FOURCC_STR(mtype)); return false; @@ -1247,7 +1242,7 @@ static NSArray *presets(void) AVCaptureSessionPreset640x480, AVCaptureSessionPreset352x288, AVCaptureSessionPreset320x240, - //AVCaptureSessionPresetHigh, + AVCaptureSessionPresetHigh, //AVCaptureSessionPresetMedium, //AVCaptureSessionPresetLow, //AVCaptureSessionPresetPhoto, @@ -1265,6 +1260,7 @@ static NSString *preset_names(NSString *preset) AVCaptureSessionPreset640x480:@"640x480", AVCaptureSessionPreset960x540:@"960x540", AVCaptureSessionPreset1280x720:@"1280x720", + AVCaptureSessionPresetHigh:@"High", }; NSString *name = preset_names[preset]; if (name) @@ -2080,11 +2076,15 @@ static obs_properties_t *av_capture_properties(void *capture) TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(dev_list, "", ""); + for (AVCaptureDevice *dev in [AVCaptureDevice - devicesWithMediaType:AVMediaTypeVideo]) { - obs_property_list_add_string(dev_list, - dev.localizedName.UTF8String, - dev.uniqueID.UTF8String); + devices]) { + if ([dev hasMediaType: AVMediaTypeVideo] || + [dev hasMediaType: AVMediaTypeMuxed]) { + obs_property_list_add_string(dev_list, + dev.localizedName.UTF8String, + dev.uniqueID.UTF8String); + } } obs_property_set_modified_callback(dev_list, @@ -2178,6 +2178,20 @@ OBS_MODULE_USE_DEFAULT_LOCALE("mac-avcapture", "en-US") bool obs_module_load(void) { +#ifdef __MAC_10_10 + // Enable iOS device to show up as AVCapture devices + // From WWDC video 2014 #508 at 5:34 + // https://developer.apple.com/videos/wwdc/2014/#508 + CMIOObjectPropertyAddress prop = { + kCMIOHardwarePropertyAllowScreenCaptureDevices, + kCMIOObjectPropertyScopeGlobal, + kCMIOObjectPropertyElementMaster + }; + UInt32 allow = 1; + CMIOObjectSetPropertyData(kCMIOObjectSystemObject, &prop, 0, NULL, + sizeof(allow), &allow); +#endif + obs_source_info av_capture_info = { .id = "av_capture_input", .type = OBS_SOURCE_TYPE_INPUT, diff --git a/plugins/mac-avcapture/data/locale/bn-BD.ini b/plugins/mac-avcapture/data/locale/bn-BD.ini new file mode 100644 index 0000000..df6159d --- /dev/null +++ b/plugins/mac-avcapture/data/locale/bn-BD.ini @@ -0,0 +1,6 @@ +AVCapture="ভিডিও ক্যাপচার ডিভাইস" +Device="ডিভাইস" +Buffering="বাফারিং ব্যবহার করুন" +VideoRange="ভিডিওর বিন্যাস" +VideoRange.Partial="আংশিক" + diff --git a/plugins/mac-avcapture/data/locale/ca-ES.ini b/plugins/mac-avcapture/data/locale/ca-ES.ini index 210e709..c53289b 100644 --- a/plugins/mac-avcapture/data/locale/ca-ES.ini +++ b/plugins/mac-avcapture/data/locale/ca-ES.ini @@ -3,7 +3,7 @@ Device="Dispositiu" UsePreset="Usa els valors predefinits" Preset="Valors predefinits" Buffering="Usa memòria intermèdia" -FrameRate="Quadres per segon" +FrameRate="Fotogrames per segon" InputFormat="Format d'entrada" ColorSpace="Espai de color" VideoRange="Gamma de vídeo" diff --git a/plugins/mac-capture/data/locale/bn-BD.ini b/plugins/mac-capture/data/locale/bn-BD.ini new file mode 100644 index 0000000..002eed9 --- /dev/null +++ b/plugins/mac-capture/data/locale/bn-BD.ini @@ -0,0 +1,12 @@ +CoreAudio.InputCapture="সব্দ​ গ্রেপ্তার" +CoreAudio.OutputCapture="অডিও আউটপুট অধিগ্রহণ" +DisplayCapture="অধিগ্রহণ প্রদর্শন করুন" +DisplayCapture.Display="প্রদর্শন" +WindowCapture.ShowShadow="উইণ্ডো ছায়া প্রদর্শন করুন" +WindowUtils.Window="জানালা" +WindowUtils.ShowEmptyNames="খালি নামের সাথে Windows প্রদর্শন করা হবে" +CropMode.ToWindow="উইণ্ডো" +CropMode.ToWindowAndManual="জানালা ও সহায়িকা" +Crop.origin.x="বাকি ফসল" +Crop.origin.y="ফসল শীর্ষে" + diff --git a/plugins/mac-capture/data/locale/et-EE.ini b/plugins/mac-capture/data/locale/et-EE.ini index 1b7bc4c..2ce090d 100644 --- a/plugins/mac-capture/data/locale/et-EE.ini +++ b/plugins/mac-capture/data/locale/et-EE.ini @@ -10,8 +10,10 @@ WindowCapture.ShowShadow="Näita akna varju" WindowUtils.Window="Aken" WindowUtils.ShowEmptyNames="Kuva nimetuid aknaid" CropMode="Kärbi" -CropMode.None="Pole" +CropMode.None="Määramata" CropMode.Manual="Manuaalne" +CropMode.ToWindow="Aknasse" +CropMode.ToWindowAndManual="Aknasse ja manuaalselt" Crop.origin.x="Kärbi vasakult" Crop.origin.y="Kärbi ülevalt" Crop.size.width="Kärbi paremalt" diff --git a/plugins/mac-capture/data/locale/hi-IN.ini b/plugins/mac-capture/data/locale/hi-IN.ini new file mode 100644 index 0000000..190d9df --- /dev/null +++ b/plugins/mac-capture/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +CoreAudio.InputCapture="ऑडियो इनपुट पर कब्जा" + diff --git a/plugins/mac-capture/mac-audio.c b/plugins/mac-capture/mac-audio.c index ed922d3..9819001 100644 --- a/plugins/mac-capture/mac-audio.c +++ b/plugins/mac-capture/mac-audio.c @@ -798,7 +798,7 @@ struct obs_source_info coreaudio_output_capture_info = { .type = OBS_SOURCE_TYPE_INPUT, .output_flags = OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE | - OBS_SOURCE_DO_NOT_MONITOR, + OBS_SOURCE_DO_NOT_SELF_MONITOR, .get_name = coreaudio_output_getname, .create = coreaudio_create_output_capture, .destroy = coreaudio_destroy, diff --git a/plugins/mac-syphon/data/locale/bn-BD.ini b/plugins/mac-syphon/data/locale/bn-BD.ini new file mode 100644 index 0000000..33bec3d --- /dev/null +++ b/plugins/mac-syphon/data/locale/bn-BD.ini @@ -0,0 +1,8 @@ +Syphon="খেলার অধিগ্রহণ (Syphon)" +Source="উৎস" +Application="অ্যাপ্লিকেশন" +SyphonLicense="Syphon লাইসেন্স" +Crop.origin.y="ফসল শীর্ষে" +Crop.size.width="তাই ফসল" +Crop.size.height="ফসল তলায়" + diff --git a/plugins/mac-syphon/data/locale/hi-IN.ini b/plugins/mac-syphon/data/locale/hi-IN.ini new file mode 100644 index 0000000..9c66d32 --- /dev/null +++ b/plugins/mac-syphon/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +Syphon="खेल पर कब्जा खेल (Syphon)" + diff --git a/plugins/mac-vth264/data/locale/bn-BD.ini b/plugins/mac-vth264/data/locale/bn-BD.ini new file mode 100644 index 0000000..3fd4a38 --- /dev/null +++ b/plugins/mac-vth264/data/locale/bn-BD.ini @@ -0,0 +1,8 @@ +VTH264EncHW="আপেল ভিটি H264 হার্ডওয়্যার এনকোডার" +VTH264EncSW="আপেল ভিটি H264 সফটওয়্যার এনকোডার" +Bitrate="বিটরেট" +UseMaxBitrate="সীমা সর্বাধিক বিটের হার।" +Profile="প্রোফাইল" +None="(একটিও না)" + + diff --git a/plugins/mac-vth264/data/locale/et-EE.ini b/plugins/mac-vth264/data/locale/et-EE.ini index d086781..451870a 100644 --- a/plugins/mac-vth264/data/locale/et-EE.ini +++ b/plugins/mac-vth264/data/locale/et-EE.ini @@ -4,6 +4,8 @@ VTEncoder="VideoToolbox kodeerija" Bitrate="Bitikiirus" UseMaxBitrate="Piira bitikiirust" MaxBitrate="Maksimaalne bitikiirus" +MaxBitrateWindow="Maksimaalne Bitikiiruse aken (sekundit)" +KeyframeIntervalSec="Võtmekaadri intervall (sekundit, 0=automaatne)" Profile="Profiil" None="(Määramata)" DefaultEncoder="(Vaikekodeering)" diff --git a/plugins/mac-vth264/data/locale/tr-TR.ini b/plugins/mac-vth264/data/locale/tr-TR.ini index 83c022f..5a45f91 100644 --- a/plugins/mac-vth264/data/locale/tr-TR.ini +++ b/plugins/mac-vth264/data/locale/tr-TR.ini @@ -4,6 +4,7 @@ VTEncoder="VideoToolbox Kodlayıcı" Bitrate="Bit hızı" UseMaxBitrate="Bit hızını sınırla" MaxBitrate="Maksimum bit hızı" +MaxBitrateWindow="Maksimum bit hızı penceresi (saniye)" KeyframeIntervalSec="Anahtar Kare Aralığı (saniye, 0=otomatik)" Profile="Profil" None="(Yok)" diff --git a/plugins/mac-vth264/data/locale/vi-VN.ini b/plugins/mac-vth264/data/locale/vi-VN.ini index e21400c..2b73d96 100644 --- a/plugins/mac-vth264/data/locale/vi-VN.ini +++ b/plugins/mac-vth264/data/locale/vi-VN.ini @@ -1,3 +1,13 @@ -None="(Ingen)" +VTH264EncHW="Bộ mã hóa Apple VT H264 bằng phần cứng" +VTH264EncSW="Bộ mã hóa Apple VT H264 bằng phần mềm" +VTEncoder="Bộ mã hóa VideoToolbox" +Bitrate="Bitrate" +UseMaxBitrate="Giới hạn bitrate" +MaxBitrate="Bitrate tối đa" +KeyframeIntervalSec="Thời gian đặt Keyframe (giây, 0=tự động)" +Profile="Hồ sơ" +None="(Không)" +DefaultEncoder="(Bộ mã hóa mặc định)" +UseBFrames="Sử dụng B-Frame" diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 50c9c8d..72c131f 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -26,7 +26,7 @@ add_library(obs-ffmpeg MODULE ${obs-ffmpeg_SOURCES}) target_link_libraries(obs-ffmpeg libobs - libff + media-playback ${obs-ffmpeg_PLATFORM_DEPS} ${FFMPEG_LIBRARIES}) diff --git a/plugins/obs-ffmpeg/data/locale/ar-SA.ini b/plugins/obs-ffmpeg/data/locale/ar-SA.ini index edb0781..4d1afe0 100644 --- a/plugins/obs-ffmpeg/data/locale/ar-SA.ini +++ b/plugins/obs-ffmpeg/data/locale/ar-SA.ini @@ -14,13 +14,11 @@ FFmpegSource="مصدر وسائط" LocalFile="ملف محلي" Looping="تكرار حلقي" Advanced="متقدم" -DiscardNone="لا شيء" ColorRange="نطاق ألوان YUV" ColorRange.Auto="تلقائي" ColorRange.Partial="جزئي" ColorRange.Full="كامل" - MediaFileFilter.AllMediaFiles="كافة ملفات الوسائط" MediaFileFilter.VideoFiles="ملفات الفيديو" MediaFileFilter.AudioFiles="ملفات الصوت" diff --git a/plugins/obs-ffmpeg/data/locale/bg-BG.ini b/plugins/obs-ffmpeg/data/locale/bg-BG.ini index f4957f4..2ffda0d 100644 --- a/plugins/obs-ffmpeg/data/locale/bg-BG.ini +++ b/plugins/obs-ffmpeg/data/locale/bg-BG.ini @@ -6,4 +6,3 @@ Bitrate="Битрейт" - diff --git a/plugins/obs-ffmpeg/data/locale/bn-BD.ini b/plugins/obs-ffmpeg/data/locale/bn-BD.ini new file mode 100644 index 0000000..562c2ee --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/bn-BD.ini @@ -0,0 +1,18 @@ +FFmpegOutput="FFmpeg উত্পাদন" +FFmpegAAC="পূর্ব-নির্ধারিত FFmpeg AAC এনকোডার" +Preset="পূর্ব-নির্ধারিত" +RateControl="হার নিয়ন্ত্রণ" + +BFrames="বি-ফ্রেম" + +NVENC.Use2Pass="দুই পাসে এনকোডিং ব্যবহার করো" +NVENC.Preset.default="পূর্ব-নির্ধারিত" +NVENC.Preset.ll="লো-সুপ্ত" +NVENC.Preset.llhq="উচ্চ মান নিম্ন-সুপ্ত" +NVENC.Preset.llhp="লো-সুপ্ত উচ্চ কার্যক্ষমতা" +NVENC.Level="শ্রেনী" + +FFmpegSource="মিডিয়া উৎস" + + + diff --git a/plugins/obs-ffmpeg/data/locale/ca-ES.ini b/plugins/obs-ffmpeg/data/locale/ca-ES.ini index d7057ce..5dd84f3 100644 --- a/plugins/obs-ffmpeg/data/locale/ca-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/ca-ES.ini @@ -23,27 +23,15 @@ LocalFile="Fitxer local" Looping="Bucle" Input="Entrada" InputFormat="Format d'entrada" -ForceFormat="Força la conversió de format" HardwareDecode="Usa la descodificació per maquinari si és disponible" ClearOnMediaEnd="Amaga l'origen en acabar la reproducció" Advanced="Avançat" -AudioBufferSize="Mida de la memòria intermèdia d'àudio (em fotogrames)" -VideoBufferSize="Mida de la memòria intermèdia de vídeo (en fotogrames)" -FrameDropping="Nivell de pèrdua de quadres" -DiscardNone="Cap" -DiscardDefault="Per defecte (paquets invàlids)" -DiscardNonRef="Quadres de no-referència" -DiscardBiDir="Quadres bidireccionals" -DiscardNonIntra="Quadres no-interiors" -DiscardNonKey="Quadres no-clau" -DiscardAll="Tots els marcs (aneu amb compte!)" RestartWhenActivated="Reinicia la reproducció quan la font estigui activa" ColorRange="Gamma de color YUV" ColorRange.Auto="Automàtic" ColorRange.Partial="Parcial" ColorRange.Full="Màxim" - MediaFileFilter.AllMediaFiles="Tots els arxius multimèdia" MediaFileFilter.VideoFiles="Arxius de vídeo" MediaFileFilter.AudioFiles="Arxius d'àudio" diff --git a/plugins/obs-ffmpeg/data/locale/cs-CZ.ini b/plugins/obs-ffmpeg/data/locale/cs-CZ.ini index ac3d601..35920ae 100644 --- a/plugins/obs-ffmpeg/data/locale/cs-CZ.ini +++ b/plugins/obs-ffmpeg/data/locale/cs-CZ.ini @@ -23,26 +23,17 @@ LocalFile="Místní soubor" Looping="Opakovat" Input="Vstup" InputFormat="Formát vstupu" -ForceFormat="Nařídit převod formátu" HardwareDecode="Použít hardwarové dekódování, pokud je k dispozici" ClearOnMediaEnd="Skrýt zdroj po skončení přehrávání" Advanced="Pokročilé" -AudioBufferSize="Velikost bufferu zvuku (snímky)" -VideoBufferSize="Velikost bufferu videa (snímky)" -FrameDropping="Úroveň ztrácení snímků" -DiscardNone="Žádné" -DiscardDefault="Výchozí (chybné packety)" -DiscardNonRef="Snímky bez reference" -DiscardBiDir="Oboustranné snímky" -DiscardNonIntra="Snímky mimo rámec" -DiscardNonKey="Ne-klíčové snímky" -DiscardAll="Všechny snímky (Pozor!)" RestartWhenActivated="Restartovat přehrávání poté, co je zdroj aktivován" +CloseFileWhenInactive="Zavřít soubor při neaktivitě" +CloseFileWhenInactive.ToolTip="Uzavře soubor poté, co již není zdroj zobrazen ve vysílání či\nnahrávání. Toto umožňuje, aby byl tento soubor změněn, když není zdroj aktivní,\nale může dojít ke prodlení při znovu aktivaci zdroje." ColorRange="Rozsah barev YUV" ColorRange.Auto="Automatický" ColorRange.Partial="Částečný" ColorRange.Full="Celkový" - +RestartMedia="Restartovat mediální zdroj" MediaFileFilter.AllMediaFiles="Všechny mediální soubory" MediaFileFilter.VideoFiles="Video soubory" diff --git a/plugins/obs-ffmpeg/data/locale/da-DK.ini b/plugins/obs-ffmpeg/data/locale/da-DK.ini index 457e266..ce09905 100644 --- a/plugins/obs-ffmpeg/data/locale/da-DK.ini +++ b/plugins/obs-ffmpeg/data/locale/da-DK.ini @@ -23,26 +23,17 @@ LocalFile="Lokal fil" Looping="Gentagelse" Input="Input" InputFormat="Input format" -ForceFormat="Gennemtving-formatkonvertering" HardwareDecode="Brug hardwareafkodning når tilgængelige" ClearOnMediaEnd="Skjul kilde når afspilning slutter" Advanced="Avanceret" -AudioBufferSize="Audio bufferstørrelse (frames)" -VideoBufferSize="Video bufferstørrelse (frames)" -FrameDropping="Billedtabsniveau" -DiscardNone="Ingen" -DiscardDefault="Standard (ugyldige pakker)" -DiscardNonRef="Ikke-reference billeder" -DiscardBiDir="Tovejs frames" -DiscardNonIntra="Non-Intra frames" -DiscardNonKey="Non-Key Frames" -DiscardAll="Alle frames (pas på!)" RestartWhenActivated="Genstart afspilning når kilde bliver aktiv" +CloseFileWhenInactive="Luk fil når inaktiv" +CloseFileWhenInactive.ToolTip="Lukker filen, når kilden ikke vises i streamen ellerr\noptagelsen. Dette muliggør at filen kan ændres, når kilden er ikke aktiv, \nmen der kan være noget opstartsforsinkelse, når kilden genaktiveres." ColorRange="YUV-farveområde" ColorRange.Auto="Auto" ColorRange.Partial="Delvis" ColorRange.Full="Fuld" - +RestartMedia="Genstart Media" MediaFileFilter.AllMediaFiles="Alle mediefiler" MediaFileFilter.VideoFiles="Videofiler" diff --git a/plugins/obs-ffmpeg/data/locale/de-DE.ini b/plugins/obs-ffmpeg/data/locale/de-DE.ini index 026d10d..65c07e6 100644 --- a/plugins/obs-ffmpeg/data/locale/de-DE.ini +++ b/plugins/obs-ffmpeg/data/locale/de-DE.ini @@ -8,7 +8,7 @@ Lossless="Verlustfrei" BFrames="B-frames" -NVENC.Use2Pass="Benutze Two-Pass Encoding" +NVENC.Use2Pass="Benutze Two-Pass Codierung" NVENC.Preset.default="Standard" NVENC.Preset.hq="Hohe Qualität" NVENC.Preset.hp="Hohe Leistung" @@ -23,26 +23,17 @@ LocalFile="Lokale Datei" Looping="Endlosschleife" Input="Eingabe" InputFormat="Eingabeformat" -ForceFormat="Erzwinge Formatkonvertierung" HardwareDecode="Verwende Hardwaredecodierung, falls verfügbar" ClearOnMediaEnd="Quelle verbergen, wenn Wiedergabe endet" Advanced="Erweitert" -AudioBufferSize="Audiopuffergröße (Frames)" -VideoBufferSize="Videopuffergröße (Frames)" -FrameDropping="Frame Dropping Level" -DiscardNone="Keine" -DiscardDefault="Standard (ungültige Pakete)" -DiscardNonRef="Non-Reference Frames" -DiscardBiDir="Bi-Directional Frames" -DiscardNonIntra="Non-Intra Frames" -DiscardNonKey="Non-Key Frames" -DiscardAll="Alle Frames (Vorsicht!)" RestartWhenActivated="Wiedergabe erneut starten, wenn Quelle aktiviert wird" +CloseFileWhenInactive="Datei schließen, wenn inaktiv" +CloseFileWhenInactive.ToolTip="Schließt die Datei, wenn die Quelle im Stream oder der Aufnahme nicht angezeigt wird.\n Dies ermöglicht, dass die Datei geändert wird, wenn die Quelle nicht aktiv ist,\n aber es gibt wahrscheinlich etwas Starverzögerung, wenn die Quelle reaktiviert wird." ColorRange="YUV-Farbmatrix" ColorRange.Auto="Automatisch" ColorRange.Partial="Teilweise" ColorRange.Full="Voll" - +RestartMedia="Medium neu starten" MediaFileFilter.AllMediaFiles="Alle Mediendateien" MediaFileFilter.VideoFiles="Video-Dateien" diff --git a/plugins/obs-ffmpeg/data/locale/el-GR.ini b/plugins/obs-ffmpeg/data/locale/el-GR.ini index e13c43e..ca56385 100644 --- a/plugins/obs-ffmpeg/data/locale/el-GR.ini +++ b/plugins/obs-ffmpeg/data/locale/el-GR.ini @@ -9,15 +9,9 @@ LocalFile="Τοπικό αρχείο" Looping="Επανάληψη" Input="Είσοδος" InputFormat="Μορφή Εισόδου" -ForceFormat="Εξαναγκασμός μετατροπής μορφής" HardwareDecode="Χρήση αποκωδικοποίησης υλικού όταν είναι διαθέσιμη" ClearOnMediaEnd="Απόκρυψη πηγής όταν τελειώνει η αναπαραγωγή" Advanced="Σύνθετες επιλογές" -FrameDropping="Επίπεδο Ρίψης Καρέ" -DiscardNone="Κανένα" -DiscardDefault="Προεπιλογή (άκυρα πακέτα)" -DiscardAll="Όλα τα καρέ (Προσοχή!)" - diff --git a/plugins/obs-ffmpeg/data/locale/en-US.ini b/plugins/obs-ffmpeg/data/locale/en-US.ini index d169833..c2ad7b6 100644 --- a/plugins/obs-ffmpeg/data/locale/en-US.ini +++ b/plugins/obs-ffmpeg/data/locale/en-US.ini @@ -23,26 +23,18 @@ LocalFile="Local File" Looping="Loop" Input="Input" InputFormat="Input Format" -ForceFormat="Force format conversion" +BufferingMB="Network Buffering (MB)" HardwareDecode="Use hardware decoding when available" ClearOnMediaEnd="Hide source when playback ends" Advanced="Advanced" -AudioBufferSize="Audio Buffer Size (frames)" -VideoBufferSize="Video Buffer Size (frames)" -FrameDropping="Frame Dropping Level" -DiscardNone="None" -DiscardDefault="Default (Invalid Packets)" -DiscardNonRef="Non-Reference Frames" -DiscardBiDir="Bi-Directional Frames" -DiscardNonIntra="Non-Intra Frames" -DiscardNonKey="Non-Key Frames" -DiscardAll="All Frames (Careful!)" RestartWhenActivated="Restart playback when source becomes active" +CloseFileWhenInactive="Close file when inactive" +CloseFileWhenInactive.ToolTip="Closes the file when the source is not being displayed on the stream or\nrecording. This allows the file to be changed when the source isn't active,\nbut there may be some startup delay when the source reactivates." ColorRange="YUV Color Range" ColorRange.Auto="Auto" ColorRange.Partial="Partial" ColorRange.Full="Full" - +RestartMedia="Restart Media" MediaFileFilter.AllMediaFiles="All Media Files" MediaFileFilter.VideoFiles="Video Files" diff --git a/plugins/obs-ffmpeg/data/locale/es-ES.ini b/plugins/obs-ffmpeg/data/locale/es-ES.ini index 15e431b..816c4a9 100644 --- a/plugins/obs-ffmpeg/data/locale/es-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/es-ES.ini @@ -23,26 +23,17 @@ LocalFile="Archivo local" Looping="Bucle" Input="Entrada" InputFormat="Formato de entrada" -ForceFormat="Forzar la conversión de formato" HardwareDecode="Utilizar la decodificación por hardware cuando esté disponible" ClearOnMediaEnd="Ocultar la fuente cuando finaliza la reproducción" Advanced="Avanzado" -AudioBufferSize="Tamaño del buffer de audio (cuadros)" -VideoBufferSize="Tamaño del buffer de vídeo (cuadros)" -FrameDropping="Nivel de omisión de cuadros" -DiscardNone="Ninguno" -DiscardDefault="Por defecto (paquetes no válidos)" -DiscardNonRef="Fotogramas referenciar" -DiscardBiDir="Fotogramas bidireccionales" -DiscardNonIntra="Fotogramas no intra-frame" -DiscardNonKey="Fotogramas no claves" -DiscardAll="Todos los fotogramas (¡Cuidado!)" RestartWhenActivated="Reiniciar la reproducción cuando la fuente esté activa" +CloseFileWhenInactive="Cerrar archivo cuando esté inactivo" +CloseFileWhenInactive.ToolTip="Cierra el archivo cuando la fuente no esta siendo mostrada en el directo o en la grabación.\nEsto permite que el archivo pueda ser cambiado cuando la fuente no esta activa,\npero puede haber una espera en el inicio cuando se reactive la fuente." ColorRange="Gama de Color YUV" ColorRange.Auto="Automatico" ColorRange.Partial="Parcial" ColorRange.Full="Completo" - +RestartMedia="Reiniciar Medio" MediaFileFilter.AllMediaFiles="Todos los archivos multimedia" MediaFileFilter.VideoFiles="Archivos de vídeo" diff --git a/plugins/obs-ffmpeg/data/locale/et-EE.ini b/plugins/obs-ffmpeg/data/locale/et-EE.ini index 188cf5d..827cc9a 100644 --- a/plugins/obs-ffmpeg/data/locale/et-EE.ini +++ b/plugins/obs-ffmpeg/data/locale/et-EE.ini @@ -1,7 +1,12 @@ FFmpegOutput="FFmpeg väljund" +FFmpegAAC="FFmpeg vaike AAC kodeerija" Bitrate="Bitikiirus" +Preset="Eelseadistus" +KeyframeIntervalSec="Võtmekaadri intervall (sekundit, 0=automaatne)" +Lossless="Kadudeta" +NVENC.Use2Pass="Kasuta Two-Pass kodeeringut" NVENC.Preset.default="Vaikimisi" NVENC.Preset.hq="Kõrge kvaliteet" NVENC.Preset.hp="Suur jõudlus" @@ -17,13 +22,12 @@ Looping="Korda" Input="Sisend" InputFormat="Sisestus formaat" ClearOnMediaEnd="Peida allikas kui taasesitus lõppeb" -AudioBufferSize="Audio puhvri suurus (kaadrit)" +RestartWhenActivated="Taaskäivita taasesitus, kui allikas muutub aktiivseks" ColorRange="YUV värviruumi vahemik" ColorRange.Auto="Automaatne" ColorRange.Partial="Osaline" ColorRange.Full="Täielik" - MediaFileFilter.AllMediaFiles="Kõik meediumifailid" MediaFileFilter.VideoFiles="Videofailid" MediaFileFilter.AudioFiles="Helifailid" diff --git a/plugins/obs-ffmpeg/data/locale/eu-ES.ini b/plugins/obs-ffmpeg/data/locale/eu-ES.ini index 149ceec..760f171 100644 --- a/plugins/obs-ffmpeg/data/locale/eu-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/eu-ES.ini @@ -23,26 +23,17 @@ LocalFile="Tokiko fitxategia" Looping="Begizta" Input="Sarrera" InputFormat="Sarrera formatua" -ForceFormat="Behartu formatu-bihurketa" HardwareDecode="Erabili hardware deskodeketa eskuragarri dagoenean" ClearOnMediaEnd="Ezkutatu iturburua erreprodukzioa amaitzean" Advanced="Aurreratua" -AudioBufferSize="Audio-bufferraren tamaina (fotogramak)" -VideoBufferSize="Bideo-bufferraren tamaina (fotogramak)" -FrameDropping="Fotogramen erorketa maila" -DiscardNone="Ezer ez" -DiscardDefault="Lehenetsia (pakete baliogabeak)" -DiscardNonRef="Erreferentziarik gabeko fotogramak" -DiscardBiDir="Norabide biko fotogramak" -DiscardNonIntra="Non-Intra fotogramak" -DiscardNonKey="Gakoa ez diren fotogramak" -DiscardAll="Fotograma guztiak (kontuz!)" RestartWhenActivated="Berrabiarazi erreprodukzioa iturburua aktiboa dagoenean" +CloseFileWhenInactive="Itxi fitxategia inaktibo dagoenean" +CloseFileWhenInactive.ToolTip="Itxi fitxategia iturburua ez bada bistaratzen transmisioan edo\ngrabazioan. Honi esker fitxategia alda daiteke iturburua aktiboa ez badago,\nbaina atzerapen bat sor daiteke iturburua biraktibatzean." ColorRange="YUV kolore-barrutia" ColorRange.Auto="Auto" ColorRange.Partial="Partziala" ColorRange.Full="Osoa" - +RestartMedia="Berrabiarazi euskarria" MediaFileFilter.AllMediaFiles="Multimedia-fitxategi guztiak" MediaFileFilter.VideoFiles="Bideo-fitxategiak" diff --git a/plugins/obs-ffmpeg/data/locale/fi-FI.ini b/plugins/obs-ffmpeg/data/locale/fi-FI.ini index fc78791..ca9af62 100644 --- a/plugins/obs-ffmpeg/data/locale/fi-FI.ini +++ b/plugins/obs-ffmpeg/data/locale/fi-FI.ini @@ -23,26 +23,17 @@ LocalFile="Paikallinen tiedosto" Looping="Toista jatkuvasti" Input="Sisääntulo" InputFormat="Sisääntulon muoto" -ForceFormat="Pakota muodon muuntaminen" HardwareDecode="Käytä laitteistotason purkua, kun mahdollista" ClearOnMediaEnd="Piilota lähde kun toisto päättyy" Advanced="Lisäasetukset" -AudioBufferSize="Äänipuskurin koko (ruutua)" -VideoBufferSize="Videopuskurin koko (ruutua)" -FrameDropping="Frame Dropping -taso" -DiscardNone="Ei mikään" -DiscardDefault="Oletus (virheelliset paketit)" -DiscardNonRef="Non-Reference Frames" -DiscardBiDir="Bi-Directional Frames" -DiscardNonIntra="Non-Intra Frames" -DiscardNonKey="Non-Key Frames" -DiscardAll="All Frames (Varoitus!)" RestartWhenActivated="Aloita toisto uudelleen kun lähde aktivoituu" +CloseFileWhenInactive="Sulje tiedosto, kun toimeton" +CloseFileWhenInactive.ToolTip="Sulkee tiedoston kun lähdettä ei näytetä lähetyksessä tai nauhoituksessa.\nTämä mahdollistaa tiedoston muuttamisen kun lähde ei ole aktiivinen,\nmutta se saattaa aiheuttaa pientä viivettä käynnistyksessä kun tiedosto aktivoituu uudelleen." ColorRange="YUV värialue" ColorRange.Auto="Automaattinen" ColorRange.Partial="Osittainen" ColorRange.Full="Täysi" - +RestartMedia="Uudelleenkäynnistä media" MediaFileFilter.AllMediaFiles="Kaikki mediatiedostot" MediaFileFilter.VideoFiles="Videotiedostot" diff --git a/plugins/obs-ffmpeg/data/locale/fr-FR.ini b/plugins/obs-ffmpeg/data/locale/fr-FR.ini index 5e08037..5396931 100644 --- a/plugins/obs-ffmpeg/data/locale/fr-FR.ini +++ b/plugins/obs-ffmpeg/data/locale/fr-FR.ini @@ -23,26 +23,17 @@ LocalFile="Fichier local" Looping="En boucle" Input="Entrée" InputFormat="Format d'entrée" -ForceFormat="Forcer la conversion du format" HardwareDecode="Utiliser le décodage matériel si possible" ClearOnMediaEnd="Cacher la source lorsque la lecture est finie" Advanced="Options avancées" -AudioBufferSize="Taille du tampon audio (en images)" -VideoBufferSize="Taille du tampon vidéo (en images)" -FrameDropping="Niveau de perte d'images" -DiscardNone="Aucune" -DiscardDefault="Par défaut (paquets invalides)" -DiscardNonRef="Images non-références" -DiscardBiDir="Images bidirectionnelles" -DiscardNonIntra="Images non-intra" -DiscardNonKey="Images non-clés" -DiscardAll="Toutes les images (Attention !)" RestartWhenActivated="Reprendre la lecture quand la source est active" +CloseFileWhenInactive="Fermer fichier lorsqu’il est inactif" +CloseFileWhenInactive.ToolTip="Ferme le fichier lorsque la source ne s'affiche pas sur le flux ou \nl'enregistrement. Cela permet de modifier le fichier lorsque la source n'est pas active, \nmais il peut y avoir un délai de démarrage lorsque la source se réactive." ColorRange="Gamme de couleurs YUV" ColorRange.Auto="Auto" ColorRange.Partial="Partielle" ColorRange.Full="Complète" - +RestartMedia="Redémarrez Media" MediaFileFilter.AllMediaFiles="Tous les fichiers multimédias" MediaFileFilter.VideoFiles="Fichiers vidéo" diff --git a/plugins/obs-ffmpeg/data/locale/gl-ES.ini b/plugins/obs-ffmpeg/data/locale/gl-ES.ini index c8d54b6..83e4029 100644 --- a/plugins/obs-ffmpeg/data/locale/gl-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/gl-ES.ini @@ -10,19 +10,9 @@ LocalFile="Ficheiro local" Looping="Bucle" Input="Entrada" InputFormat="Formato de entrada" -ForceFormat="Forzar a conversión do formato" HardwareDecode="Utilizar a descodificación por hárdware cando estiver dispoñible" ClearOnMediaEnd="Agochar a fonte cando a reprodución remata" Advanced="Avanzado" -AudioBufferSize="Tamaño do búfer de audio (marcos)" -VideoBufferSize="Tamaño do búfer de vídeo (marcos)" -FrameDropping="Nivel de omisión de marcos" -DiscardNone="Ningún" -DiscardDefault="Predefinido (paquetes non válidos)" -DiscardNonRef="Marcos sen referencias" -DiscardBiDir="Marcos bidireccionais" -DiscardAll="Todos os marcos (con tino!)" - diff --git a/plugins/obs-ffmpeg/data/locale/he-IL.ini b/plugins/obs-ffmpeg/data/locale/he-IL.ini index 255d8c5..44ca97e 100644 --- a/plugins/obs-ffmpeg/data/locale/he-IL.ini +++ b/plugins/obs-ffmpeg/data/locale/he-IL.ini @@ -22,27 +22,15 @@ LocalFile="קובץ מקומי" Looping="לולאה" Input="קלט" InputFormat="תבנית קלט" -ForceFormat="כפה המרת תבנית" HardwareDecode="השתמש בפענוח חומרה כאשר היא זמין" ClearOnMediaEnd="הסתר את מקור כאשר ההשמעה מסתיימת" Advanced="מתקדם" -AudioBufferSize="גודל מאגר שמע (פריימים)" -VideoBufferSize="גודל מאגר הווידאו (פריימים)" -FrameDropping="רמת נשירת פריימים" -DiscardNone="ללא" -DiscardDefault="ברירת מחדל (מנות לא חוקיות)" -DiscardNonRef="פריימים לא יחס" -DiscardBiDir="פריימים דו-כיוונים" -DiscardNonIntra="פריימים לא ביינים" -DiscardNonKey="מסגרות שאינן מפתח" -DiscardAll="כל הפריימים (זהירות!)" RestartWhenActivated="הפעל מחדש השמעה כאשר מקור הופך לפעיל" ColorRange="טווח צבעים YUV" ColorRange.Auto="אוטומטי" ColorRange.Partial="חלקי" ColorRange.Full="מלא" - MediaFileFilter.AllMediaFiles="כל קבצי המדיה" MediaFileFilter.VideoFiles="קבצי וידאו" MediaFileFilter.AudioFiles="קבצי אודיו" diff --git a/plugins/obs-ffmpeg/data/locale/hi-IN.ini b/plugins/obs-ffmpeg/data/locale/hi-IN.ini new file mode 100644 index 0000000..175db22 --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/hi-IN.ini @@ -0,0 +1,7 @@ +FFmpegOutput="FFmpeg आउटपुट" + + + + + + diff --git a/plugins/obs-ffmpeg/data/locale/hr-HR.ini b/plugins/obs-ffmpeg/data/locale/hr-HR.ini index 1e584a2..004975d 100644 --- a/plugins/obs-ffmpeg/data/locale/hr-HR.ini +++ b/plugins/obs-ffmpeg/data/locale/hr-HR.ini @@ -22,27 +22,15 @@ LocalFile="Lokalna datoteka" Looping="Ponavljanje" Input="Ulaz" InputFormat="Format ulaza" -ForceFormat="Prisilno pretvaranje formata" HardwareDecode="Koristi hardversko enkodiranje kada je dostupno" ClearOnMediaEnd="Sakrij izvor kada se reprodukcija završi" Advanced="Napredno" -AudioBufferSize="Veličina zvučnog bafera (u frejmovima)" -VideoBufferSize="Veličina video bafera (u frejmovima)" -FrameDropping="Nivo ispuštanja frejmova" -DiscardNone="Nijedan" -DiscardDefault="Podrazumevano (neispravni paketi)" -DiscardNonRef="Frejmovi bez reference" -DiscardBiDir="Dvosmerni frejmovi" -DiscardNonIntra="Ne-intra frejmovi" -DiscardNonKey="Frejmovi koji nisu ključni" -DiscardAll="Svi frejmovi (oprezno!)" RestartWhenActivated="Ponovi reprodukciju kada izvor postane aktivan" ColorRange="YUV opseg boja" ColorRange.Auto="Automatski" ColorRange.Partial="Delimični" ColorRange.Full="Potpuni" - MediaFileFilter.AllMediaFiles="Sve medija datoteke" MediaFileFilter.VideoFiles="Video datoteke" MediaFileFilter.AudioFiles="Zvučne datoteke" diff --git a/plugins/obs-ffmpeg/data/locale/hu-HU.ini b/plugins/obs-ffmpeg/data/locale/hu-HU.ini index e0aa725..306e33b 100644 --- a/plugins/obs-ffmpeg/data/locale/hu-HU.ini +++ b/plugins/obs-ffmpeg/data/locale/hu-HU.ini @@ -23,26 +23,17 @@ LocalFile="Helyi fájl" Looping="Ismétlés" Input="Bemenet" InputFormat="Bemeneti formátum" -ForceFormat="Formátum átváltás kényszerítése" HardwareDecode="Hardveres dekódolás használata, ha rendelkezésre áll" ClearOnMediaEnd="Forrás elrejtése a lejátszás végeztével" Advanced="Haladó" -AudioBufferSize="Audio pufferméret (képkockák)" -VideoBufferSize="Video pufferméret (képkockák)" -FrameDropping="Képkocka ejtésszint" -DiscardNone="Semmi" -DiscardDefault="Alapértelmezett (érvénytelen csomagok)" -DiscardNonRef="Nem referencia kockák" -DiscardBiDir="Kétirányú kockák" -DiscardNonIntra="Nem belső kockák" -DiscardNonKey="Nem kulcskockák" -DiscardAll="Összes kocka (óvatosan!)" RestartWhenActivated="Lejátszás újraindítása, ha a forrás aktivizálódik" +CloseFileWhenInactive="Fájl bezárása ha tétlen" +CloseFileWhenInactive.ToolTip="Bezárja a fájlt, ha a forrás nem aktív streamen vagy\nfelvételen. Ez lehetővé teszi, hogy a fájlt meg kell változtatni, ha a forrás nem aktív, a \nviszont felléphet némi indítási késés, ha a forrás reaktiválódik." ColorRange="YUV színtartomány" ColorRange.Auto="Auto" ColorRange.Partial="Részleges" ColorRange.Full="Teljes" - +RestartMedia="Media újraindítása" MediaFileFilter.AllMediaFiles="Minden médiafájl" MediaFileFilter.VideoFiles="Videofájlok" diff --git a/plugins/obs-ffmpeg/data/locale/it-IT.ini b/plugins/obs-ffmpeg/data/locale/it-IT.ini index 193377d..0398a83 100644 --- a/plugins/obs-ffmpeg/data/locale/it-IT.ini +++ b/plugins/obs-ffmpeg/data/locale/it-IT.ini @@ -23,27 +23,15 @@ LocalFile="File locale" Looping="Ripeti" Input="Input" InputFormat="Formato di input" -ForceFormat="Forza conversione di formato" HardwareDecode="Utilizza la decodifica hardware quando disponibile" ClearOnMediaEnd="Nascondi la fonte quando termina la riproduzione" Advanced="Avanzate" -AudioBufferSize="Dimensione del Buffer audio (fotogrammi)" -VideoBufferSize="Dimensioni del Buffer video (fotogrammi)" -FrameDropping="Frame Dropping Level" -DiscardNone="Nessuno" -DiscardDefault="Predefinito (pacchetti non validi)" -DiscardNonRef="Frame non di riferimento" -DiscardBiDir="Frame bi-direzionali" -DiscardNonIntra="Frame non interposti" -DiscardNonKey="Frame non di chiave" -DiscardAll="Tutti i Frame (opzione per utenti più esperti)" RestartWhenActivated="Riattiva playback quando la fonte torna attiva" ColorRange="Gamma di colore YUV" ColorRange.Auto="Autom." ColorRange.Partial="Parziale" ColorRange.Full="Intero" - MediaFileFilter.AllMediaFiles="Tutti i file media" MediaFileFilter.VideoFiles="File video" MediaFileFilter.AudioFiles="File audio" diff --git a/plugins/obs-ffmpeg/data/locale/ja-JP.ini b/plugins/obs-ffmpeg/data/locale/ja-JP.ini index f53ab12..693b2c7 100644 --- a/plugins/obs-ffmpeg/data/locale/ja-JP.ini +++ b/plugins/obs-ffmpeg/data/locale/ja-JP.ini @@ -23,26 +23,17 @@ LocalFile="ローカルファイル" Looping="繰り返し" Input="入力" InputFormat="入力フォーマット" -ForceFormat="強制的にフォーマットを変換" HardwareDecode="可能な場合ハードウェアデコードを使用" ClearOnMediaEnd="再生終了時にソースを非表示にする" Advanced="高度な設定" -AudioBufferSize="音声バッファーサイズ (フレーム)" -VideoBufferSize="映像バッファーサイズ (フレーム)" -FrameDropping="フレームドロップレベル" -DiscardNone="なし" -DiscardDefault="既定 (無効なパケット)" -DiscardNonRef="非参照フレーム" -DiscardBiDir="双方向フレーム" -DiscardNonIntra="非イントラフレーム" -DiscardNonKey="非キーフレーム" -DiscardAll="すべてのフレーム (注意!)" RestartWhenActivated="ソースがアクティブになったときに再生を再開する" +CloseFileWhenInactive="アクティブでないときにファイルを閉じる" +CloseFileWhenInactive.ToolTip="ソースが配信/録画に表示されていない時にファイルを閉じます。\nこれによりソースがアクティブでない時にファイルを変更することができますが、\nソースが再度アクティブになる際に開始の遅延が発生することがあります。" ColorRange="YUV 色範囲" ColorRange.Auto="自動" ColorRange.Partial="一部" ColorRange.Full="全部" - +RestartMedia="メディアを再開する" MediaFileFilter.AllMediaFiles="すべてのメディアファイル" MediaFileFilter.VideoFiles="ビデオファイル" diff --git a/plugins/obs-ffmpeg/data/locale/ko-KR.ini b/plugins/obs-ffmpeg/data/locale/ko-KR.ini index 776b398..fb8dec3 100644 --- a/plugins/obs-ffmpeg/data/locale/ko-KR.ini +++ b/plugins/obs-ffmpeg/data/locale/ko-KR.ini @@ -23,26 +23,17 @@ LocalFile="로컬 파일" Looping="반복" Input="입력" InputFormat="입력 형식" -ForceFormat="강제 형식 전환" HardwareDecode="가능한 경우 하드웨어 디코딩 사용" ClearOnMediaEnd="재생이 끝나면 소스를 숨기기" Advanced="고급" -AudioBufferSize="오디오 버퍼 크기 (프레임)" -VideoBufferSize="비디오 버퍼 크기 (프레임)" -FrameDropping="프레임 손실 수준" -DiscardNone="없음" -DiscardDefault="기본 (유효하지 않은 패킷)" -DiscardNonRef="비 참조 프레임" -DiscardBiDir="양방향 프레임" -DiscardNonIntra="비 내부 프레임" -DiscardNonKey="비 키 프레임" -DiscardAll="모든 프레임 (주의!)" RestartWhenActivated="소스가 활성화될 때 재생을 다시 시작" +CloseFileWhenInactive="비활성화 상태일 때 파일 닫기" +CloseFileWhenInactive.ToolTip="소스가 방송이나 \n녹화에 표시가 되지 않으면 파일을 닫습니다. 이 설정을 사용하면 소스가 활성화되어 있지 않을 때, 파일을 수정할 수 있습니다.\n하지만 다시 활성화를 시작할 때 약간의 지연이 일어날 수 있습니다." ColorRange="YUV 색상 범위" ColorRange.Auto="자동" ColorRange.Partial="부분" ColorRange.Full="전체" - +RestartMedia="미디어 다시재생" MediaFileFilter.AllMediaFiles="모든 미디어 파일" MediaFileFilter.VideoFiles="비디오 파일" diff --git a/plugins/obs-ffmpeg/data/locale/nb-NO.ini b/plugins/obs-ffmpeg/data/locale/nb-NO.ini index ab7f0ae..03c1131 100644 --- a/plugins/obs-ffmpeg/data/locale/nb-NO.ini +++ b/plugins/obs-ffmpeg/data/locale/nb-NO.ini @@ -22,27 +22,15 @@ LocalFile="Lokal fil" Looping="Repeter" Input="Inngang" InputFormat="Inngangsformat" -ForceFormat="Tving formatkonvertering" HardwareDecode="Bruk maskinvaredekoding når tilgjengelig" ClearOnMediaEnd="Skjul kilde når avspilling ender" Advanced="Avansert" -AudioBufferSize="Lydbufferstørrelsen (i bilder)" -VideoBufferSize="Videobufferstørrelsen (i bilder)" -FrameDropping="Bildeforkastingsnivå" -DiscardNone="Ingen" -DiscardDefault="Standard (ugyldige pakker)" -DiscardNonRef="Ikkereferansebilder" -DiscardBiDir="Toveisbilder" -DiscardNonIntra="Non-intra bilder" -DiscardNonKey="Ikkenøkkelbilder" -DiscardAll="Alle bilder (forsiktig!)" RestartWhenActivated="Start avspilling omigjen når kilde blir aktiv" ColorRange="YUV fargerom" ColorRange.Auto="Automatisk" ColorRange.Partial="Delvis" ColorRange.Full="Hel" - MediaFileFilter.AllMediaFiles="Alle mediefiler" MediaFileFilter.VideoFiles="Videofiler" MediaFileFilter.AudioFiles="Lydfiler" diff --git a/plugins/obs-ffmpeg/data/locale/nl-NL.ini b/plugins/obs-ffmpeg/data/locale/nl-NL.ini index 480be4a..67b619c 100644 --- a/plugins/obs-ffmpeg/data/locale/nl-NL.ini +++ b/plugins/obs-ffmpeg/data/locale/nl-NL.ini @@ -23,26 +23,17 @@ LocalFile="Lokaal bestand" Looping="Herhalen" Input="Invoer" InputFormat="Invoerformaat" -ForceFormat="Forceer formaatconversie" HardwareDecode="Gebruik hardware-decoding wanneer mogelijk" ClearOnMediaEnd="Verberg de bron na het afspelen" Advanced="Geavanceerd" -AudioBufferSize="Audio Buffergrootte (frames)" -VideoBufferSize="Video Buffergrootte (frames)" -FrameDropping="Frame-Dropping Niveau" -DiscardNone="Geen" -DiscardDefault="Standaard (Ongeldige Pakketten)" -DiscardNonRef="Niet-Reference Frames" -DiscardBiDir="Bi-Directionele Frames" -DiscardNonIntra="Niet-Intra Frames" -DiscardNonKey="Niet-Key Frames" -DiscardAll="Alle Frames (Voorzichtig!)" RestartWhenActivated="Opnieuw starten met afspelen zodra de bron actief wordt" +CloseFileWhenInactive="Sluit bestand wanneer niet actief" +CloseFileWhenInactive.ToolTip="Het bestand wordt afgesloten wanneer de bron niet wordt weergegeven op de stream of\nopname. Hierdoor kan het bestand worden gewijzigd wanneer de bron niet actief is, \ner kan echter enige opstartvertraging zijn wanneer de bron opnieuw geactiveerd wordt." ColorRange="YUV Kleurbereik" ColorRange.Auto="Automatisch" ColorRange.Partial="Gedeeltelijk" ColorRange.Full="Volledig" - +RestartMedia="Media herstarten" MediaFileFilter.AllMediaFiles="Alle mediabestanden" MediaFileFilter.VideoFiles="Videobestanden" diff --git a/plugins/obs-ffmpeg/data/locale/pl-PL.ini b/plugins/obs-ffmpeg/data/locale/pl-PL.ini index 250b3f7..ae1f281 100644 --- a/plugins/obs-ffmpeg/data/locale/pl-PL.ini +++ b/plugins/obs-ffmpeg/data/locale/pl-PL.ini @@ -23,26 +23,17 @@ LocalFile="Plik lokalny" Looping="Pętla" Input="Wejście" InputFormat="Format wejściowy" -ForceFormat="Wymuś konwersję formatu" HardwareDecode="Użyj sprzętowego dekodowania gdy to możliwe" ClearOnMediaEnd="Ukryj źródło po zakończeniu odtwarzania" Advanced="Zaawansowane" -AudioBufferSize="Bufor audio (w klatkach)" -VideoBufferSize="Bufor video (w klatkach)" -FrameDropping="Poziom gubienia klatek" -DiscardNone="Bez gubienia" -DiscardDefault="Domyślny (Nieprawidłowe pakiety)" -DiscardNonRef="Klatki niereferencyjne" -DiscardBiDir="Klatki dwukierunkowe (bi-directional)" -DiscardNonIntra="Klatki niewewnętrzne (non-intra)" -DiscardNonKey="Klatki niekluczowe (non-key)" -DiscardAll="Wszystkie klatki (Ostrożnie!)" RestartWhenActivated="Zrestartuj odtwarzanie, gdy źródła będą aktywne" +CloseFileWhenInactive="Zamknij plik, gdy niekatywne" +CloseFileWhenInactive.ToolTip="Plik jest zamykany, gdy źródło nie jest wyświetlane.\nPozwala to na zmianę pliku, gdy źródło nie jest aktywne\nmoże jednak opóźniać włączenie źródła w momencie jego aktywacji." ColorRange="Zakres kolorów YUV" ColorRange.Auto="Automatycznie" ColorRange.Partial="Częściowy" ColorRange.Full="Pełny" - +RestartMedia="Zrestartuj plik audio-wideo" MediaFileFilter.AllMediaFiles="Wszystkie pliki multimedialne" MediaFileFilter.VideoFiles="Pliki video" diff --git a/plugins/obs-ffmpeg/data/locale/pt-BR.ini b/plugins/obs-ffmpeg/data/locale/pt-BR.ini index ae80659..49d4a95 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-BR.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-BR.ini @@ -23,26 +23,17 @@ LocalFile="Arquivo Local" Looping="Loop" Input="Entrada" InputFormat="Formato de entrada" -ForceFormat="Forçar conversão de formato" HardwareDecode="Utilizar descodificação de hardware quando disponível" ClearOnMediaEnd="Ocultar fonte quando a reprodução terminar" Advanced="Avançado" -AudioBufferSize="Tamanho do buffer de áudio (frames)" -VideoBufferSize="Tamanho de Buffer do vídeo (frames)" -FrameDropping="Nível de frame drop" -DiscardNone="Nenhum" -DiscardDefault="Padrão (pacotes inválidos)" -DiscardNonRef="Sem frames de referencia" -DiscardBiDir="Frames Bi-direcionais" -DiscardNonIntra="Sem intra-frames" -DiscardNonKey="Sem keyframes" -DiscardAll="Todos os frames(cuidado!)" RestartWhenActivated="Reiniciar reprodução quando a fonte se tornar ativa" +CloseFileWhenInactive="Fechar arquivo quando inativo" +CloseFileWhenInactive.ToolTip="Fechar o arquivo quando a fonte não estiver sendo exibida na transmissão\n ou gravação. Isto permite alterar o arquivo quando a fonte não está ativa,\nmas pode ter algum atraso de inicialização quando a fonte for reativada." ColorRange="Intervalo de Cores YUV" ColorRange.Auto="Auto" ColorRange.Partial="Parcial" ColorRange.Full="Completo" - +RestartMedia="Reiniciar Mídia" MediaFileFilter.AllMediaFiles="Todos Arquivos de Mídia" MediaFileFilter.VideoFiles="Arquivos de Vídeo" diff --git a/plugins/obs-ffmpeg/data/locale/pt-PT.ini b/plugins/obs-ffmpeg/data/locale/pt-PT.ini index 36e96f1..471d343 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-PT.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-PT.ini @@ -21,27 +21,15 @@ LocalFile="Ficheiro local" Looping="Repetir" Input="Entrada" InputFormat="Formato de entrada" -ForceFormat="Forçar conversão de formato" HardwareDecode="Utilizar descodificação de hardware quando disponível" ClearOnMediaEnd="Ocultar fonte quando a reprodução terminar" Advanced="Avançado" -AudioBufferSize="Tamanho do buffer de áudio (fotogramas)" -VideoBufferSize="Tamanho do buffer de vídeo (fotogramas)" -FrameDropping="Nível de perda de fotogramas" -DiscardNone="Nenhum" -DiscardDefault="Padrão (pacotes inválidos)" -DiscardNonRef="Fotogramas sem referência" -DiscardBiDir="Fotogramas bidirecionais" -DiscardNonIntra="Fotogramas não internos" -DiscardNonKey="Fotogramas não registados" -DiscardAll="Todos os fotogramas (cuidado!)" RestartWhenActivated="Reiniciar reprodução quando a fonte se torna ativo" ColorRange="Gama de cor YUV" ColorRange.Auto="Auto" ColorRange.Partial="Parcial" ColorRange.Full="Completo" - MediaFileFilter.AllMediaFiles="Todos os Arquivos de Media" MediaFileFilter.VideoFiles="Arquivos de Vídeo" MediaFileFilter.AudioFiles="Arquivos de Áudio" diff --git a/plugins/obs-ffmpeg/data/locale/ro-RO.ini b/plugins/obs-ffmpeg/data/locale/ro-RO.ini index 749198f..6be3b7e 100644 --- a/plugins/obs-ffmpeg/data/locale/ro-RO.ini +++ b/plugins/obs-ffmpeg/data/locale/ro-RO.ini @@ -13,27 +13,15 @@ LocalFile="Fișier local" Looping="Buclă" Input="Intrare" InputFormat="Format de intrare" -ForceFormat="Forțează conversia formatului" HardwareDecode="Folosește decodarea hardware când este disponibilă" ClearOnMediaEnd="Ascunde sursa atunci când se termină redarea" Advanced="Avansat" -AudioBufferSize="Dimensiune pentru bufferul audio (cadre)" -VideoBufferSize="Dimensiune pentru bufferul video (cadre)" -FrameDropping="Nivel de pierdere al cadrelor" -DiscardNone="Niciunul" -DiscardDefault="Implicit (Pachete invalide)" -DiscardNonRef="Cadre fără referință" -DiscardBiDir="Cadre bidirecționale" -DiscardNonIntra="Cadre non-intra" -DiscardNonKey="Cadre non-cheie" -DiscardAll="Toate cadrele (Atenție!)" RestartWhenActivated="Repornește redarea când sursa devine activă" ColorRange="Gamă de culori YUV" ColorRange.Auto="Auto" ColorRange.Partial="Parțială" ColorRange.Full="Completă" - MediaFileFilter.AllMediaFiles="Toate fișierele media" MediaFileFilter.VideoFiles="Fișiere video" MediaFileFilter.AudioFiles="Fișiere audio" diff --git a/plugins/obs-ffmpeg/data/locale/ru-RU.ini b/plugins/obs-ffmpeg/data/locale/ru-RU.ini index 92cb9fa..0727f1d 100644 --- a/plugins/obs-ffmpeg/data/locale/ru-RU.ini +++ b/plugins/obs-ffmpeg/data/locale/ru-RU.ini @@ -23,26 +23,17 @@ LocalFile="Локальный файл" Looping="Повтор" Input="Ввод" InputFormat="Формат ввода" -ForceFormat="Принудительно конвертировать формат" HardwareDecode="Использовать аппаратное декодирование при наличии" ClearOnMediaEnd="Скрывать источник, когда воспроизведение заканчивается" Advanced="Дополнительно" -AudioBufferSize="Размер аудиобуфера (в кадрах)" -VideoBufferSize="Размер видеобуфера (в кадрах)" -FrameDropping="Уровень пропуска кадров" -DiscardNone="Нет" -DiscardDefault="По умолчанию (неисправные пакеты)" -DiscardNonRef="Неопорные кадры" -DiscardBiDir="Двунаправленные кадры" -DiscardNonIntra="Невнутренние кадры" -DiscardNonKey="Неключевые кадры" -DiscardAll="Все кадры (осторожно!)" RestartWhenActivated="Перезапустить воспроизведение, когда источник становится активным" +CloseFileWhenInactive="Закрыть файл при отсутствии активности" +CloseFileWhenInactive.ToolTip="Закрывает файл, когда источник не отображается во время трансляции или записи.\nЭто позволяет изменять файл, когда источник не активен, но могут возникнуть\nзадержки запуска при повторной активации источника." ColorRange="Цветовой диапазон YUV" ColorRange.Auto="Автоматически" ColorRange.Partial="Частичный" ColorRange.Full="Полный" - +RestartMedia="Перезапустить медиа" MediaFileFilter.AllMediaFiles="Все медиа-файлы" MediaFileFilter.VideoFiles="Видеофайлы" diff --git a/plugins/obs-ffmpeg/data/locale/sk-SK.ini b/plugins/obs-ffmpeg/data/locale/sk-SK.ini index 593adbe..54e7fa2 100644 --- a/plugins/obs-ffmpeg/data/locale/sk-SK.ini +++ b/plugins/obs-ffmpeg/data/locale/sk-SK.ini @@ -6,8 +6,6 @@ Bitrate="Bitrate" Looping="Slučka" Advanced="Rozšírené" -DiscardNone="Žiadny" - diff --git a/plugins/obs-ffmpeg/data/locale/sl-SI.ini b/plugins/obs-ffmpeg/data/locale/sl-SI.ini index 0004a8e..60e05a8 100644 --- a/plugins/obs-ffmpeg/data/locale/sl-SI.ini +++ b/plugins/obs-ffmpeg/data/locale/sl-SI.ini @@ -9,21 +9,9 @@ LocalFile="Lokalna Datoteka" Looping="Ponavljaj" Input="Vhod" InputFormat="Format vnosa" -ForceFormat="Prisili spremembo formata" HardwareDecode="Uporabi strojno pospeševanje, ko je na voljo" ClearOnMediaEnd="Skri vir, ko se predvajanje konča" Advanced="Napredno" -AudioBufferSize="Velikost Zvočnega medpomnilnika (frames)" -VideoBufferSize="Velikost video medpomnolnika (frames)" -FrameDropping="Raven izpuščanja framov" -DiscardNone="Nobeno" -DiscardDefault="Prevzeto (Neveljavni Paketi)" -DiscardNonRef="Nereferenčne Frame" -DiscardBiDir="Dvosmerne Frame" -DiscardNonIntra="Non-Intra Frames" -DiscardNonKey="Non-Key Frames" -DiscardAll="Vse Frame (Pazite!)" - diff --git a/plugins/obs-ffmpeg/data/locale/sr-CS.ini b/plugins/obs-ffmpeg/data/locale/sr-CS.ini index 1e584a2..004975d 100644 --- a/plugins/obs-ffmpeg/data/locale/sr-CS.ini +++ b/plugins/obs-ffmpeg/data/locale/sr-CS.ini @@ -22,27 +22,15 @@ LocalFile="Lokalna datoteka" Looping="Ponavljanje" Input="Ulaz" InputFormat="Format ulaza" -ForceFormat="Prisilno pretvaranje formata" HardwareDecode="Koristi hardversko enkodiranje kada je dostupno" ClearOnMediaEnd="Sakrij izvor kada se reprodukcija završi" Advanced="Napredno" -AudioBufferSize="Veličina zvučnog bafera (u frejmovima)" -VideoBufferSize="Veličina video bafera (u frejmovima)" -FrameDropping="Nivo ispuštanja frejmova" -DiscardNone="Nijedan" -DiscardDefault="Podrazumevano (neispravni paketi)" -DiscardNonRef="Frejmovi bez reference" -DiscardBiDir="Dvosmerni frejmovi" -DiscardNonIntra="Ne-intra frejmovi" -DiscardNonKey="Frejmovi koji nisu ključni" -DiscardAll="Svi frejmovi (oprezno!)" RestartWhenActivated="Ponovi reprodukciju kada izvor postane aktivan" ColorRange="YUV opseg boja" ColorRange.Auto="Automatski" ColorRange.Partial="Delimični" ColorRange.Full="Potpuni" - MediaFileFilter.AllMediaFiles="Sve medija datoteke" MediaFileFilter.VideoFiles="Video datoteke" MediaFileFilter.AudioFiles="Zvučne datoteke" diff --git a/plugins/obs-ffmpeg/data/locale/sr-SP.ini b/plugins/obs-ffmpeg/data/locale/sr-SP.ini index db30444..3d4700d 100644 --- a/plugins/obs-ffmpeg/data/locale/sr-SP.ini +++ b/plugins/obs-ffmpeg/data/locale/sr-SP.ini @@ -22,27 +22,15 @@ LocalFile="Локална датотека" Looping="Понављање" Input="Улаз" InputFormat="Формат улаза" -ForceFormat="Присилно претварање формата" HardwareDecode="Користи хардверско енкодирање када је доступно" ClearOnMediaEnd="Сакриј извор када се репродукција заврши" Advanced="Напредно" -AudioBufferSize="Величина звучног бафера (у фрејмовима)" -VideoBufferSize="Величина видео бафера (у фрејмовима)" -FrameDropping="Ниво испуштања фрејмова" -DiscardNone="Ниједан" -DiscardDefault="Подразумевано (неисправни фрејмови)" -DiscardNonRef="Фрејмови без референце" -DiscardBiDir="Двосмерни фрејмови" -DiscardNonIntra="Не-интра фрејмови" -DiscardNonKey="Фрејмови који нису кључни" -DiscardAll="Сви фрејмови (опрезно!)" RestartWhenActivated="Понови репродукцију када извор постане активан" ColorRange="YUV опсег боја" ColorRange.Auto="Аутоматски" ColorRange.Partial="Делимични" ColorRange.Full="Потпуни" - MediaFileFilter.AllMediaFiles="Све медија датотеке" MediaFileFilter.VideoFiles="Видео датотеке" MediaFileFilter.AudioFiles="Звучне датотеке" diff --git a/plugins/obs-ffmpeg/data/locale/sv-SE.ini b/plugins/obs-ffmpeg/data/locale/sv-SE.ini index 4185cbd..01fbaa3 100644 --- a/plugins/obs-ffmpeg/data/locale/sv-SE.ini +++ b/plugins/obs-ffmpeg/data/locale/sv-SE.ini @@ -23,26 +23,17 @@ LocalFile="Lokal fil" Looping="Upprepa" Input="Infoga" InputFormat="Inmatningsformat" -ForceFormat="Tvinga formatkonvertering" HardwareDecode="Använda hårdvareavkodning när tillgängligt" ClearOnMediaEnd="Dölja källa när uppspelningen slutar" Advanced="Avancerat" -AudioBufferSize="Ljudbuffertstorlek (bilder)" -VideoBufferSize="Videobuffertstorlek (bilder)" -FrameDropping="Bild droppnivå" -DiscardNone="Ingen" -DiscardDefault="Standard (ogiltig paket)" -DiscardNonRef="Icke-referensbildrutor" -DiscardBiDir="Dubbelriktade bilder" -DiscardNonIntra="Icke-Intra bilder" -DiscardNonKey="Icke-nyckel bild" -DiscardAll="Alla bilder (Varsam!)" RestartWhenActivated="Starta om uppspelning när källa blir aktiv" +CloseFileWhenInactive="Stäng filen vid inaktivitet" +CloseFileWhenInactive.ToolTip="Stänger filen när källan inte visas i strömmen vid\ninspelning. Detta låter filen att ändras när källan inte är aktiv,\nmen en liten fördröjning kan uppstå när källan aktiveras igen." ColorRange="YUV-färgområde" ColorRange.Auto="Automatisk" ColorRange.Partial="Delvis" ColorRange.Full="Full" - +RestartMedia="Starta om media" MediaFileFilter.AllMediaFiles="Alla mediafiler" MediaFileFilter.VideoFiles="Videofiler" diff --git a/plugins/obs-ffmpeg/data/locale/th-TH.ini b/plugins/obs-ffmpeg/data/locale/th-TH.ini index 8e88f66..d47c30b 100644 --- a/plugins/obs-ffmpeg/data/locale/th-TH.ini +++ b/plugins/obs-ffmpeg/data/locale/th-TH.ini @@ -5,4 +5,3 @@ Bitrate="บิตเรท" - diff --git a/plugins/obs-ffmpeg/data/locale/tr-TR.ini b/plugins/obs-ffmpeg/data/locale/tr-TR.ini index 051af38..4ab85ca 100644 --- a/plugins/obs-ffmpeg/data/locale/tr-TR.ini +++ b/plugins/obs-ffmpeg/data/locale/tr-TR.ini @@ -23,26 +23,17 @@ LocalFile="Yerel Dosya" Looping="Döngü" Input="Giriş" InputFormat="Giriş Biçimi" -ForceFormat="Dosya biçim dönüşümünü zorla" HardwareDecode="Kullanılabilir ise, donanım kod çözmeyi kullan" ClearOnMediaEnd="Kayıttan yürütme bittiğinde kaynağı gizle" Advanced="Gelişmiş" -AudioBufferSize="Ses Arabellek Boyutu (kare)" -VideoBufferSize="Video Arabellek Boyutu (kare)" -FrameDropping="Kare Düşüş Seviyesi" -DiscardNone="Hiçbiri" -DiscardDefault="Varsayılan (Geçersiz Paketler)" -DiscardNonRef="Referanssız Kareler" -DiscardBiDir="Çift-Yönlü Kareler" -DiscardNonIntra="Intra Olmayan Kareler" -DiscardNonKey="Anahtar Olmayan Kareler" -DiscardAll="Tüm Kareler (Dikkatli Olun!)" RestartWhenActivated="Yeniden oynatmayı kaynak etkin olduğunda yeniden başlat" +CloseFileWhenInactive="Etkin değilken dosyayı kapat" +CloseFileWhenInactive.ToolTip="Kaynak yayında veya kayıtta gösterilmiyorken dosyayı kapatır.\nBu dosya etkin değilken onun değiştirilmesine izin verir,\nancak kaynak yeniden etkinleşirken başlatmada gecikme olabilir." ColorRange="YUV Renk Aralığı" ColorRange.Auto="Otomatik" ColorRange.Partial="Kısmi" ColorRange.Full="Tam" - +RestartMedia="Ortamı Yeniden Başlat" MediaFileFilter.AllMediaFiles="Tüm Medya Dosyaları" MediaFileFilter.VideoFiles="Video Dosyaları" diff --git a/plugins/obs-ffmpeg/data/locale/uk-UA.ini b/plugins/obs-ffmpeg/data/locale/uk-UA.ini index 1bb839a..8187aa1 100644 --- a/plugins/obs-ffmpeg/data/locale/uk-UA.ini +++ b/plugins/obs-ffmpeg/data/locale/uk-UA.ini @@ -23,26 +23,17 @@ LocalFile="Локальний файл" Looping="Повторювати" Input="Вхід" InputFormat="Вхідний формат" -ForceFormat="Примусове перетворення формату" HardwareDecode="Використовувати апаратне декодування, за наявності" ClearOnMediaEnd="Не відображати джерело, коли відтворення завершено" Advanced="Розширені параметри" -AudioBufferSize="Розмір аудіо буфера, (кадрів)" -VideoBufferSize="Розмір відео буфера, (кадрів)" -FrameDropping="Вид пропущених кадрів" -DiscardNone="Немає" -DiscardDefault="За замовчанням (лише збійні пакети)" -DiscardNonRef="Всі крім опорних кадрів" -DiscardBiDir="Лише В-кадри" -DiscardNonIntra="Всі крім I-кадрів" -DiscardNonKey="Всі крім ключових кадрів" -DiscardAll="Всі кадри (Обережно!)" RestartWhenActivated="Грати з початку, коли джерело стає активним" +CloseFileWhenInactive="Закрити файл, коли неактивен" +CloseFileWhenInactive.ToolTip="Завжди закриває файл, коли джерело не відображається у трансляції чи запису.\nЦе дозволяє редагувати файл, якщо джерело не є активним, але може виникнути\nдеяка затримка запуску, коли джерело повторно активується." ColorRange="YUV, колірний діапазон" ColorRange.Auto="Автовизначення" ColorRange.Partial="Частковий" ColorRange.Full="Повний" - +RestartMedia="Перезапустити медіа" MediaFileFilter.AllMediaFiles="Файли мультимедіа" MediaFileFilter.VideoFiles="Відео" diff --git a/plugins/obs-ffmpeg/data/locale/vi-VN.ini b/plugins/obs-ffmpeg/data/locale/vi-VN.ini index a8f714b..79ebf40 100644 --- a/plugins/obs-ffmpeg/data/locale/vi-VN.ini +++ b/plugins/obs-ffmpeg/data/locale/vi-VN.ini @@ -24,4 +24,3 @@ InputFormat="Định dạng đầu vào" - diff --git a/plugins/obs-ffmpeg/data/locale/zh-CN.ini b/plugins/obs-ffmpeg/data/locale/zh-CN.ini index ffe9e45..8e31c7d 100644 --- a/plugins/obs-ffmpeg/data/locale/zh-CN.ini +++ b/plugins/obs-ffmpeg/data/locale/zh-CN.ini @@ -23,26 +23,17 @@ LocalFile="本地文件" Looping="循环" Input="输入" InputFormat="输入格式" -ForceFormat="强制格式转换" HardwareDecode="在可用时使用硬件解码" ClearOnMediaEnd="当播放结束时隐藏源" Advanced="高级" -AudioBufferSize="音频缓冲区大小(帧)" -VideoBufferSize="视频缓冲区大小(帧)" -FrameDropping="帧丢失等级" -DiscardNone="无" -DiscardDefault="默认(无效数据包)" -DiscardNonRef="非参考帧" -DiscardBiDir="双向帧" -DiscardNonIntra="非intra帧" -DiscardNonKey="非关键帧" -DiscardAll="所有的帧(小心!)" RestartWhenActivated="当源变为活动状态时重新启动播放" +CloseFileWhenInactive="非活跃状态时关闭文件" +CloseFileWhenInactive.ToolTip="当源没有显示在推流或者\n录像时关闭文件. 这使得在源不活跃状态时可以更改文件,\n但是当当源重新激活时, 可能有一些启动延迟." ColorRange="YUV 颜色范围" ColorRange.Auto="自动" ColorRange.Partial="局部" ColorRange.Full="全部" - +RestartMedia="重新启动媒体" MediaFileFilter.AllMediaFiles="所有媒体文件" MediaFileFilter.VideoFiles="视频文件" diff --git a/plugins/obs-ffmpeg/data/locale/zh-TW.ini b/plugins/obs-ffmpeg/data/locale/zh-TW.ini index 84149d4..846e7a0 100644 --- a/plugins/obs-ffmpeg/data/locale/zh-TW.ini +++ b/plugins/obs-ffmpeg/data/locale/zh-TW.ini @@ -23,26 +23,17 @@ LocalFile="本機檔案" Looping="循環" Input="輸入" InputFormat="輸入格式" -ForceFormat="強制格式轉換" HardwareDecode="盡可能使用硬體解碼" ClearOnMediaEnd="當播放結束時隱藏來源" Advanced="進階" -AudioBufferSize="音訊緩衝區大小 (訊框)" -VideoBufferSize="影像緩衝區大小 (訊框)" -FrameDropping="丟棄訊框级别" -DiscardNone="無" -DiscardDefault="預設 (無效封包)" -DiscardNonRef="非參考訊框" -DiscardBiDir="雙向訊框" -DiscardNonIntra="非內部訊框" -DiscardNonKey="非關鍵訊框" -DiscardAll="所有訊框(小心!)" RestartWhenActivated="當來源可使用時重新播放" +CloseFileWhenInactive="非使用狀態時關閉檔案" +CloseFileWhenInactive.ToolTip="當來源沒有被用以串流或錄影時關閉檔案。\n這允許來源在非使用狀態時更改檔案,但在\n來源重新啟用時可能會有許些的啟動延遲。" ColorRange="YUV 色彩空間" ColorRange.Auto="自動" ColorRange.Partial="部分" ColorRange.Full="全部" - +RestartMedia="重新播放媒體" MediaFileFilter.AllMediaFiles="所有媒體檔案" MediaFileFilter.VideoFiles="影像檔" diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c index 56565ed..c8df627 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c @@ -646,6 +646,8 @@ int main(int argc, char *argv[]) #ifdef _WIN32 char **argv; + SetErrorMode(SEM_FAILCRITICALERRORS); + argv = malloc(argc * sizeof(char*)); for (int i = 0; i < argc; i++) { size_t len = wcslen(argv_w[i]); diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index 5d5d533..286ae13 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -39,6 +39,7 @@ struct ffmpeg_muxer { obs_output_t *output; os_process_pipe_t *pipe; int64_t stop_ts; + uint64_t total_bytes; struct dstr path; bool sent_headers; volatile bool active; @@ -292,6 +293,7 @@ static bool ffmpeg_mux_start(void *data) /* write headers and start capture */ os_atomic_set_bool(&stream->active, true); os_atomic_set_bool(&stream->capturing, true); + stream->total_bytes = 0; obs_output_begin_data_capture(stream->output, 0); info("Writing file '%s'...", stream->path.array); @@ -374,6 +376,7 @@ static bool write_packet(struct ffmpeg_muxer *stream, return false; } + stream->total_bytes += packet->size; return true; } @@ -460,6 +463,12 @@ static obs_properties_t *ffmpeg_mux_properties(void *unused) return props; } +static uint64_t ffmpeg_mux_total_bytes(void *data) +{ + struct ffmpeg_muxer *stream = data; + return stream->total_bytes; +} + struct obs_output_info ffmpeg_muxer = { .id = "ffmpeg_muxer", .flags = OBS_OUTPUT_AV | @@ -471,6 +480,7 @@ struct obs_output_info ffmpeg_muxer = { .start = ffmpeg_mux_start, .stop = ffmpeg_mux_stop, .encoded_packet = ffmpeg_mux_data, + .get_total_bytes= ffmpeg_mux_total_bytes, .get_properties = ffmpeg_mux_properties }; @@ -542,6 +552,7 @@ static bool replay_buffer_start(void *data) os_atomic_set_bool(&stream->active, true); os_atomic_set_bool(&stream->capturing, true); + stream->total_bytes = 0; obs_output_begin_data_capture(stream->output, 0); return true; @@ -811,5 +822,6 @@ struct obs_output_info replay_buffer = { .start = replay_buffer_start, .stop = ffmpeg_mux_stop, .encoded_packet = replay_buffer_data, + .get_total_bytes= ffmpeg_mux_total_bytes, .get_defaults = replay_buffer_defaults }; diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index fd200d3..6cee357 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -91,6 +91,8 @@ struct ffmpeg_output { bool connecting; pthread_t start_thread; + uint64_t total_bytes; + uint64_t audio_start_ts; uint64_t video_start_ts; uint64_t stop_ts; @@ -893,6 +895,8 @@ static int process_packet(struct ffmpeg_output *output) } } + output->total_bytes += packet.size; + ret = av_interleaved_write_frame(output->ff_data.output, &packet); if (ret < 0) { av_free_packet(&packet); @@ -1051,6 +1055,7 @@ static bool ffmpeg_output_start(void *data) os_atomic_set_bool(&output->stopping, false); output->audio_start_ts = 0; output->video_start_ts = 0; + output->total_bytes = 0; ret = pthread_create(&output->start_thread, NULL, start_thread, output); return (output->connecting = (ret == 0)); @@ -1100,6 +1105,12 @@ static void ffmpeg_deactivate(struct ffmpeg_output *output) ffmpeg_data_free(&output->ff_data); } +static uint64_t ffmpeg_output_total_bytes(void *data) +{ + struct ffmpeg_output *output = data; + return output->total_bytes; +} + struct obs_output_info ffmpeg_output = { .id = "ffmpeg_output", .flags = OBS_OUTPUT_AUDIO | OBS_OUTPUT_VIDEO, @@ -1110,4 +1121,5 @@ struct obs_output_info ffmpeg_output = { .stop = ffmpeg_output_stop, .raw_video = receive_video, .raw_audio = receive_audio, + .get_total_bytes = ffmpeg_output_total_bytes, }; diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-source.c b/plugins/obs-ffmpeg/obs-ffmpeg-source.c index ab026ff..d75ca79 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-source.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-source.c @@ -21,9 +21,7 @@ #include "obs-ffmpeg-compat.h" #include "obs-ffmpeg-formats.h" -#include - -#include +#include #define FF_LOG(level, format, ...) \ blog(level, "[Media Source]: " format, ##__VA_ARGS__) @@ -37,260 +35,31 @@ static bool video_frame(struct ff_frame *frame, void *opaque); static bool video_format(AVCodecContext *codec_context, void *opaque); struct ffmpeg_source { - struct ff_demuxer *demuxer; + mp_media_t media; + bool media_valid; + bool destroy_media; + struct SwsContext *sws_ctx; int sws_width; int sws_height; enum AVPixelFormat sws_format; uint8_t *sws_data; int sws_linesize; + enum video_range_type range; obs_source_t *source; + obs_hotkey_id hotkey; char *input; char *input_format; - enum AVDiscard frame_drop; - enum video_range_type range; - int audio_buffer_size; - int video_buffer_size; - bool is_advanced; + int buffering_mb; bool is_looping; - bool is_forcing_scale; + bool is_local_file; bool is_hw_decoding; bool is_clear_on_media_end; bool restart_on_activate; + bool close_when_inactive; }; -static bool set_obs_frame_colorprops(struct ff_frame *frame, - struct ffmpeg_source *s, struct obs_source_frame *obs_frame) -{ - enum AVColorSpace frame_cs = av_frame_get_colorspace(frame->frame); - enum video_colorspace obs_cs; - - switch(frame_cs) { - case AVCOL_SPC_BT709: obs_cs = VIDEO_CS_709; break; - case AVCOL_SPC_SMPTE170M: - case AVCOL_SPC_BT470BG: obs_cs = VIDEO_CS_601; break; - case AVCOL_SPC_UNSPECIFIED: obs_cs = VIDEO_CS_DEFAULT; break; - default: - FF_BLOG(LOG_WARNING, "frame using an unsupported colorspace %d", - frame_cs); - obs_cs = VIDEO_CS_DEFAULT; - } - - enum video_range_type range; - obs_frame->format = ffmpeg_to_obs_video_format(frame->frame->format); - obs_frame->full_range = - frame->frame->color_range == AVCOL_RANGE_JPEG; - - if (s->range != VIDEO_RANGE_DEFAULT) - obs_frame->full_range = s->range == VIDEO_RANGE_FULL; - - range = obs_frame->full_range ? VIDEO_RANGE_FULL : VIDEO_RANGE_PARTIAL; - - if (!video_format_get_parameters(obs_cs, - range, obs_frame->color_matrix, - obs_frame->color_range_min, - obs_frame->color_range_max)) { - FF_BLOG(LOG_ERROR, "Failed to get video format " - "parameters for video format %u", - obs_cs); - return false; - } - - return true; -} - -bool update_sws_context(struct ffmpeg_source *s, AVFrame *frame) -{ - if (frame->width != s->sws_width - || frame->height != s->sws_height - || frame->format != s->sws_format) { - if (s->sws_ctx != NULL) - sws_freeContext(s->sws_ctx); - - if (frame->width <= 0 || frame->height <= 0) { - FF_BLOG(LOG_ERROR, "unable to create a sws " - "context that has a width(%d) or " - "height(%d) of zero.", frame->width, - frame->height); - goto fail; - } - - s->sws_ctx = sws_getContext( - frame->width, - frame->height, - frame->format, - frame->width, - frame->height, - AV_PIX_FMT_BGRA, - SWS_BILINEAR, - NULL, NULL, NULL); - - if (s->sws_ctx == NULL) { - FF_BLOG(LOG_ERROR, "unable to create sws " - "context with src{w:%d,h:%d,f:%d}->" - "dst{w:%d,h:%d,f:%d}", - frame->width, frame->height, - frame->format, frame->width, - frame->height, AV_PIX_FMT_BGRA); - goto fail; - - } - - if (s->sws_data != NULL) - bfree(s->sws_data); - s->sws_data = bzalloc(frame->width * frame->height * 4); - if (s->sws_data == NULL) { - FF_BLOG(LOG_ERROR, "unable to allocate sws " - "pixel data with size %d", - frame->width * frame->height * 4); - goto fail; - } - - s->sws_linesize = frame->width * 4; - s->sws_width = frame->width; - s->sws_height = frame->height; - s->sws_format = frame->format; - } - - return true; - -fail: - if (s->sws_ctx != NULL) - sws_freeContext(s->sws_ctx); - s->sws_ctx = NULL; - - if (s->sws_data) - bfree(s->sws_data); - s->sws_data = NULL; - - s->sws_linesize = 0; - s->sws_width = 0; - s->sws_height = 0; - s->sws_format = 0; - - return false; -} - -static bool video_frame_scale(struct ff_frame *frame, - struct ffmpeg_source *s, struct obs_source_frame *obs_frame) -{ - if (!update_sws_context(s, frame->frame)) - return false; - - sws_scale( - s->sws_ctx, - (uint8_t const *const *)frame->frame->data, - frame->frame->linesize, - 0, - frame->frame->height, - &s->sws_data, - &s->sws_linesize - ); - - obs_frame->data[0] = s->sws_data; - obs_frame->linesize[0] = s->sws_linesize; - obs_frame->format = VIDEO_FORMAT_BGRA; - - obs_source_output_video(s->source, obs_frame); - - return true; -} - -static bool video_frame_hwaccel(struct ff_frame *frame, - struct ffmpeg_source *s, struct obs_source_frame *obs_frame) -{ - // 4th plane is pixelbuf reference for mac - for (int i = 0; i < 3; i++) { - obs_frame->data[i] = frame->frame->data[i]; - obs_frame->linesize[i] = frame->frame->linesize[i]; - } - - if (!set_obs_frame_colorprops(frame, s, obs_frame)) - return false; - - obs_source_output_video(s->source, obs_frame); - return true; -} - -static bool video_frame_direct(struct ff_frame *frame, - struct ffmpeg_source *s, struct obs_source_frame *obs_frame) -{ - int i; - - for (i = 0; i < MAX_AV_PLANES; i++) { - obs_frame->data[i] = frame->frame->data[i]; - obs_frame->linesize[i] = frame->frame->linesize[i]; - } - - if (!set_obs_frame_colorprops(frame, s, obs_frame)) - return false; - - obs_source_output_video(s->source, obs_frame); - return true; -} - -static bool video_frame(struct ff_frame *frame, void *opaque) -{ - struct ffmpeg_source *s = opaque; - struct obs_source_frame obs_frame = {0}; - uint64_t pts; - - // Media ended - if (frame == NULL) { - if (s->is_clear_on_media_end) - obs_source_output_video(s->source, NULL); - return true; - } - - pts = (uint64_t)(frame->pts * 1000000000.0L); - - obs_frame.timestamp = pts; - obs_frame.width = frame->frame->width; - obs_frame.height = frame->frame->height; - - enum video_format format = - ffmpeg_to_obs_video_format(frame->frame->format); - - if (s->is_forcing_scale || format == VIDEO_FORMAT_NONE) - return video_frame_scale(frame, s, &obs_frame); - else if (s->is_hw_decoding) - return video_frame_hwaccel(frame, s, &obs_frame); - else - return video_frame_direct(frame, s, &obs_frame); -} - -static bool audio_frame(struct ff_frame *frame, void *opaque) -{ - struct ffmpeg_source *s = opaque; - - struct obs_source_audio audio_data = {0}; - - uint64_t pts; - - // Media ended - if (frame == NULL || frame->frame == NULL) - return true; - - pts = (uint64_t)(frame->pts * 1000000000.0L); - - int channels = av_frame_get_channels(frame->frame); - - for(int i = 0; i < channels; i++) - audio_data.data[i] = frame->frame->data[i]; - - audio_data.samples_per_sec = frame->frame->sample_rate; - audio_data.frames = frame->frame->nb_samples; - audio_data.timestamp = pts; - audio_data.format = - convert_ffmpeg_sample_format(frame->frame->format); - audio_data.speakers = channels; - - obs_source_output_audio(s->source, &audio_data); - - return true; -} - static bool is_local_file_modified(obs_properties_t *props, obs_property_t *prop, obs_data_t *settings) { @@ -302,44 +71,28 @@ static bool is_local_file_modified(obs_properties_t *props, "input_format"); obs_property_t *local_file = obs_properties_get(props, "local_file"); obs_property_t *looping = obs_properties_get(props, "looping"); + obs_property_t *buffering = obs_properties_get(props, "buffering_mb"); + obs_property_t *close = obs_properties_get(props, "close_when_inactive"); obs_property_set_visible(input, !enabled); obs_property_set_visible(input_format, !enabled); + obs_property_set_visible(buffering, !enabled); + obs_property_set_visible(close, enabled); obs_property_set_visible(local_file, enabled); obs_property_set_visible(looping, enabled); return true; } -static bool is_advanced_modified(obs_properties_t *props, - obs_property_t *prop, obs_data_t *settings) -{ - UNUSED_PARAMETER(prop); - - bool enabled = obs_data_get_bool(settings, "advanced"); - obs_property_t *fscale = obs_properties_get(props, "force_scale"); - obs_property_t *abuf = obs_properties_get(props, "audio_buffer_size"); - obs_property_t *vbuf = obs_properties_get(props, "video_buffer_size"); - obs_property_t *frame_drop = obs_properties_get(props, "frame_drop"); - obs_property_t *color_range = obs_properties_get(props, "color_range"); - obs_property_set_visible(fscale, enabled); - obs_property_set_visible(abuf, enabled); - obs_property_set_visible(vbuf, enabled); - obs_property_set_visible(frame_drop, enabled); - obs_property_set_visible(color_range, enabled); - - return true; -} - static void ffmpeg_source_defaults(obs_data_t *settings) { obs_data_set_default_bool(settings, "is_local_file", true); obs_data_set_default_bool(settings, "looping", false); obs_data_set_default_bool(settings, "clear_on_media_end", true); obs_data_set_default_bool(settings, "restart_on_activate", true); - obs_data_set_default_bool(settings, "force_scale", true); #if defined(_WIN32) obs_data_set_default_bool(settings, "hw_decode", true); #endif + obs_data_set_default_int(settings, "buffering_mb", 2); } static const char *media_filter = @@ -392,7 +145,8 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data) dstr_free(&filter); dstr_free(&path); - obs_properties_add_bool(props, "looping", obs_module_text("Looping")); + prop = obs_properties_add_bool(props, "looping", + obs_module_text("Looping")); obs_properties_add_bool(props, "restart_on_activate", obs_module_text("RestartWhenActivated")); @@ -403,52 +157,19 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data) obs_properties_add_text(props, "input_format", obs_module_text("InputFormat"), OBS_TEXT_DEFAULT); +#ifndef __APPLE__ obs_properties_add_bool(props, "hw_decode", obs_module_text("HardwareDecode")); +#endif obs_properties_add_bool(props, "clear_on_media_end", obs_module_text("ClearOnMediaEnd")); - prop = obs_properties_add_bool(props, "advanced", - obs_module_text("Advanced")); + prop = obs_properties_add_bool(props, "close_when_inactive", + obs_module_text("CloseFileWhenInactive")); - obs_property_set_modified_callback(prop, is_advanced_modified); - - obs_properties_add_bool(props, "force_scale", - obs_module_text("ForceFormat")); - - prop = obs_properties_add_int(props, "audio_buffer_size", - obs_module_text("AudioBufferSize"), 1, 9999, 1); - - obs_property_set_visible(prop, false); - - prop = obs_properties_add_int(props, "video_buffer_size", - obs_module_text("VideoBufferSize"), 1, 9999, 1); - - obs_property_set_visible(prop, false); - - prop = obs_properties_add_list(props, "frame_drop", - obs_module_text("FrameDropping"), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - - obs_property_list_add_int(prop, obs_module_text("DiscardNone"), - AVDISCARD_NONE); - obs_property_list_add_int(prop, obs_module_text("DiscardDefault"), - AVDISCARD_DEFAULT); - obs_property_list_add_int(prop, obs_module_text("DiscardNonRef"), - AVDISCARD_NONREF); - obs_property_list_add_int(prop, obs_module_text("DiscardBiDir"), - AVDISCARD_BIDIR); -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 67, 100) - obs_property_list_add_int(prop, obs_module_text("DiscardNonIntra"), - AVDISCARD_NONINTRA); -#endif - obs_property_list_add_int(prop, obs_module_text("DiscardNonKey"), - AVDISCARD_NONKEY); - obs_property_list_add_int(prop, obs_module_text("DiscardAll"), - AVDISCARD_ALL); - - obs_property_set_visible(prop, false); + obs_property_set_long_description(prop, + obs_module_text("CloseFileWhenInactive.ToolTip")); prop = obs_properties_add_list(props, "color_range", obs_module_text("ColorRange"), OBS_COMBO_TYPE_LIST, @@ -460,89 +181,94 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data) obs_property_list_add_int(prop, obs_module_text("ColorRange.Full"), VIDEO_RANGE_FULL); - obs_property_set_visible(prop, false); - return props; } -static const char *frame_drop_to_str(enum AVDiscard discard) -{ -#define DISCARD_CASE(x) case AVDISCARD_ ## x: return "AVDISCARD_" #x - switch (discard) - { - DISCARD_CASE(NONE); - DISCARD_CASE(DEFAULT); - DISCARD_CASE(NONREF); - DISCARD_CASE(BIDIR); -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 67, 100) - DISCARD_CASE(NONINTRA); -#endif - DISCARD_CASE(NONKEY); - DISCARD_CASE(ALL); - default: return "(Unknown)"; - }; -#undef DISCARD_CASE -} - static void dump_source_info(struct ffmpeg_source *s, const char *input, - const char *input_format, bool is_advanced) + const char *input_format) { FF_BLOG(LOG_INFO, "settings:\n" "\tinput: %s\n" "\tinput_format: %s\n" "\tis_looping: %s\n" - "\tis_forcing_scale: %s\n" "\tis_hw_decoding: %s\n" "\tis_clear_on_media_end: %s\n" - "\trestart_on_activate: %s", + "\trestart_on_activate: %s\n" + "\tclose_when_inactive: %s", input ? input : "(null)", input_format ? input_format : "(null)", s->is_looping ? "yes" : "no", - s->is_forcing_scale ? "yes" : "no", s->is_hw_decoding ? "yes" : "no", s->is_clear_on_media_end ? "yes" : "no", - s->restart_on_activate ? "yes" : "no"); + s->restart_on_activate ? "yes" : "no", + s->close_when_inactive ? "yes" : "no"); +} - if (!is_advanced) +static void get_frame(void *opaque, struct obs_source_frame *f) +{ + struct ffmpeg_source *s = opaque; + obs_source_output_video(s->source, f); +} + +static void preload_frame(void *opaque, struct obs_source_frame *f) +{ + struct ffmpeg_source *s = opaque; + if (s->close_when_inactive) return; - FF_BLOG(LOG_INFO, - "advanced settings:\n" - "\taudio_buffer_size: %d\n" - "\tvideo_buffer_size: %d\n" - "\tframe_drop: %s", - s->audio_buffer_size, - s->video_buffer_size, - frame_drop_to_str(s->frame_drop)); + if (s->is_clear_on_media_end || s->is_looping) + obs_source_preload_video(s->source, f); +} + +static void get_audio(void *opaque, struct obs_source_audio *a) +{ + struct ffmpeg_source *s = opaque; + obs_source_output_audio(s->source, a); +} + +static void media_stopped(void *opaque) +{ + struct ffmpeg_source *s = opaque; + if (s->is_clear_on_media_end) { + obs_source_output_video(s->source, NULL); + if (s->close_when_inactive) + s->destroy_media = true; + } +} + +static void ffmpeg_source_open(struct ffmpeg_source *s) +{ + if (s->input && *s->input) + s->media_valid = mp_media_init(&s->media, + s->input, s->input_format, + s->buffering_mb * 1024 * 1024, + s, get_frame, get_audio, media_stopped, + preload_frame, s->is_hw_decoding, s->range); +} + +static void ffmpeg_source_tick(void *data, float seconds) +{ + struct ffmpeg_source *s = data; + if (s->destroy_media) { + if (s->media_valid) { + mp_media_free(&s->media); + s->media_valid = false; + } + s->destroy_media = false; + } } static void ffmpeg_source_start(struct ffmpeg_source *s) { - if (s->demuxer != NULL) - ff_demuxer_free(s->demuxer); + if (!s->media_valid) + ffmpeg_source_open(s); - s->demuxer = ff_demuxer_init(); - s->demuxer->options.is_hw_decoding = s->is_hw_decoding; - s->demuxer->options.is_looping = s->is_looping; - - ff_demuxer_set_callbacks(&s->demuxer->video_callbacks, - video_frame, NULL, - NULL, NULL, NULL, s); - - ff_demuxer_set_callbacks(&s->demuxer->audio_callbacks, - audio_frame, NULL, - NULL, NULL, NULL, s); - - if (s->is_advanced) { - s->demuxer->options.audio_frame_queue_size = - s->audio_buffer_size; - s->demuxer->options.video_frame_queue_size = - s->video_buffer_size; - s->demuxer->options.frame_drop = s->frame_drop; + if (s->media_valid) { + mp_media_play(&s->media, s->is_looping); + if (s->is_local_file) + obs_source_show_preloaded_video(s->source); } - - ff_demuxer_open(s->demuxer, s->input, s->input_format); } static void ffmpeg_source_update(void *data, obs_data_t *settings) @@ -550,7 +276,6 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings) struct ffmpeg_source *s = data; bool is_local_file = obs_data_get_bool(settings, "is_local_file"); - bool is_advanced = obs_data_get_bool(settings, "advanced"); char *input; char *input_format; @@ -562,57 +287,45 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings) input = (char *)obs_data_get_string(settings, "local_file"); input_format = NULL; s->is_looping = obs_data_get_bool(settings, "looping"); + s->close_when_inactive = obs_data_get_bool(settings, + "close_when_inactive"); + + obs_source_set_async_unbuffered(s->source, true); } else { input = (char *)obs_data_get_string(settings, "input"); input_format = (char *)obs_data_get_string(settings, "input_format"); s->is_looping = false; + s->close_when_inactive = true; + + obs_source_set_async_unbuffered(s->source, false); } s->input = input ? bstrdup(input) : NULL; s->input_format = input_format ? bstrdup(input_format) : NULL; - s->is_advanced = is_advanced; +#ifndef __APPLE__ s->is_hw_decoding = obs_data_get_bool(settings, "hw_decode"); +#endif s->is_clear_on_media_end = obs_data_get_bool(settings, "clear_on_media_end"); s->restart_on_activate = obs_data_get_bool(settings, "restart_on_activate"); - s->is_forcing_scale = true; - s->range = VIDEO_RANGE_DEFAULT; + s->range = (enum video_range_type)obs_data_get_int(settings, + "color_range"); + s->buffering_mb = (int)obs_data_get_int(settings, "buffering_mb"); + s->is_local_file = is_local_file; - if (is_advanced) { - s->audio_buffer_size = (int)obs_data_get_int(settings, - "audio_buffer_size"); - s->video_buffer_size = (int)obs_data_get_int(settings, - "video_buffer_size"); - s->frame_drop = (enum AVDiscard)obs_data_get_int(settings, - "frame_drop"); - s->is_forcing_scale = obs_data_get_bool(settings, - "force_scale"); - s->range = (enum video_range_type)obs_data_get_int(settings, - "color_range"); - - if (s->audio_buffer_size < 1) { - s->audio_buffer_size = 1; - FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d", - s->audio_buffer_size); - } - if (s->video_buffer_size < 1) { - s->video_buffer_size = 1; - FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d", - s->audio_buffer_size); - } - - if (s->frame_drop < AVDISCARD_NONE || - s->frame_drop > AVDISCARD_ALL) { - s->frame_drop = AVDISCARD_DEFAULT; - FF_BLOG(LOG_WARNING, "invalid frame_drop %d", - s->frame_drop); - } + if (s->media_valid) { + mp_media_free(&s->media); + s->media_valid = false; } - dump_source_info(s, input, input_format, is_advanced); - if (!s->restart_on_activate || obs_source_active(s->source)) + bool active = obs_source_active(s->source); + if (!s->close_when_inactive || active) + ffmpeg_source_open(s); + + dump_source_info(s, input, input_format); + if (!s->restart_on_activate || active) ffmpeg_source_start(s); } @@ -622,6 +335,25 @@ static const char *ffmpeg_source_getname(void *unused) return obs_module_text("FFMpegSource"); } +static bool restart_hotkey(void *data, obs_hotkey_id id, + obs_hotkey_t *hotkey, bool pressed) +{ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(hotkey); + UNUSED_PARAMETER(pressed); + + struct ffmpeg_source *s = data; + if (obs_source_active(s->source)) + ffmpeg_source_start(s); + return true; +} + +static void restart_proc(void *data, calldata_t *cd) +{ + restart_hotkey(data, 0, NULL, true); + UNUSED_PARAMETER(cd); +} + static void *ffmpeg_source_create(obs_data_t *settings, obs_source_t *source) { UNUSED_PARAMETER(settings); @@ -629,6 +361,14 @@ static void *ffmpeg_source_create(obs_data_t *settings, obs_source_t *source) struct ffmpeg_source *s = bzalloc(sizeof(struct ffmpeg_source)); s->source = source; + s->hotkey = obs_hotkey_register_source(source, + "MediaSource.Restart", + obs_module_text("RestartMedia"), + restart_hotkey, s); + + proc_handler_t *ph = obs_source_get_proc_handler(source); + proc_handler_add(ph, "void restart()", restart_proc, s); + ffmpeg_source_update(s, settings); return s; } @@ -637,8 +377,10 @@ static void ffmpeg_source_destroy(void *data) { struct ffmpeg_source *s = data; - if (s->demuxer) - ff_demuxer_free(s->demuxer); + if (s->hotkey) + obs_hotkey_unregister(s->hotkey); + if (s->media_valid) + mp_media_free(&s->media); if (s->sws_ctx != NULL) sws_freeContext(s->sws_ctx); @@ -661,9 +403,8 @@ static void ffmpeg_source_deactivate(void *data) struct ffmpeg_source *s = data; if (s->restart_on_activate) { - if (s->demuxer != NULL) { - ff_demuxer_free(s->demuxer); - s->demuxer = NULL; + if (s->media_valid) { + mp_media_stop(&s->media); if (s->is_clear_on_media_end) obs_source_output_video(s->source, NULL); @@ -683,5 +424,6 @@ struct obs_source_info ffmpeg_source = { .get_properties = ffmpeg_source_getproperties, .activate = ffmpeg_source_activate, .deactivate = ffmpeg_source_deactivate, + .video_tick = ffmpeg_source_tick, .update = ffmpeg_source_update }; diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 54b19e5..c88da31 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -22,6 +22,7 @@ set(obs-filters_SOURCES obs-filters.c color-correction-filter.c async-delay-filter.c + gpu-delay.c crop-filter.c scale-filter.c scroll-filter.c diff --git a/plugins/obs-filters/data/LUTs/black_and_white.png b/plugins/obs-filters/data/LUTs/black_and_white.png index d55914c..4496a42 100644 Binary files a/plugins/obs-filters/data/LUTs/black_and_white.png and b/plugins/obs-filters/data/LUTs/black_and_white.png differ diff --git a/plugins/obs-filters/data/LUTs/original.png b/plugins/obs-filters/data/LUTs/original.png index ed814df..c16b4d0 100644 Binary files a/plugins/obs-filters/data/LUTs/original.png and b/plugins/obs-filters/data/LUTs/original.png differ diff --git a/plugins/obs-filters/data/LUTs/posterize.png b/plugins/obs-filters/data/LUTs/posterize.png index 2ef1e1d..b41afd2 100644 Binary files a/plugins/obs-filters/data/LUTs/posterize.png and b/plugins/obs-filters/data/LUTs/posterize.png differ diff --git a/plugins/obs-filters/data/LUTs/red_isolated.png b/plugins/obs-filters/data/LUTs/red_isolated.png index 624dc97..e0327c7 100644 Binary files a/plugins/obs-filters/data/LUTs/red_isolated.png and b/plugins/obs-filters/data/LUTs/red_isolated.png differ diff --git a/plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png b/plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png index abc162c..a669282 100644 Binary files a/plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png and b/plugins/obs-filters/data/LUTs/teal_lows_orange_highs.png differ diff --git a/plugins/obs-filters/data/locale/bn-BD.ini b/plugins/obs-filters/data/locale/bn-BD.ini new file mode 100644 index 0000000..55ae407 --- /dev/null +++ b/plugins/obs-filters/data/locale/bn-BD.ini @@ -0,0 +1,10 @@ +ColorFilter="রং এর সংশোধনী" +CropFilter="ফসল/প্যাড" +ScrollFilter="নিচে স্ক্রোল করুন" +SharpnessFilter="ধার" +ScaleFilter="স্কেল/দৃশ্যরূপ অনুপাত" +Gain="মোহ" +DelayMs="বিলম্ব (মিলিসেকেন্ড)" +Type="নমুনা" +MaskBlendType.MaskColor="আলফা মুখোশ (রং চ্যানেল)" + diff --git a/plugins/obs-filters/data/locale/cs-CZ.ini b/plugins/obs-filters/data/locale/cs-CZ.ini index 56d0741..66176e9 100644 --- a/plugins/obs-filters/data/locale/cs-CZ.ini +++ b/plugins/obs-filters/data/locale/cs-CZ.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Chroma Key" ColorKeyFilter="Klíč barvy" SharpnessFilter="Ostření" ScaleFilter="Škálování/poměr stran" +GPUDelayFilter="Zpoždění vykreslování" UndistortCenter="Zlepší střed obrázku při škálování z ultra-širokého obrazu" NoiseGate="Šumová brána" NoiseSuppress="Potlačení šumu" @@ -66,4 +67,10 @@ NoiseSuppress.SuppressLevel="Úroveň potlačení (dB)" Saturation="Saturace" HueShift="Posun odstínu" Amount="Množství" +Compressor="Kompresor" +Compressor.Ratio="Poměr (X:1)" +Compressor.Threshold="Práh (v dB)" +Compressor.AttackTime="Stažení (v ms)" +Compressor.ReleaseTime="Uvolnění (v ms)" +Compressor.OutputGain="Síla výstupu (v dB)" diff --git a/plugins/obs-filters/data/locale/da-DK.ini b/plugins/obs-filters/data/locale/da-DK.ini index 6e4029c..05026f5 100644 --- a/plugins/obs-filters/data/locale/da-DK.ini +++ b/plugins/obs-filters/data/locale/da-DK.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Chroma nøgle" ColorKeyFilter="Farvenøgle" SharpnessFilter="Skarphed" ScaleFilter="Skalering/Formatforhold" +GPUDelayFilter="Renderingsforsinkelse" UndistortCenter="Fjern forvrængning af billedets midte ved skalering fra ultrabred" NoiseGate="Noise Gate" NoiseSuppress="Støjundertrykkelse" @@ -66,4 +67,10 @@ NoiseSuppress.SuppressLevel="Undertrykkelsesniveau (dB)" Saturation="Mætning" HueShift="Nuanceskift" Amount="Værdi" +Compressor="Kompressor" +Compressor.Ratio="Forhold (X:1)" +Compressor.Threshold="Grænse (dB)" +Compressor.AttackTime="Attack (ms)" +Compressor.ReleaseTime="Release (ms)" +Compressor.OutputGain="Output øgning (dB)" diff --git a/plugins/obs-filters/data/locale/de-DE.ini b/plugins/obs-filters/data/locale/de-DE.ini index 1686a36..40b17f2 100644 --- a/plugins/obs-filters/data/locale/de-DE.ini +++ b/plugins/obs-filters/data/locale/de-DE.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Schärfen" ScaleFilter="Skalierung/Seitenverhältnis" +GPUDelayFilter="Renderverzögerung" UndistortCenter="Entzerre Mitte des Bildes bei der Skalierung von Ultraweitwinkel" NoiseGate="Noise Gate" NoiseSuppress="Rauschunterdrückung" diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index e183d03..f605d7d 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Sharpen" ScaleFilter="Scaling/Aspect Ratio" +GPUDelayFilter="Render Delay" UndistortCenter="Undistort center of image when scaling from ultrawide" NoiseGate="Noise Gate" NoiseSuppress="Noise Suppression" diff --git a/plugins/obs-filters/data/locale/es-ES.ini b/plugins/obs-filters/data/locale/es-ES.ini index f025a15..445b890 100644 --- a/plugins/obs-filters/data/locale/es-ES.ini +++ b/plugins/obs-filters/data/locale/es-ES.ini @@ -1,13 +1,14 @@ ColorFilter="Corrección de color" ColorGradeFilter="Aplicar LUT" MaskFilter="Imagen máscara/mezcla" -AsyncDelayFilter="Demora de Video (asincróno)" +AsyncDelayFilter="Retardo de Video (asincróno)" CropFilter="Recortar/Acolchar" ScrollFilter="desplazamiento" ChromaKeyFilter="Fondro croma" ColorKeyFilter="Filtro de color" SharpnessFilter="Filtro de enfoque" ScaleFilter="Escala/Relación de Aspecto" +GPUDelayFilter="Retardo de procesamiento" UndistortCenter="No distorsionar el centro de la imagen en escalar des de una ultrapanorámica" NoiseGate="Puerta anti-ruidos" NoiseSuppress="Eliminación de ruido" diff --git a/plugins/obs-filters/data/locale/et-EE.ini b/plugins/obs-filters/data/locale/et-EE.ini index 8cac372..233831d 100644 --- a/plugins/obs-filters/data/locale/et-EE.ini +++ b/plugins/obs-filters/data/locale/et-EE.ini @@ -1,10 +1,20 @@ ColorFilter="Värvi korrektsioon" ColorGradeFilter="Lisa LUT" +CropFilter="Kärbi/Padding" +ScrollFilter="Kerimine" +ChromaKeyFilter="Chroma Efekt" +SharpnessFilter="Teravda" Gain="Võimendus" +DelayMs="Viivitus (millisekundites)" +Type="Tüüp" +Path="Tee" +Color="Värv" +Opacity="Läbipaistvus" Contrast="Kontrast" Brightness="Heledus" Gamma="Gamma" BrowsePath.Images="Kõik pildifailid" +BrowsePath.AllFiles="Kõik failid" Crop.Left="Vasakult" Crop.Right="Paremalt" Crop.Top="Ülevalt" @@ -12,5 +22,17 @@ Crop.Bottom="Alt" Crop.Width="Laius" Crop.Height="Kõrgus" Crop.Relative="Suhteline" +ScrollFilter.SpeedY="Vertikaalne kiirus" +ScrollFilter.LimitWidth="Piira laiust" +ScrollFilter.LimitHeight="Piira kõrgust" +Red="Punane" +Green="Roheline" +Blue="Sinine" +Gain.GainDB="Võimendus (dB)" +None="Määramata" +ScaleFiltering.Point="Punkt" +Saturation="Küllastatus" Amount="Kogus" +Compressor.Ratio="Suhe (X:1)" +Compressor.OutputGain="Väljundi võimendus (dB)" diff --git a/plugins/obs-filters/data/locale/eu-ES.ini b/plugins/obs-filters/data/locale/eu-ES.ini index 6f18330..7fa668d 100644 --- a/plugins/obs-filters/data/locale/eu-ES.ini +++ b/plugins/obs-filters/data/locale/eu-ES.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Kroma gakoa" ColorKeyFilter="Kolore gakoa" SharpnessFilter="Enfokea" ScaleFilter="Eskala/Aspektu-erlazioa" +GPUDelayFilter="Errendatzearen atzerapena" UndistortCenter="Ez distortsionatu irudiaren erdigunea ultra zabala eskalatzean" NoiseGate="Zarata atalasea" NoiseSuppress="Zarata kendu" diff --git a/plugins/obs-filters/data/locale/fi-FI.ini b/plugins/obs-filters/data/locale/fi-FI.ini index 8572128..608f5d7 100644 --- a/plugins/obs-filters/data/locale/fi-FI.ini +++ b/plugins/obs-filters/data/locale/fi-FI.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Läpinäkyvä tausta" ColorKeyFilter="Väriavain" SharpnessFilter="Terävöitä" ScaleFilter="Skaalaus/Kuvasuhde" +GPUDelayFilter="Renderoinnin viive" UndistortCenter="Poista vääristymä keskeltä kuvaa skaalattaessa ultra-leveästä" NoiseGate="Noise Gate" NoiseSuppress="Melunvaimennus" diff --git a/plugins/obs-filters/data/locale/hu-HU.ini b/plugins/obs-filters/data/locale/hu-HU.ini index 9f548bb..0b44de2 100644 --- a/plugins/obs-filters/data/locale/hu-HU.ini +++ b/plugins/obs-filters/data/locale/hu-HU.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Chroma kulcs" ColorKeyFilter="Színkulcs" SharpnessFilter="Élesítés" ScaleFilter="Méretezés/Képarány" +GPUDelayFilter="Render késleltetés" UndistortCenter="Kép közepének zavarosságának a csökkentése ultraszélesről való skálázás esetén" NoiseGate="Zajgát" NoiseSuppress="Zajcsökkentés" diff --git a/plugins/obs-filters/data/locale/it-IT.ini b/plugins/obs-filters/data/locale/it-IT.ini index 028f31b..0012555 100644 --- a/plugins/obs-filters/data/locale/it-IT.ini +++ b/plugins/obs-filters/data/locale/it-IT.ini @@ -64,4 +64,11 @@ ScaleFiltering.Lanczos="Lanczos" NoiseSuppress.SuppressLevel="Livello di soppressione (dB)" Saturation="Saturazione" HueShift="Cambio di tonalità" +Amount="Quantità" +Compressor="Compressore" +Compressor.Ratio="Rapporto (X:1)" +Compressor.Threshold="Soglia (dB)" +Compressor.AttackTime="Attacco (ms)" +Compressor.ReleaseTime="Rilascio (ms)" +Compressor.OutputGain="Guadagno di uscita (dB)" diff --git a/plugins/obs-filters/data/locale/ja-JP.ini b/plugins/obs-filters/data/locale/ja-JP.ini index 6827ced..ed1c205 100644 --- a/plugins/obs-filters/data/locale/ja-JP.ini +++ b/plugins/obs-filters/data/locale/ja-JP.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="クロマキー" ColorKeyFilter="カラーキー" SharpnessFilter="シャープ" ScaleFilter="スケーリング/アスペクト比" +GPUDelayFilter="レンダリング遅延" UndistortCenter="超広角からスケーリングするときに画像の中心を歪めない" NoiseGate="ノイズゲート" NoiseSuppress="ノイズ抑制" diff --git a/plugins/obs-filters/data/locale/ko-KR.ini b/plugins/obs-filters/data/locale/ko-KR.ini index 775008f..3b2439c 100644 --- a/plugins/obs-filters/data/locale/ko-KR.ini +++ b/plugins/obs-filters/data/locale/ko-KR.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="크로마 키" ColorKeyFilter="색상 키" SharpnessFilter="선명하게" ScaleFilter="비례축소/가로세로 비율" +GPUDelayFilter="렌더링 지연" UndistortCenter="울트라와이드에서 크기조정 시 이미지 중앙의 왜곡을 수정" NoiseGate="노이즈 게이트" NoiseSuppress="소음 억제" diff --git a/plugins/obs-filters/data/locale/nl-NL.ini b/plugins/obs-filters/data/locale/nl-NL.ini index 4c1597c..fae5c38 100644 --- a/plugins/obs-filters/data/locale/nl-NL.ini +++ b/plugins/obs-filters/data/locale/nl-NL.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Verscherpen" ScaleFilter="Schalen/Aspect Ratio" +GPUDelayFilter="Rendervertraging" UndistortCenter="Verbeter beeldverhouding in het midden van bij schalen vanaf ultrawide" NoiseGate="Noise Gate" NoiseSuppress="Ruisonderdrukking" @@ -66,4 +67,10 @@ NoiseSuppress.SuppressLevel="Onderdrukkingsniveau (dB)" Saturation="Verzadiging" HueShift="Tintverschuiving" Amount="Hoeveelheid" +Compressor="Compressor" +Compressor.Ratio="Verhouding (X:1)" +Compressor.Threshold="Drempel (dB)" +Compressor.AttackTime="Attack (ms)" +Compressor.ReleaseTime="Release (ms)" +Compressor.OutputGain="Uitvoergain (dB)" diff --git a/plugins/obs-filters/data/locale/pl-PL.ini b/plugins/obs-filters/data/locale/pl-PL.ini index 343aa4a..656ce21 100644 --- a/plugins/obs-filters/data/locale/pl-PL.ini +++ b/plugins/obs-filters/data/locale/pl-PL.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Kluczowanie koloru (chroma key)" ColorKeyFilter="Kluczowanie koloru (kolor)" SharpnessFilter="Wyostrzanie" ScaleFilter="Skalowanie/proporcje" +GPUDelayFilter="Opóźnienie renderowania" UndistortCenter="Usuń przekłamania przy skalowaniu źródeł o dużej szerokości" NoiseGate="Bramka szumów" NoiseSuppress="Tłumienie hałasu" diff --git a/plugins/obs-filters/data/locale/pt-BR.ini b/plugins/obs-filters/data/locale/pt-BR.ini index e3901ed..2bcfecf 100644 --- a/plugins/obs-filters/data/locale/pt-BR.ini +++ b/plugins/obs-filters/data/locale/pt-BR.ini @@ -1,11 +1,15 @@ ColorFilter="Correção de cor" +ColorGradeFilter="Aplicar LUT" MaskFilter="Máscara/mistura de imagem" AsyncDelayFilter="Atraso de vídeo (Async)" +CropFilter="Recortar/Preencher" ScrollFilter="Rolagem" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Nitidez" ScaleFilter="Dimensionamento/proporção" +GPUDelayFilter="Atraso de Renderização" +UndistortCenter="Remover distorção do centro da imagem ao redimensionar de ultralargo" NoiseGate="Filtro de Rúido" NoiseSuppress="Redução de ruídos" Gain="Ganho" @@ -47,9 +51,9 @@ Blue="Azul" Magenta="Magenta" NoiseGate.OpenThreshold="Limite de Abertura (dB)" NoiseGate.CloseThreshold="Limite de Fecho (dB)" -NoiseGate.AttackTime="Tempo de ataque (milissegundos)" +NoiseGate.AttackTime="Tempo de Ataque (milissegundos)" NoiseGate.HoldTime="Tempo de Bloqueio (milissegundos)" -NoiseGate.ReleaseTime="Tempo de libertação (milissegundos)" +NoiseGate.ReleaseTime="Tempo de liberação (milissegundos)" Gain.GainDB="Ganho (dB)" StretchImage="Esticar a Imagem (descartar aspecto da imagem)" Resolution="Resolução" @@ -60,4 +64,13 @@ ScaleFiltering.Bilinear="Bilinear" ScaleFiltering.Bicubic="Bicúbico" ScaleFiltering.Lanczos="Lanczos" NoiseSuppress.SuppressLevel="Nível de redução (dB)" +Saturation="Saturação" +HueShift="Alteração de matiz" +Amount="Valor" +Compressor="Compressor" +Compressor.Ratio="Razão (X:1)" +Compressor.Threshold="Limiar (dB)" +Compressor.AttackTime="Ataque (ms)" +Compressor.ReleaseTime="Liberação (ms)" +Compressor.OutputGain="Ganho na saída (dB)" diff --git a/plugins/obs-filters/data/locale/ru-RU.ini b/plugins/obs-filters/data/locale/ru-RU.ini index 770e3cc..1547423 100644 --- a/plugins/obs-filters/data/locale/ru-RU.ini +++ b/plugins/obs-filters/data/locale/ru-RU.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Хромакей" ColorKeyFilter="Цветовой ключ" SharpnessFilter="Увеличить резкость" ScaleFilter="Коэффициент Масштабирования/Аспект" +GPUDelayFilter="Задержка отображения" UndistortCenter="Не искривлять центр изображения при масштабировании Ultrawide разрешения" NoiseGate="Подавление шума" NoiseSuppress="Шумоподавление" diff --git a/plugins/obs-filters/data/locale/sv-SE.ini b/plugins/obs-filters/data/locale/sv-SE.ini index 469056a..2edc1af 100644 --- a/plugins/obs-filters/data/locale/sv-SE.ini +++ b/plugins/obs-filters/data/locale/sv-SE.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Kromafilter" ColorKeyFilter="Färgfilter" SharpnessFilter="Skärpa" ScaleFilter="Skalning/Bildförhållande" +GPUDelayFilter="Renderingsfördröjning" NoiseGate="Brusblockering" NoiseSuppress="Brusreducering" Gain="Förstärkning" diff --git a/plugins/obs-filters/data/locale/tr-TR.ini b/plugins/obs-filters/data/locale/tr-TR.ini index 77472ac..33e1d9f 100644 --- a/plugins/obs-filters/data/locale/tr-TR.ini +++ b/plugins/obs-filters/data/locale/tr-TR.ini @@ -1,13 +1,14 @@ ColorFilter="Renk Düzeltme" ColorGradeFilter="LUT Uygula" MaskFilter="Görüntü Maskesi/Blend" -AsyncDelayFilter="Görüntü Gecikmesi (Async)" +AsyncDelayFilter="Görüntü Gecikmesi (Eşzamansız)" CropFilter="Kes/Kaydır" ScrollFilter="Kaydır" ChromaKeyFilter="Chroma Anahtarı" ColorKeyFilter="Renk Anahtarı" SharpnessFilter="Keskinleştirme" -ScaleFilter="Ölçeklendirme/Boy Oranı" +ScaleFilter="Boyutlandırma/En-Boy Oranı" +GPUDelayFilter="İşleyici Gecikmesi" UndistortCenter="Ultra genişten boyutlandırırken görüntü merkezindeki bozulmayı düzelt" NoiseGate="Gürültü Filtresi" NoiseSuppress="Gürültü Bastırma" @@ -54,7 +55,7 @@ NoiseGate.AttackTime="Atak Süresi (milisaniye)" NoiseGate.HoldTime="Kavrama Süresi (milisaniye)" NoiseGate.ReleaseTime="Bırakma Süresi (milisaniye)" Gain.GainDB="Kazanç (dB)" -StretchImage="Görüntüyü Uzat (görüntü boy oranını görmezden gelir)" +StretchImage="Görüntüyü Uzat (görüntü en-boy oranını görmezden gel)" Resolution="Çözünürlük" None="Hiçbiri" ScaleFiltering="Ölçek Filtreleme" diff --git a/plugins/obs-filters/data/locale/uk-UA.ini b/plugins/obs-filters/data/locale/uk-UA.ini index f0736b3..29755ae 100644 --- a/plugins/obs-filters/data/locale/uk-UA.ini +++ b/plugins/obs-filters/data/locale/uk-UA.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="Зелений екран" ColorKeyFilter="Фільтрування за кольором" SharpnessFilter="Різкість" ScaleFilter="Масштабування/пропорції" +GPUDelayFilter="Затримка візуалізації" UndistortCenter="Зменшити викривлення у центрі, якщо масштабувати з надширокоформатного" NoiseGate="Пороговий шумопонижувач" NoiseSuppress="Подавлення шуму" diff --git a/plugins/obs-filters/data/locale/zh-CN.ini b/plugins/obs-filters/data/locale/zh-CN.ini index 4d31867..c93bb40 100644 --- a/plugins/obs-filters/data/locale/zh-CN.ini +++ b/plugins/obs-filters/data/locale/zh-CN.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="色度键" ColorKeyFilter="色值" SharpnessFilter="锐化" ScaleFilter="缩放比例" +GPUDelayFilter="渲染延迟" UndistortCenter="当从超宽扩展时, 让图片中心不失真" NoiseGate="噪音阈值" NoiseSuppress="噪声抑制" diff --git a/plugins/obs-filters/data/locale/zh-TW.ini b/plugins/obs-filters/data/locale/zh-TW.ini index 41a8960..b21e302 100644 --- a/plugins/obs-filters/data/locale/zh-TW.ini +++ b/plugins/obs-filters/data/locale/zh-TW.ini @@ -8,6 +8,7 @@ ChromaKeyFilter="色度鍵" ColorKeyFilter="色彩鍵" SharpnessFilter="銳化" ScaleFilter="縮放/長寬比" +GPUDelayFilter="繪製延遲" UndistortCenter="從超寬影像縮放時彌補影像中心的畸變" NoiseGate="噪音閾" NoiseSuppress="雜訊抑制" @@ -66,4 +67,10 @@ NoiseSuppress.SuppressLevel="抑制標準 (dB)" Saturation="飽合度" HueShift="色調偏移" Amount="影響總量" +Compressor="動態壓縮處理器" +Compressor.Ratio="比率 (X:1)" +Compressor.Threshold="閾值 (dB)" +Compressor.AttackTime="起始時間 (ms)" +Compressor.ReleaseTime="釋放時間 (ms)" +Compressor.OutputGain="輸出增益 (dB)" diff --git a/plugins/obs-filters/gpu-delay.c b/plugins/obs-filters/gpu-delay.c new file mode 100644 index 0000000..c4628be --- /dev/null +++ b/plugins/obs-filters/gpu-delay.c @@ -0,0 +1,265 @@ +#include +#include + +#define S_DELAY_MS "delay_ms" +#define T_DELAY_MS obs_module_text("DelayMs") + +struct frame { + gs_texrender_t *render; + uint64_t ts; +}; + +struct gpu_delay_filter_data { + obs_source_t *context; + struct circlebuf frames; + uint64_t delay_ns; + uint64_t interval_ns; + uint32_t cx; + uint32_t cy; + bool target_valid; + bool processed_frame; +}; + +static const char *gpu_delay_filter_get_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("GPUDelayFilter"); +} + +static void free_textures(struct gpu_delay_filter_data *f) +{ + obs_enter_graphics(); + while (f->frames.size) { + struct frame frame; + circlebuf_pop_front(&f->frames, &frame, sizeof(frame)); + gs_texrender_destroy(frame.render); + } + circlebuf_free(&f->frames); + obs_leave_graphics(); +} + +static size_t num_frames(struct circlebuf *buf) +{ + return buf->size / sizeof(struct frame); +} + +static void update_interval(struct gpu_delay_filter_data *f, + uint64_t new_interval_ns) +{ + if (!f->target_valid) { + free_textures(f); + return; + } + + f->interval_ns = new_interval_ns; + size_t num = (size_t)(f->delay_ns / new_interval_ns); + + if (num > num_frames(&f->frames)) { + size_t prev_num = num_frames(&f->frames); + + obs_enter_graphics(); + + circlebuf_upsize(&f->frames, num * sizeof(struct frame)); + + for (size_t i = prev_num; i < num; i++) { + struct frame *frame = circlebuf_data(&f->frames, + i * sizeof(*frame)); + frame->render = gs_texrender_create(GS_RGBA, GS_ZS_NONE); + } + + obs_leave_graphics(); + + } else if (num < num_frames(&f->frames)) { + obs_enter_graphics(); + + while (num_frames(&f->frames) > num) { + struct frame frame; + circlebuf_pop_front(&f->frames, &frame, sizeof(frame)); + gs_texrender_destroy(frame.render); + } + + obs_leave_graphics(); + } +} + +static inline void check_interval(struct gpu_delay_filter_data *f) +{ + struct obs_video_info ovi = {0}; + uint64_t interval_ns; + + obs_get_video_info(&ovi); + + interval_ns = (uint64_t)ovi.fps_den * 1000000000ULL / + (uint64_t)ovi.fps_num; + + if (interval_ns != f->interval_ns) + update_interval(f, interval_ns); +} + +static inline void reset_textures(struct gpu_delay_filter_data *f) +{ + f->interval_ns = 0; + free_textures(f); + check_interval(f); +} + +static inline bool check_size(struct gpu_delay_filter_data *f) +{ + obs_source_t *target = obs_filter_get_target(f->context); + uint32_t cx; + uint32_t cy; + + f->target_valid = !!target; + if (!f->target_valid) + return true; + + cx = obs_source_get_base_width(target); + cy = obs_source_get_base_height(target); + + f->target_valid = !!cx && !!cy; + if (!f->target_valid) + return true; + + if (cx != f->cx || cy != f->cy) { + f->cx = cx; + f->cy = cy; + reset_textures(f); + return true; + } + + return false; +} + +static void gpu_delay_filter_update(void *data, obs_data_t *s) +{ + struct gpu_delay_filter_data *f = data; + + f->delay_ns = (uint64_t)obs_data_get_int(s, S_DELAY_MS) * 1000000ULL; + + /* full reset */ + f->cx = 0; + f->cy = 0; + f->interval_ns = 0; + free_textures(f); +} + +static obs_properties_t *gpu_delay_filter_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_properties_add_int(props, S_DELAY_MS, T_DELAY_MS, 0, 500, 1); + + UNUSED_PARAMETER(data); + return props; +} + +static void *gpu_delay_filter_create(obs_data_t *settings, obs_source_t *context) +{ + struct gpu_delay_filter_data *f = bzalloc(sizeof(*f)); + f->context = context; + + obs_source_update(context, settings); + return f; +} + +static void gpu_delay_filter_destroy(void *data) +{ + struct gpu_delay_filter_data *f = data; + + free_textures(f); + bfree(f); +} + +static void gpu_delay_filter_tick(void *data, float t) +{ + struct gpu_delay_filter_data *f = data; + uint64_t cur_time = obs_get_video_frame_time(); + + f->processed_frame = false; + + if (check_size(f)) + return; + check_interval(f); +} + +static void draw_frame(struct gpu_delay_filter_data *f) +{ + struct frame frame; + circlebuf_peek_front(&f->frames, &frame, sizeof(frame)); + + gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); + gs_texture_t *tex = gs_texrender_get_texture(frame.render); + if (tex) { + gs_eparam_t *image = + gs_effect_get_param_by_name(effect, "image"); + gs_effect_set_texture(image, tex); + + while (gs_effect_loop(effect, "Draw")) + gs_draw_sprite(tex, 0, f->cx, f->cy); + } +} + +static void gpu_delay_filter_render(void *data, gs_effect_t *effect) +{ + struct gpu_delay_filter_data *f = data; + obs_source_t *target = obs_filter_get_target(f->context); + obs_source_t *parent = obs_filter_get_parent(f->context); + + if (!f->target_valid || !target || !parent || !f->frames.size) { + obs_source_skip_video_filter(f->context); + return; + } + + if (f->processed_frame) { + draw_frame(f); + return; + } + + struct frame frame; + circlebuf_pop_front(&f->frames, &frame, sizeof(frame)); + + gs_texrender_reset(frame.render); + + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + + if (gs_texrender_begin(frame.render, f->cx, f->cy)) { + uint32_t parent_flags = obs_source_get_output_flags(target); + bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0; + bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0; + struct vec4 clear_color; + + vec4_zero(&clear_color); + gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0); + gs_ortho(0.0f, (float)f->cx, 0.0f, (float)f->cy, + -100.0f, 100.0f); + + if (target == parent && !custom_draw && !async) + obs_source_default_render(target); + else + obs_source_video_render(target); + + gs_texrender_end(frame.render); + } + + gs_blend_state_pop(); + + circlebuf_push_back(&f->frames, &frame, sizeof(frame)); + draw_frame(f); + f->processed_frame = true; + + UNUSED_PARAMETER(effect); +} + +struct obs_source_info gpu_delay_filter = { + .id = "gpu_delay", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = gpu_delay_filter_get_name, + .create = gpu_delay_filter_create, + .destroy = gpu_delay_filter_destroy, + .update = gpu_delay_filter_update, + .get_properties = gpu_delay_filter_properties, + .video_tick = gpu_delay_filter_tick, + .video_render = gpu_delay_filter_render +}; diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 6c0e413..02ac63d 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -11,6 +11,7 @@ extern struct obs_source_info gain_filter; extern struct obs_source_info color_filter; extern struct obs_source_info scale_filter; extern struct obs_source_info scroll_filter; +extern struct obs_source_info gpu_delay_filter; extern struct obs_source_info color_key_filter; extern struct obs_source_info color_grade_filter; extern struct obs_source_info sharpness_filter; @@ -30,6 +31,7 @@ bool obs_module_load(void) obs_register_source(&color_filter); obs_register_source(&scale_filter); obs_register_source(&scroll_filter); + obs_register_source(&gpu_delay_filter); obs_register_source(&color_key_filter); obs_register_source(&color_grade_filter); obs_register_source(&sharpness_filter); diff --git a/plugins/obs-filters/scale-filter.c b/plugins/obs-filters/scale-filter.c index 29e42c4..3a1cfef 100644 --- a/plugins/obs-filters/scale-filter.c +++ b/plugins/obs-filters/scale-filter.c @@ -38,10 +38,10 @@ struct scale_filter_data { int cy_out; enum obs_scale_type sampling; gs_samplerstate_t *point_sampler; - bool aspect_ratio_only : 1; - bool target_valid : 1; - bool valid : 1; - bool undistort : 1; + bool aspect_ratio_only; + bool target_valid; + bool valid; + bool undistort; }; static const char *scale_filter_name(void *unused) diff --git a/plugins/obs-libfdk/data/locale/bn-BD.ini b/plugins/obs-libfdk/data/locale/bn-BD.ini new file mode 100644 index 0000000..d2e2c03 --- /dev/null +++ b/plugins/obs-libfdk/data/locale/bn-BD.ini @@ -0,0 +1,3 @@ +LibFDK="libfdk AAC এনকোডার" +Bitrate="বিটরেট" + diff --git a/plugins/obs-libfdk/data/locale/et-EE.ini b/plugins/obs-libfdk/data/locale/et-EE.ini index 08ab058..aeadf91 100644 --- a/plugins/obs-libfdk/data/locale/et-EE.ini +++ b/plugins/obs-libfdk/data/locale/et-EE.ini @@ -1,3 +1,4 @@ LibFDK="libfdk AAC kodeerija" Bitrate="Bitikiirus" +Afterburner="Luba AAC Afterburner" diff --git a/plugins/obs-libfdk/data/locale/hi-IN.ini b/plugins/obs-libfdk/data/locale/hi-IN.ini new file mode 100644 index 0000000..226d55c --- /dev/null +++ b/plugins/obs-libfdk/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +LibFDK="libfdk AAC एनकोडर" + diff --git a/plugins/obs-libfdk/data/locale/ur-PK.ini b/plugins/obs-libfdk/data/locale/ur-PK.ini new file mode 100644 index 0000000..eb205a3 --- /dev/null +++ b/plugins/obs-libfdk/data/locale/ur-PK.ini @@ -0,0 +1,2 @@ +LibFDK="لابفدک AAC انکوڈر" + diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index 0a0b114..c8bbf81 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -69,6 +69,7 @@ set(obs-outputs_HEADERS librtmp) set(obs-outputs_SOURCES obs-outputs.c + null-output.c rtmp-stream.c rtmp-windows.c flv-output.c diff --git a/plugins/obs-outputs/data/locale/ar-SA.ini b/plugins/obs-outputs/data/locale/ar-SA.ini index fe2e41f..81799e9 100644 --- a/plugins/obs-outputs/data/locale/ar-SA.ini +++ b/plugins/obs-outputs/data/locale/ar-SA.ini @@ -3,3 +3,4 @@ RTMPStream.DropThreshold="انخفاض البداية (مللي ثانية)" FLVOutput="اخراج الملف بصيغة FLV" FLVOutput.FilePath="مسار الملف" + diff --git a/plugins/obs-outputs/data/locale/bn-BD.ini b/plugins/obs-outputs/data/locale/bn-BD.ini new file mode 100644 index 0000000..4b28f5c --- /dev/null +++ b/plugins/obs-outputs/data/locale/bn-BD.ini @@ -0,0 +1,5 @@ +RTMPStream="RTMP স্ট্রিম" +RTMPStream.DropThreshold="ড্রপ সীমার (মিলিসেকেন্ড)" +Default="পূর্ব-নির্ধারিত" + + diff --git a/plugins/obs-outputs/data/locale/ca-ES.ini b/plugins/obs-outputs/data/locale/ca-ES.ini index dbeb5fc..ecd362e 100644 --- a/plugins/obs-outputs/data/locale/ca-ES.ini +++ b/plugins/obs-outputs/data/locale/ca-ES.ini @@ -4,3 +4,4 @@ FLVOutput="Sortida del fitxer FLV" FLVOutput.FilePath="Camí del fitxer" Default="Per defecte" + diff --git a/plugins/obs-outputs/data/locale/cs-CZ.ini b/plugins/obs-outputs/data/locale/cs-CZ.ini index 8d0f9ce..3c56618 100644 --- a/plugins/obs-outputs/data/locale/cs-CZ.ini +++ b/plugins/obs-outputs/data/locale/cs-CZ.ini @@ -4,3 +4,11 @@ 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é)." + diff --git a/plugins/obs-outputs/data/locale/da-DK.ini b/plugins/obs-outputs/data/locale/da-DK.ini index 292cbf0..0a068fb 100644 --- a/plugins/obs-outputs/data/locale/da-DK.ini +++ b/plugins/obs-outputs/data/locale/da-DK.ini @@ -4,3 +4,11 @@ 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)." + diff --git a/plugins/obs-outputs/data/locale/de-DE.ini b/plugins/obs-outputs/data/locale/de-DE.ini index 7663210..dcdb824 100644 --- a/plugins/obs-outputs/data/locale/de-DE.ini +++ b/plugins/obs-outputs/data/locale/de-DE.ini @@ -4,3 +4,11 @@ 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)." + diff --git a/plugins/obs-outputs/data/locale/el-GR.ini b/plugins/obs-outputs/data/locale/el-GR.ini index e31000f..9d7dfce 100644 --- a/plugins/obs-outputs/data/locale/el-GR.ini +++ b/plugins/obs-outputs/data/locale/el-GR.ini @@ -4,3 +4,4 @@ FLVOutput="FLV Αρχείο Εξόδου" FLVOutput.FilePath="Διαδρομή Αρχείου" Default="Προεπιλογή" + diff --git a/plugins/obs-outputs/data/locale/en-US.ini b/plugins/obs-outputs/data/locale/en-US.ini index c28e54e..58596b1 100644 --- a/plugins/obs-outputs/data/locale/en-US.ini +++ b/plugins/obs-outputs/data/locale/en-US.ini @@ -3,3 +3,11 @@ 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)." diff --git a/plugins/obs-outputs/data/locale/es-ES.ini b/plugins/obs-outputs/data/locale/es-ES.ini index 8286872..5381632 100644 --- a/plugins/obs-outputs/data/locale/es-ES.ini +++ b/plugins/obs-outputs/data/locale/es-ES.ini @@ -4,3 +4,7 @@ FLVOutput="Archivo de salida FLV" FLVOutput.FilePath="Ruta de archivo" Default="Por defecto" +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." +AddressNotAvailable="Dirección no disponible. Puede que hayas intentado enlazar con una dirección IP no valida (vea Configuración / Avanzado)." + diff --git a/plugins/obs-outputs/data/locale/et-EE.ini b/plugins/obs-outputs/data/locale/et-EE.ini index dfca04e..9ac2c59 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 a5eb350..663d34b 100644 --- a/plugins/obs-outputs/data/locale/eu-ES.ini +++ b/plugins/obs-outputs/data/locale/eu-ES.ini @@ -4,3 +4,11 @@ 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)." + diff --git a/plugins/obs-outputs/data/locale/fi-FI.ini b/plugins/obs-outputs/data/locale/fi-FI.ini index e4e8491..7a815c0 100644 --- a/plugins/obs-outputs/data/locale/fi-FI.ini +++ b/plugins/obs-outputs/data/locale/fi-FI.ini @@ -4,3 +4,11 @@ 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)." + diff --git a/plugins/obs-outputs/data/locale/fr-FR.ini b/plugins/obs-outputs/data/locale/fr-FR.ini index ad4b9b8..f63beec 100644 --- a/plugins/obs-outputs/data/locale/fr-FR.ini +++ b/plugins/obs-outputs/data/locale/fr-FR.ini @@ -4,3 +4,7 @@ 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." + diff --git a/plugins/obs-outputs/data/locale/gl-ES.ini b/plugins/obs-outputs/data/locale/gl-ES.ini index 69c31e5..bc89bf0 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 836a106..77f7987 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/hi-IN.ini b/plugins/obs-outputs/data/locale/hi-IN.ini new file mode 100644 index 0000000..b6a7c2d --- /dev/null +++ b/plugins/obs-outputs/data/locale/hi-IN.ini @@ -0,0 +1,3 @@ +RTMPStream="RTMP स्ट्रीम" + + diff --git a/plugins/obs-outputs/data/locale/hr-HR.ini b/plugins/obs-outputs/data/locale/hr-HR.ini index 6c94bfe..2310a6e 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 bd3c51a..41352d9 100644 --- a/plugins/obs-outputs/data/locale/hu-HU.ini +++ b/plugins/obs-outputs/data/locale/hu-HU.ini @@ -4,3 +4,11 @@ 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ó)." + diff --git a/plugins/obs-outputs/data/locale/it-IT.ini b/plugins/obs-outputs/data/locale/it-IT.ini index 863a954..28455fb 100644 --- a/plugins/obs-outputs/data/locale/it-IT.ini +++ b/plugins/obs-outputs/data/locale/it-IT.ini @@ -4,3 +4,4 @@ FLVOutput="Uscita file FLV" FLVOutput.FilePath="Destinazione file" Default="Predefinito" + diff --git a/plugins/obs-outputs/data/locale/ja-JP.ini b/plugins/obs-outputs/data/locale/ja-JP.ini index 659d21b..25f6a00 100644 --- a/plugins/obs-outputs/data/locale/ja-JP.ini +++ b/plugins/obs-outputs/data/locale/ja-JP.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/ko-KR.ini b/plugins/obs-outputs/data/locale/ko-KR.ini index ec2669d..6ec434f 100644 --- a/plugins/obs-outputs/data/locale/ko-KR.ini +++ b/plugins/obs-outputs/data/locale/ko-KR.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/nb-NO.ini b/plugins/obs-outputs/data/locale/nb-NO.ini index 7ad0194..50015aa 100644 --- a/plugins/obs-outputs/data/locale/nb-NO.ini +++ b/plugins/obs-outputs/data/locale/nb-NO.ini @@ -4,3 +4,4 @@ FLVOutput="FLV-filutgang" FLVOutput.FilePath="Filbane" Default="Standard" + diff --git a/plugins/obs-outputs/data/locale/nl-NL.ini b/plugins/obs-outputs/data/locale/nl-NL.ini index 944c809..0ef923f 100644 --- a/plugins/obs-outputs/data/locale/nl-NL.ini +++ b/plugins/obs-outputs/data/locale/nl-NL.ini @@ -4,3 +4,11 @@ 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)." + diff --git a/plugins/obs-outputs/data/locale/pl-PL.ini b/plugins/obs-outputs/data/locale/pl-PL.ini index c5676ee..f612ef3 100644 --- a/plugins/obs-outputs/data/locale/pl-PL.ini +++ b/plugins/obs-outputs/data/locale/pl-PL.ini @@ -4,3 +4,11 @@ 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)." + diff --git a/plugins/obs-outputs/data/locale/pt-BR.ini b/plugins/obs-outputs/data/locale/pt-BR.ini index f070534..3ede596 100644 --- a/plugins/obs-outputs/data/locale/pt-BR.ini +++ b/plugins/obs-outputs/data/locale/pt-BR.ini @@ -4,3 +4,11 @@ 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)." + diff --git a/plugins/obs-outputs/data/locale/pt-PT.ini b/plugins/obs-outputs/data/locale/pt-PT.ini index ebcd465..c3442dd 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 2333b47..9d67c92 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 ec14e97..d113e39 100644 --- a/plugins/obs-outputs/data/locale/ru-RU.ini +++ b/plugins/obs-outputs/data/locale/ru-RU.ini @@ -4,3 +4,4 @@ FLVOutput="Выходной файл FLV" FLVOutput.FilePath="Путь к файлу" Default="По умолчанию" + diff --git a/plugins/obs-outputs/data/locale/sk-SK.ini b/plugins/obs-outputs/data/locale/sk-SK.ini index 4949ef4..56b7d7b 100644 --- a/plugins/obs-outputs/data/locale/sk-SK.ini +++ b/plugins/obs-outputs/data/locale/sk-SK.ini @@ -3,3 +3,4 @@ FLVOutput="Výstup do súboru FLV" FLVOutput.FilePath="Cesta k súboru" Default="Základné" + diff --git a/plugins/obs-outputs/data/locale/sl-SI.ini b/plugins/obs-outputs/data/locale/sl-SI.ini index c74af98..2451f6d 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 6c94bfe..2310a6e 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 b509fa7..ceb2473 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 3dbc1d4..6b17e4d 100644 --- a/plugins/obs-outputs/data/locale/sv-SE.ini +++ b/plugins/obs-outputs/data/locale/sv-SE.ini @@ -4,3 +4,10 @@ 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." +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)." + diff --git a/plugins/obs-outputs/data/locale/th-TH.ini b/plugins/obs-outputs/data/locale/th-TH.ini index d1d63e0..f136bd9 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/tr-TR.ini b/plugins/obs-outputs/data/locale/tr-TR.ini index 6da5420..8215642 100644 --- a/plugins/obs-outputs/data/locale/tr-TR.ini +++ b/plugins/obs-outputs/data/locale/tr-TR.ini @@ -4,3 +4,11 @@ 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ş)." + diff --git a/plugins/obs-outputs/data/locale/uk-UA.ini b/plugins/obs-outputs/data/locale/uk-UA.ini index de0fe2e..aa253b3 100644 --- a/plugins/obs-outputs/data/locale/uk-UA.ini +++ b/plugins/obs-outputs/data/locale/uk-UA.ini @@ -4,3 +4,11 @@ FLVOutput="Вивід FLV файлу" FLVOutput.FilePath="Шлях до файлу" Default="За замовчанням" +ConnectionTimedOut="Вичерпано час підключення. Переконайтеся, що ви вказали правильний сервіс трансляцій і брандмауер не блокує з'єднання." +PermissionDenied="З'єднання було заблоковано. Перевірте налаштування вашого брандмауеру / антивірусу, щоб переконатися, що OBS має повний доступ до Інтернету." +ConnectionAborted="З'єднання було перервано. Зазвичай свідчить про проблеми з Інтернет підключенням між вами і постачальником з сервісу трансляцій." +ConnectionReset="З'єднання було скинуте рівноправним вузлом (reset by peer). Зазвичай свідчить про проблеми з Інтернет підключенням між вами і постачальником з сервісу трансляцій." +HostNotFound="Ім'я хоста, не знайдено. Переконайтеся, що ви ввели дійсний сервер трансляцій і підключення до Інтернету / DNS працює правильно." +NoData="Ім'я хоста знайдено, але нема жодних даних вказаного типу. Це може статися, якщо ви вказали прив'язку до IPv6-адресу, але ваш сервіс трансляцій підтримує лише адреси IPv4 (див. Налаштування / Розширені)." +AddressNotAvailable="Адреса недоступна. Напевно ви спробували прив'язатись до адаптера з неіснуючую IP-адресою (див. Налаштування / Розширені)." + diff --git a/plugins/obs-outputs/data/locale/ur-PK.ini b/plugins/obs-outputs/data/locale/ur-PK.ini new file mode 100644 index 0000000..8ca53a5 --- /dev/null +++ b/plugins/obs-outputs/data/locale/ur-PK.ini @@ -0,0 +1,3 @@ +RTMPStream="رٹمپ اسٹریم" + + diff --git a/plugins/obs-outputs/data/locale/vi-VN.ini b/plugins/obs-outputs/data/locale/vi-VN.ini index 8dc44d6..f71ceb6 100644 --- a/plugins/obs-outputs/data/locale/vi-VN.ini +++ b/plugins/obs-outputs/data/locale/vi-VN.ini @@ -1,3 +1,12 @@ +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" +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 đề." +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 2f0a123..950273d 100644 --- a/plugins/obs-outputs/data/locale/zh-CN.ini +++ b/plugins/obs-outputs/data/locale/zh-CN.ini @@ -4,3 +4,11 @@ FLVOutput="FLV 文件输出" FLVOutput.FilePath="文件路径" Default="默认" +ConnectionTimedOut="连接超时. 请确保您已经配置了一个有效的流媒体服务并且没有防火墙阻止连接." +PermissionDenied="连接被阻止. 检查您的防火墙 / 防病毒设置以确保允许 OBS 自由访问互联网." +ConnectionAborted="连接被中止. 这通常表明你和流媒体服务之间的互联网连接问题." +ConnectionReset="对方重置连接. 这通常表明你和流媒体服务之间的互联网连接问题." +HostNotFound="找不到 Hostname. 请确保您输入一个有效的流媒体服务器并且您的互联网连接 / DNS 工作正常." +NoData="Hostname 发现, 但没有请求的类型的数据的主机名. 这有可能因为你绑定到 IPv6 地址并且你的流媒体服务仅有 IPv4 地址 (请参阅设置 / 高级)." +AddressNotAvailable="没有可用的地址. 你可能在试图绑定到一个无效的 IP 地址 (请参阅设置 / 高级)." + diff --git a/plugins/obs-outputs/data/locale/zh-TW.ini b/plugins/obs-outputs/data/locale/zh-TW.ini index 679909a..ab05bb8 100644 --- a/plugins/obs-outputs/data/locale/zh-TW.ini +++ b/plugins/obs-outputs/data/locale/zh-TW.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/flv-mux.c b/plugins/obs-outputs/flv-mux.c index 1978a8a..e74b2da 100644 --- a/plugins/obs-outputs/flv-mux.c +++ b/plugins/obs-outputs/flv-mux.c @@ -208,7 +208,7 @@ static void flv_video(struct serializer *s, struct encoder_packet *packet, s_wb24(s, get_ms_time(packet, offset)); s_write(s, packet->data, packet->size); - /* write tag size (starting byte doesnt count) */ + /* write tag size (starting byte doesn't count) */ s_wb32(s, (uint32_t)serializer_get_pos(s) + 4 - 1); } @@ -241,7 +241,7 @@ static void flv_audio(struct serializer *s, struct encoder_packet *packet, s_w8(s, is_header ? 0 : 1); s_write(s, packet->data, packet->size); - /* write tag size (starting byte doesnt count) */ + /* write tag size (starting byte doesn't count) */ s_wb32(s, (uint32_t)serializer_get_pos(s) + 4 - 1); } diff --git a/plugins/obs-outputs/librtmp/cencode.c b/plugins/obs-outputs/librtmp/cencode.c index c415aff..6259fd7 100644 --- a/plugins/obs-outputs/librtmp/cencode.c +++ b/plugins/obs-outputs/librtmp/cencode.c @@ -48,6 +48,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; + /* Falls through. */ case step_B: if (plainchar == plaintextend) { @@ -59,6 +60,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; + /* Falls through. */ case step_C: if (plainchar == plaintextend) { diff --git a/plugins/obs-outputs/librtmp/rtmp.c b/plugins/obs-outputs/librtmp/rtmp.c index a017b07..dccad0e 100644 --- a/plugins/obs-outputs/librtmp/rtmp.c +++ b/plugins/obs-outputs/librtmp/rtmp.c @@ -32,6 +32,8 @@ #include "rtmp_sys.h" #include "log.h" +#include + #ifdef CRYPTO #ifdef __APPLE__ @@ -650,7 +652,7 @@ int RTMP_AddStream(RTMP *r, const char *playpath) } static int -add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, int port, socklen_t addrlen_hint) +add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, int port, socklen_t addrlen_hint, int *socket_error) { char *hostname; int ret = TRUE; @@ -691,6 +693,7 @@ add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, #define gai_strerrorA gai_strerror #endif RTMP_Log(RTMP_LOGERROR, "Could not resolve %s: %s (%d)", hostname, gai_strerrorA(GetSockError()), GetSockError()); + *socket_error = GetSockError(); ret = FALSE; goto finish; } @@ -706,23 +709,32 @@ add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, } } - if (!*addrlen) - { - for (ptr = result; ptr != NULL; ptr = ptr->ai_next) - { + if (!*addrlen) + { + for (ptr = result; ptr != NULL; ptr = ptr->ai_next) + { if (ptr->ai_family == AF_INET6 && (!addrlen_hint || ptr->ai_addrlen == addrlen_hint)) - { - memcpy(service, ptr->ai_addr, ptr->ai_addrlen); - *addrlen = (socklen_t)ptr->ai_addrlen; - break; - } - } - } + { + memcpy(service, ptr->ai_addr, ptr->ai_addrlen); + *addrlen = (socklen_t)ptr->ai_addrlen; + break; + } + } + } freeaddrinfo(result); if (service->ss_family == AF_UNSPEC || *addrlen == 0) { + // since we're handling multiple addresses internally, fake the correct error response +#ifdef _WIN32 + *socket_error = WSANO_DATA; +#elif __FreeBSD__ + *socket_error = ENOATTR; +#else + *socket_error = ENODATA; +#endif + RTMP_Log(RTMP_LOGERROR, "Could not resolve server '%s': no valid address found", hostname); ret = FALSE; goto finish; @@ -768,11 +780,14 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service, socklen_t addrlen) int err = GetSockError(); RTMP_Log(RTMP_LOGERROR, "%s, failed to bind socket: %s (%d)", __FUNCTION__, socketerror(err), err); + r->last_error_code = err; RTMP_Close(r); return FALSE; } } + uint64_t connect_start = os_gettime_ns(); + if (connect(r->m_sb.sb_socket, service, addrlen) < 0) { int err = GetSockError(); @@ -785,10 +800,13 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service, socklen_t addrlen) else RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket: %s (%d)", __FUNCTION__, socketerror(err), err); + r->last_error_code = err; RTMP_Close(r); return FALSE; } + r->connect_time_ms = (int)((os_gettime_ns() - connect_start) / 1000000); + if (r->Link.socksport) { RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__); @@ -906,6 +924,8 @@ RTMP_Connect(RTMP *r, RTMPPacket *cp) struct sockaddr_storage service; socklen_t addrlen = 0; socklen_t addrlen_hint = 0; + int socket_error = 0; + if (!r->Link.hostname.av_len) return FALSE; @@ -914,6 +934,7 @@ RTMP_Connect(RTMP *r, RTMPPacket *cp) h = gethostbyname("localhost"); if (!h && GetLastError() == WSAHOST_NOT_FOUND) { + r->last_error_code = WSAHOST_NOT_FOUND; RTMP_Log(RTMP_LOGERROR, "RTMP_Connect: Connection test failed. This error is likely caused by Comodo Internet Security running OBS in sandbox mode. Please add OBS to the Comodo automatic sandbox exclusion list, restart OBS and try again (11001)."); return FALSE; } @@ -927,14 +948,20 @@ RTMP_Connect(RTMP *r, RTMPPacket *cp) if (r->Link.socksport) { /* Connect via SOCKS */ - if (!add_addr_info(&service, &addrlen, &r->Link.sockshost, r->Link.socksport, addrlen_hint)) + if (!add_addr_info(&service, &addrlen, &r->Link.sockshost, r->Link.socksport, addrlen_hint, &socket_error)) + { + r->last_error_code = socket_error; return FALSE; + } } else { /* Connect directly */ - if (!add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, addrlen_hint)) + if (!add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, addrlen_hint, &socket_error)) + { + r->last_error_code = socket_error; return FALSE; + } } if (!RTMP_Connect0(r, (struct sockaddr *)&service, addrlen)) @@ -951,9 +978,10 @@ SocksNegotiate(RTMP *r) unsigned long addr; struct sockaddr_storage service; socklen_t addrlen = 0; + int socket_error = 0; memset(&service, 0, sizeof(service)); - add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, 0); + add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, 0, &socket_error); // not doing IPv6 socks if (service.ss_family == AF_INET6) @@ -1436,6 +1464,8 @@ WriteN(RTMP *r, const char *buffer, int n) if (sockerr == EINTR && !RTMP_ctrlC) continue; + r->last_error_code = sockerr; + RTMP_Close(r); n = 1; break; diff --git a/plugins/obs-outputs/librtmp/rtmp.h b/plugins/obs-outputs/librtmp/rtmp.h index bc59c57..0975190 100644 --- a/plugins/obs-outputs/librtmp/rtmp.h +++ b/plugins/obs-outputs/librtmp/rtmp.h @@ -324,6 +324,8 @@ extern "C" RTMPPacket m_write; RTMPSockBuf m_sb; RTMP_LNK Link; + int connect_time_ms; + int last_error_code; } RTMP; int RTMP_ParseURL(const char *url, int *protocol, AVal *host, diff --git a/plugins/obs-outputs/null-output.c b/plugins/obs-outputs/null-output.c new file mode 100644 index 0000000..6ea9e48 --- /dev/null +++ b/plugins/obs-outputs/null-output.c @@ -0,0 +1,99 @@ +/****************************************************************************** + Copyright (C) 2017 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 +#include + +struct null_output { + obs_output_t *output; + + pthread_t stop_thread; + bool stop_thread_active; +}; + +static const char *null_output_getname(void *unused) +{ + UNUSED_PARAMETER(unused); + return "Null Encoding Output"; +} + +static void *null_output_create(obs_data_t *settings, obs_output_t *output) +{ + struct null_output *context = bzalloc(sizeof(*context)); + context->output = output; + UNUSED_PARAMETER(settings); + return context; +} + +static void null_output_destroy(void *data) +{ + struct null_output *context = data; + if (context->stop_thread_active) + pthread_join(context->stop_thread, NULL); + bfree(context); +} + +static bool null_output_start(void *data) +{ + struct null_output *context = data; + + if (!obs_output_can_begin_data_capture(context->output, 0)) + return false; + if (!obs_output_initialize_encoders(context->output, 0)) + return false; + + if (context->stop_thread_active) + pthread_join(context->stop_thread, NULL); + + obs_output_begin_data_capture(context->output, 0); + return true; +} + +static void *stop_thread(void *data) +{ + struct null_output *context = data; + obs_output_end_data_capture(context->output); + context->stop_thread_active = false; + return NULL; +} + +static void null_output_stop(void *data, uint64_t ts) +{ + struct null_output *context = data; + UNUSED_PARAMETER(ts); + + context->stop_thread_active = pthread_create(&context->stop_thread, + NULL, stop_thread, data) == 0; +} + +static void null_output_data(void *data, struct encoder_packet *packet) +{ + struct null_output *context = data; + UNUSED_PARAMETER(packet); +} + +struct obs_output_info null_output_info = { + .id = "null_output", + .flags = OBS_OUTPUT_AV | + OBS_OUTPUT_ENCODED, + .get_name = null_output_getname, + .create = null_output_create, + .destroy = null_output_destroy, + .start = null_output_start, + .stop = null_output_stop, + .encoded_packet = null_output_data +}; diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c index 6277c00..d452330 100644 --- a/plugins/obs-outputs/obs-outputs.c +++ b/plugins/obs-outputs/obs-outputs.c @@ -9,6 +9,7 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-outputs", "en-US") extern struct obs_output_info rtmp_output_info; +extern struct obs_output_info null_output_info; extern struct obs_output_info flv_output_info; bool obs_module_load(void) @@ -19,6 +20,7 @@ bool obs_module_load(void) #endif obs_register_output(&rtmp_output_info); + obs_register_output(&null_output_info); obs_register_output(&flv_output_info); return true; } diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 25d3825..3026b6a 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -378,6 +378,64 @@ static inline bool can_shutdown_stream(struct rtmp_stream *stream, return timeout || packet->sys_dts_usec >= (int64_t)stream->stop_ts; } +static void set_output_error(struct rtmp_stream *stream) +{ + const char *msg = NULL; +#ifdef _WIN32 + switch (stream->rtmp.last_error_code) + { + case WSAETIMEDOUT: + msg = obs_module_text("ConnectionTimedOut"); + break; + case WSAEACCES: + msg = obs_module_text("PermissionDenied"); + break; + case WSAECONNABORTED: + msg = obs_module_text("ConnectionAborted"); + break; + case WSAECONNRESET: + msg = obs_module_text("ConnectionReset"); + break; + case WSAHOST_NOT_FOUND: + msg = obs_module_text("HostNotFound"); + break; + case WSANO_DATA: + msg = obs_module_text("NoData"); + break; + case WSAEADDRNOTAVAIL: + msg = obs_module_text("AddressNotAvailable"); + break; + } +#else + switch (stream->rtmp.last_error_code) + { + case ETIMEDOUT: + msg = obs_module_text("ConnectionTimedOut"); + break; + case EACCES: + msg = obs_module_text("PermissionDenied"); + break; + case ECONNABORTED: + msg = obs_module_text("ConnectionAborted"); + break; + case ECONNRESET: + msg = obs_module_text("ConnectionReset"); + break; + case HOST_NOT_FOUND: + msg = obs_module_text("HostNotFound"); + break; + case NO_DATA: + msg = obs_module_text("NoData"); + break; + case EADDRNOTAVAIL: + msg = obs_module_text("AddressNotAvailable"); + break; + } +#endif + + obs_output_set_last_error(stream->output, msg); +} + static void *send_thread(void *data) { struct rtmp_stream *stream = data; @@ -428,6 +486,7 @@ static void *send_thread(void *data) stream->rtmp.m_bCustomSend = false; } + set_output_error(stream); RTMP_Close(&stream->rtmp); if (!stopping(stream)) { @@ -571,8 +630,10 @@ static int init_send(struct rtmp_stream *stream) int one = 1; #ifdef _WIN32 if (ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) { + stream->rtmp.last_error_code = WSAGetLastError(); #else if (ioctl(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) { + stream->rtmp.last_error_code = errno; #endif warn("Failed to set non-blocking socket"); return OBS_OUTPUT_ERROR; @@ -644,6 +705,7 @@ static int init_send(struct rtmp_stream *stream) if (!send_meta_data(stream, idx++, &next)) { warn("Disconnected while attempting to connect to " "server."); + set_output_error(stream); return OBS_OUTPUT_DISCONNECTED; } } @@ -718,7 +780,7 @@ static int try_connect(struct rtmp_stream *stream) info("Connecting to RTMP URL %s...", stream->path.array); - memset(&stream->rtmp.Link, 0, sizeof(stream->rtmp.Link)); + RTMP_Init(&stream->rtmp); if (!RTMP_SetupURL(&stream->rtmp, stream->path.array)) return OBS_OUTPUT_BAD_PATH; @@ -767,8 +829,11 @@ static int try_connect(struct rtmp_stream *stream) win32_log_interface_type(stream); #endif - if (!RTMP_Connect(&stream->rtmp, NULL)) + if (!RTMP_Connect(&stream->rtmp, NULL)) { + set_output_error(stream); return OBS_OUTPUT_CONNECT_FAILED; + } + if (!RTMP_ConnectStream(&stream->rtmp, 0)) return OBS_OUTPUT_INVALID_STREAM; @@ -798,7 +863,6 @@ static bool init_connect(struct rtmp_stream *stream) os_atomic_set_bool(&stream->disconnected, false); stream->total_bytes_sent = 0; stream->dropped_frames = 0; - stream->min_drop_dts_usec= 0; stream->min_priority = 0; settings = obs_output_get_settings(stream->output); @@ -876,7 +940,6 @@ static inline bool add_packet(struct rtmp_stream *stream, { circlebuf_push_back(&stream->packets, packet, sizeof(struct encoder_packet)); - stream->last_dts_usec = packet->dts_usec; return true; } @@ -886,7 +949,7 @@ static inline size_t num_buffered_packets(struct rtmp_stream *stream) } static void drop_frames(struct rtmp_stream *stream, const char *name, - int highest_priority, int64_t *p_min_dts_usec) + int highest_priority, bool pframes) { struct circlebuf new_buf = {0}; uint64_t last_drop_dts_usec = 0; @@ -922,8 +985,8 @@ static void drop_frames(struct rtmp_stream *stream, const char *name, if (stream->min_priority < highest_priority) stream->min_priority = highest_priority; - - *p_min_dts_usec = last_drop_dts_usec; + if (!num_frames_dropped) + return; stream->dropped_frames += num_frames_dropped; #ifdef _DEBUG @@ -934,6 +997,23 @@ static void drop_frames(struct rtmp_stream *stream, const char *name, #endif } +static bool find_first_video_packet(struct rtmp_stream *stream, + struct encoder_packet *first) +{ + size_t count = stream->packets.size / sizeof(*first); + + for (size_t i = 0; i < count; i++) { + struct encoder_packet *cur = circlebuf_data(&stream->packets, + i * sizeof(*first)); + if (cur->type == OBS_ENCODER_VIDEO && !cur->keyframe) { + *first = *cur; + return true; + } + } + + return false; +} + static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes) { struct encoder_packet first; @@ -942,9 +1022,6 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes) const char *name = pframes ? "p-frames" : "b-frames"; int priority = pframes ? OBS_NAL_PRIORITY_HIGHEST : OBS_NAL_PRIORITY_HIGH; - int64_t *p_min_dts_usec = pframes ? - &stream->pframe_min_drop_dts_usec : - &stream->min_drop_dts_usec; int64_t drop_threshold = pframes ? stream->pframe_drop_threshold_usec : stream->drop_threshold_usec; @@ -955,10 +1032,7 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes) return; } - circlebuf_peek_front(&stream->packets, &first, sizeof(first)); - - /* do not drop frames if frames were just dropped within this time */ - if (first.dts_usec < *p_min_dts_usec) + if (!find_first_video_packet(stream, &first)) return; /* if the amount of time stored in the buffered packets waiting to be @@ -972,7 +1046,7 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes) if (buffer_duration_usec > drop_threshold) { debug("buffer_duration_usec: %" PRId64, buffer_duration_usec); - drop_frames(stream, name, priority, p_min_dts_usec); + drop_frames(stream, name, priority, pframes); } } @@ -991,6 +1065,7 @@ static bool add_video_packet(struct rtmp_stream *stream, stream->min_priority = 0; } + stream->last_dts_usec = packet->dts_usec; return add_packet(stream, packet); } @@ -1090,6 +1165,12 @@ static float rtmp_stream_congestion(void *data) return stream->min_priority > 0 ? 1.0f : stream->congestion; } +static int rtmp_stream_connect_time(void *data) +{ + struct rtmp_stream *stream = data; + return stream->rtmp.connect_time_ms; +} + struct obs_output_info rtmp_output_info = { .id = "rtmp_output", .flags = OBS_OUTPUT_AV | @@ -1106,5 +1187,6 @@ struct obs_output_info rtmp_output_info = { .get_properties = rtmp_stream_properties, .get_total_bytes = rtmp_stream_total_bytes_sent, .get_congestion = rtmp_stream_congestion, + .get_connect_time_ms= rtmp_stream_connect_time, .get_dropped_frames = rtmp_stream_dropped_frames }; diff --git a/plugins/obs-outputs/rtmp-stream.h b/plugins/obs-outputs/rtmp-stream.h index 38fd760..863eda6 100644 --- a/plugins/obs-outputs/rtmp-stream.h +++ b/plugins/obs-outputs/rtmp-stream.h @@ -72,9 +72,7 @@ struct rtmp_stream { /* frame drop variables */ int64_t drop_threshold_usec; - int64_t min_drop_dts_usec; int64_t pframe_drop_threshold_usec; - int64_t pframe_min_drop_dts_usec; int min_priority; float congestion; diff --git a/plugins/obs-outputs/rtmp-windows.c b/plugins/obs-outputs/rtmp-windows.c index 5a58753..64e8ec0 100644 --- a/plugins/obs-outputs/rtmp-windows.c +++ b/plugins/obs-outputs/rtmp-windows.c @@ -81,6 +81,7 @@ static bool socket_event(struct rtmp_stream *stream, bool *can_write, "Socket error, recv() returned " "%d, GetLastError() %d", ret, err_code); + stream->rtmp.last_error_code = err_code; fatal_sock_shutdown(stream); return false; } @@ -215,6 +216,7 @@ static enum data_ret write_data(struct rtmp_stream *stream, bool *can_write, ret, err_code); pthread_mutex_unlock(&stream->write_buf_mutex); + stream->rtmp.last_error_code = err_code; fatal_sock_shutdown(stream); return RET_FATAL; } diff --git a/plugins/obs-text/data/locale/bn-BD.ini b/plugins/obs-text/data/locale/bn-BD.ini new file mode 100644 index 0000000..160768a --- /dev/null +++ b/plugins/obs-text/data/locale/bn-BD.ini @@ -0,0 +1,10 @@ +TextGDIPlus="টেক্সট (জিডিআই +)" +ReadFromFile="ফাইল থেকে পড়া" +TextFile="টেক্সট ফাইল (UTF-8)" +Opacity="অস্বচ্ছতা" +Gradient="গ্রেডিয়েন্ট" +BkColor="পেছনের রঙ" +BkOpacity="ব্যাকগ্রাউন্ডের স্বচ্ছতা" +Alignment="সারিবদ্ধতা" +Alignment.Left="বামদিকে" + diff --git a/plugins/obs-text/data/locale/et-EE.ini b/plugins/obs-text/data/locale/et-EE.ini index a185cb1..d8cebd3 100644 --- a/plugins/obs-text/data/locale/et-EE.ini +++ b/plugins/obs-text/data/locale/et-EE.ini @@ -2,12 +2,24 @@ TextGDIPlus="Tekst (GDI +)" Font="Font" Text="Tekst" ReadFromFile="Loe failist" +TextFile="Tekstifail (UTF-8)" +Filter.TextFiles="Tekstifailid" +Filter.AllFiles="Kõik failid" Color="Värv" Opacity="Läbipaistvus" +BkOpacity="Tausta läbipaistvus" +Alignment="Joondus" +Alignment.Left="Vasakule" Alignment.Center="Keskele" +Alignment.Right="Paremale" +Vertical="Vertikaalne" +VerticalAlignment="Vertikaaljoondus" +VerticalAlignment.Top="Ülevalt" +VerticalAlignment.Bottom="Alt" Outline="Kontuur" Outline.Size="Kontuuri suurus" Outline.Color="Kontuuri värv" +Outline.Opacity="Kontuuri läbipaistvus" Width="Laius" Height="Kõrgus" diff --git a/plugins/obs-transitions/data/locale/bn-BD.ini b/plugins/obs-transitions/data/locale/bn-BD.ini new file mode 100644 index 0000000..2b5c609 --- /dev/null +++ b/plugins/obs-transitions/data/locale/bn-BD.ini @@ -0,0 +1,11 @@ +FadeTransition="ফেইড" +CutTransition="ছেদন" +SlideTransition="স্লাইড করুন" +FadeToColorTransition="রঙ বিলিন হয়ে যায়" +Direction.Up="উপরে" +Direction.Down="নিচের" +LumaWipeTransition="লুমা আল নামি মুছে ফেলুন" +LumaWipe.Image="ছবি" +LumaWipe.Invert="বিপরীতমুখী" +LumaWipe.Softness="তারল্য" + diff --git a/plugins/obs-transitions/data/locale/et-EE.ini b/plugins/obs-transitions/data/locale/et-EE.ini index 1486d15..7c61114 100644 --- a/plugins/obs-transitions/data/locale/et-EE.ini +++ b/plugins/obs-transitions/data/locale/et-EE.ini @@ -16,8 +16,14 @@ LumaWipe.Softness="Pehmus" LumaWipe.Type.BoxTopLeft="Kast üleval vasakul" LumaWipe.Type.BoxTopRight="Kast üleval paremal" LumaWipe.Type.Burst="Plahvatuse" +LumaWipe.Type.Circles="Ringid" LumaWipe.Type.Clock="Kell" LumaWipe.Type.Cloud="Pilv" LumaWipe.Type.Curtain="Kardin" LumaWipe.Type.Fan="Ventilaator" +LumaWipe.Type.Square="Ruut" +LumaWipe.Type.Squares="Ruudud" +LumaWipe.Type.Stripes="Triibud" +LumaWipe.Type.StripsHorizontal="Horisontaalsed triibud" +LumaWipe.Type.StripsVertical="Vertikaalsed triibud" diff --git a/plugins/obs-transitions/data/locale/pt-BR.ini b/plugins/obs-transitions/data/locale/pt-BR.ini index bec4e96..3a55c32 100644 --- a/plugins/obs-transitions/data/locale/pt-BR.ini +++ b/plugins/obs-transitions/data/locale/pt-BR.ini @@ -20,6 +20,12 @@ LumaWipe.Type.BarndoorHorizontal="Barndoor Horizontal" LumaWipe.Type.BarndoorTopLeft="Barndoor inferior esquerdo" LumaWipe.Type.BarndoorVertical="Barndoor Vertical" LumaWipe.Type.BlindsHorizontal="Persianas horizontais" +LumaWipe.Type.BoxBottomLeft="Caixa Inferior Esquerda" +LumaWipe.Type.BoxBottomRight="Caixa Inferior Direita" +LumaWipe.Type.BoxTopLeft="Caixa Superior Esquerda" +LumaWipe.Type.BoxTopRight="Caixa Superior Direita" +LumaWipe.Type.Burst="Explosão" +LumaWipe.Type.CheckerboardSmall="Quadriculado pequeno" LumaWipe.Type.Circles="Círculos" LumaWipe.Type.Clock="Relógio" LumaWipe.Type.Cloud="Nuvem" @@ -31,6 +37,9 @@ LumaWipe.Type.LinearHorizontal="Linear Horizontal" LumaWipe.Type.LinearTopLeft="Linear superior esquerdo" LumaWipe.Type.LinearTopRight="Linear superior direito" LumaWipe.Type.LinearVertical="Linear Vertical" +LumaWipe.Type.ParallelZigzagHorizontal="Zigue-Zague Paralelo Horizontal" +LumaWipe.Type.ParallelZigzagVertical="Zigue-Zague Paralelo Vertical" +LumaWipe.Type.Sinus9="Sinus 9" LumaWipe.Type.Spiral="Espiral" LumaWipe.Type.Square="Quadrado" LumaWipe.Type.Squares="Quadrados" diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-botleft.png b/plugins/obs-transitions/data/luma_wipes/barndoor-botleft.png index 0e83cfc..b238a55 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/barndoor-botleft.png and b/plugins/obs-transitions/data/luma_wipes/barndoor-botleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-h.png b/plugins/obs-transitions/data/luma_wipes/barndoor-h.png index c60729e..2c121dc 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/barndoor-h.png and b/plugins/obs-transitions/data/luma_wipes/barndoor-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-topleft.png b/plugins/obs-transitions/data/luma_wipes/barndoor-topleft.png index a21fd8f..93397cd 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/barndoor-topleft.png and b/plugins/obs-transitions/data/luma_wipes/barndoor-topleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/barndoor-v.png b/plugins/obs-transitions/data/luma_wipes/barndoor-v.png index bb538e5..fa7a179 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/barndoor-v.png and b/plugins/obs-transitions/data/luma_wipes/barndoor-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/blinds-h.png b/plugins/obs-transitions/data/luma_wipes/blinds-h.png index 8de01de..60172b7 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/blinds-h.png and b/plugins/obs-transitions/data/luma_wipes/blinds-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-botleft.png b/plugins/obs-transitions/data/luma_wipes/box-botleft.png index 0be56f5..8cc689a 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/box-botleft.png and b/plugins/obs-transitions/data/luma_wipes/box-botleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-botright.png b/plugins/obs-transitions/data/luma_wipes/box-botright.png index c2b15e8..b003cc8 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/box-botright.png and b/plugins/obs-transitions/data/luma_wipes/box-botright.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-topleft.png b/plugins/obs-transitions/data/luma_wipes/box-topleft.png index cbef40e..f089504 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/box-topleft.png and b/plugins/obs-transitions/data/luma_wipes/box-topleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/box-topright.png b/plugins/obs-transitions/data/luma_wipes/box-topright.png index 0e55734..576a27f 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/box-topright.png and b/plugins/obs-transitions/data/luma_wipes/box-topright.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/burst.png b/plugins/obs-transitions/data/luma_wipes/burst.png index 74e5f4e..78bec01 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/burst.png and b/plugins/obs-transitions/data/luma_wipes/burst.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/checkerboard-small.png b/plugins/obs-transitions/data/luma_wipes/checkerboard-small.png index d6fb839..7aa7321 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/checkerboard-small.png and b/plugins/obs-transitions/data/luma_wipes/checkerboard-small.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/circles.png b/plugins/obs-transitions/data/luma_wipes/circles.png index 2b83a1b..16fb377 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/circles.png and b/plugins/obs-transitions/data/luma_wipes/circles.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/clock.png b/plugins/obs-transitions/data/luma_wipes/clock.png index 3bd2b87..d3f1bf7 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/clock.png and b/plugins/obs-transitions/data/luma_wipes/clock.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/cloud.png b/plugins/obs-transitions/data/luma_wipes/cloud.png index 0095f71..4cf92dd 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/cloud.png and b/plugins/obs-transitions/data/luma_wipes/cloud.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/curtain.png b/plugins/obs-transitions/data/luma_wipes/curtain.png index 552ee17..f8d54c4 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/curtain.png and b/plugins/obs-transitions/data/luma_wipes/curtain.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/fan.png b/plugins/obs-transitions/data/luma_wipes/fan.png index 4fd4957..bc289c7 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/fan.png and b/plugins/obs-transitions/data/luma_wipes/fan.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/fractal.png b/plugins/obs-transitions/data/luma_wipes/fractal.png index b875a50..0232392 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/fractal.png and b/plugins/obs-transitions/data/luma_wipes/fractal.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/iris.png b/plugins/obs-transitions/data/luma_wipes/iris.png index 69e637c..c330bc1 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/iris.png and b/plugins/obs-transitions/data/luma_wipes/iris.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-h.png b/plugins/obs-transitions/data/luma_wipes/linear-h.png index 10ce279..05864db 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/linear-h.png and b/plugins/obs-transitions/data/luma_wipes/linear-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-topleft.png b/plugins/obs-transitions/data/luma_wipes/linear-topleft.png index c5bf908..63f4f8e 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/linear-topleft.png and b/plugins/obs-transitions/data/luma_wipes/linear-topleft.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-topright.png b/plugins/obs-transitions/data/luma_wipes/linear-topright.png index dfcb76c..8256969 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/linear-topright.png and b/plugins/obs-transitions/data/luma_wipes/linear-topright.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/linear-v.png b/plugins/obs-transitions/data/luma_wipes/linear-v.png index 031970a..694374c 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/linear-v.png and b/plugins/obs-transitions/data/luma_wipes/linear-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-h.png b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-h.png index 05e5e57..7dd278e 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-h.png and b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-v.png b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-v.png index 2c9ee23..f68f299 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-v.png and b/plugins/obs-transitions/data/luma_wipes/parallel-zigzag-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/sinus9.png b/plugins/obs-transitions/data/luma_wipes/sinus9.png index 07dc820..66511ad 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/sinus9.png and b/plugins/obs-transitions/data/luma_wipes/sinus9.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/spiral.png b/plugins/obs-transitions/data/luma_wipes/spiral.png index a07a501..e5bf439 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/spiral.png and b/plugins/obs-transitions/data/luma_wipes/spiral.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/square.png b/plugins/obs-transitions/data/luma_wipes/square.png index f33faf3..a7b216b 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/square.png and b/plugins/obs-transitions/data/luma_wipes/square.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/squares.png b/plugins/obs-transitions/data/luma_wipes/squares.png index 2cc9ff4..e5fdd3b 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/squares.png and b/plugins/obs-transitions/data/luma_wipes/squares.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/stripes.png b/plugins/obs-transitions/data/luma_wipes/stripes.png index 84e90e8..80dfd1f 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/stripes.png and b/plugins/obs-transitions/data/luma_wipes/stripes.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/strips-h.png b/plugins/obs-transitions/data/luma_wipes/strips-h.png index 1b44739..e1fab45 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/strips-h.png and b/plugins/obs-transitions/data/luma_wipes/strips-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/strips-v.png b/plugins/obs-transitions/data/luma_wipes/strips-v.png index e4dc434..4afabff 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/strips-v.png and b/plugins/obs-transitions/data/luma_wipes/strips-v.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/watercolor.png b/plugins/obs-transitions/data/luma_wipes/watercolor.png index 571b979..403b772 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/watercolor.png and b/plugins/obs-transitions/data/luma_wipes/watercolor.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/zigzag-h.png b/plugins/obs-transitions/data/luma_wipes/zigzag-h.png index c042d49..4148615 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/zigzag-h.png and b/plugins/obs-transitions/data/luma_wipes/zigzag-h.png differ diff --git a/plugins/obs-transitions/data/luma_wipes/zigzag-v.png b/plugins/obs-transitions/data/luma_wipes/zigzag-v.png index 2f4380b..82e0bc3 100644 Binary files a/plugins/obs-transitions/data/luma_wipes/zigzag-v.png and b/plugins/obs-transitions/data/luma_wipes/zigzag-v.png differ diff --git a/plugins/obs-x264/data/locale/bn-BD.ini b/plugins/obs-x264/data/locale/bn-BD.ini new file mode 100644 index 0000000..fcf3ccd --- /dev/null +++ b/plugins/obs-x264/data/locale/bn-BD.ini @@ -0,0 +1,9 @@ +Bitrate="বিটরেট" +CustomBufsize="ব্যবহার স্বনির্বাচিত বাফারের আকার" +RateControl="হার নিয়ন্ত্রণ" +CRF="CRF" +KeyframeIntervalSec="Keyframe বিরতি (সেকেন্ড, 0 অটো =)" +Tune="টিউন" +None="(একটিও না)" +EncoderOptions="x264 বিকল্প (স্পেইস দিয়ে)" + diff --git a/plugins/obs-x264/data/locale/et-EE.ini b/plugins/obs-x264/data/locale/et-EE.ini index 84fda6f..f066092 100644 --- a/plugins/obs-x264/data/locale/et-EE.ini +++ b/plugins/obs-x264/data/locale/et-EE.ini @@ -1,7 +1,12 @@ Bitrate="Bitikiirus" CustomBufsize="Kasuta kohandatud puhvri suurust" BufferSize="Puhvri suurus" +CRF="CRF" +KeyframeIntervalSec="Võtmekaadri intervall (sekundit, 0=automaatne)" +CPUPreset="CPU kasutus eelseadistus(kõrgem = vähem CPU)" Profile="Profiil" +Tune="Häälestus" None="(Määramata)" EncoderOptions="x264 suvandid (eraldatud tühikutega)" +VFR="Muutuv kaadrisagedus (VFR)" diff --git a/plugins/obs-x264/data/locale/hi-IN.ini b/plugins/obs-x264/data/locale/hi-IN.ini new file mode 100644 index 0000000..738582a --- /dev/null +++ b/plugins/obs-x264/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +Bitrate="बिटदर" + diff --git a/plugins/obs-x264/data/locale/ur-PK.ini b/plugins/obs-x264/data/locale/ur-PK.ini new file mode 100644 index 0000000..9c4689f --- /dev/null +++ b/plugins/obs-x264/data/locale/ur-PK.ini @@ -0,0 +1,2 @@ +Bitrate="بٹ شرح" + diff --git a/plugins/rtmp-services/data/locale/bn-BD.ini b/plugins/rtmp-services/data/locale/bn-BD.ini new file mode 100644 index 0000000..c865d49 --- /dev/null +++ b/plugins/rtmp-services/data/locale/bn-BD.ini @@ -0,0 +1,6 @@ +StreamingServices="স্ট্রিমিং সেবা" +CustomStreamingServer="কাস্টম স্ট্রিমিং সার্ভার" +StreamKey="স্ত্রিম্ চাবি" +UseAuth="প্রমাণীকরণ ব্যবহার করুন" +ShowAll="সকল সেবা প্রদর্শন করা হবে" + diff --git a/plugins/rtmp-services/data/locale/hi-IN.ini b/plugins/rtmp-services/data/locale/hi-IN.ini new file mode 100644 index 0000000..48e2b86 --- /dev/null +++ b/plugins/rtmp-services/data/locale/hi-IN.ini @@ -0,0 +1,2 @@ +StreamingServices="स्ट्रीमिंग सेवाए" + diff --git a/plugins/rtmp-services/data/locale/ur-PK.ini b/plugins/rtmp-services/data/locale/ur-PK.ini new file mode 100644 index 0000000..f1d2ecb --- /dev/null +++ b/plugins/rtmp-services/data/locale/ur-PK.ini @@ -0,0 +1,2 @@ +StreamingServices="محرومی کی خدمات" + diff --git a/plugins/rtmp-services/data/locale/zh-CN.ini b/plugins/rtmp-services/data/locale/zh-CN.ini index 8c71e21..c2db436 100644 --- a/plugins/rtmp-services/data/locale/zh-CN.ini +++ b/plugins/rtmp-services/data/locale/zh-CN.ini @@ -2,7 +2,7 @@ StreamingServices="流媒体服务" CustomStreamingServer="自定义流媒体服务器" Service="服务" Server="服务器" -StreamKey="流密钥" +StreamKey="流名称" UseAuth="使用身份验证" Username="用户名" Password="密码" diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index a77c4d7..38890e9 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 52, + "version": 60, "files": [ { "name": "services.json", - "version": 52 + "version": 60 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 50b6088..69ce728 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -49,6 +49,10 @@ "name": "EU: London, UK", "url": "rtmp://live-lhr.twitch.tv/app" }, + { + "name": "EU: Madrid, Spain", + "url": "rtmp://live-mad.twitch.tv/app" + }, { "name": "EU: Milan, Italy", "url": "rtmp://live-mil.twitch.tv/app" @@ -65,6 +69,10 @@ "name": "EU: Stockholm, SE", "url": "rtmp://live-arn.twitch.tv/app" }, + { + "name": "EU: Vienna, Austria", + "url": "rtmp://live-vie.twitch.tv/app" + }, { "name": "EU: Warsaw, Poland", "url": "rtmp://live-waw.twitch.tv/app" @@ -95,20 +103,32 @@ }, { "name": "South America: Rio de Janeiro, Brazil", - "url": "rtmp://live-gig.twitch.tv/app" + "url": "rtmp://live-rio.twitch.tv/app" }, { "name": "South America: Sao Paulo, Brazil", - "url": "rtmp://live-gru.twitch.tv/app" + "url": "rtmp://live-sao.twitch.tv/app" }, { "name": "US Central: Dallas, TX", "url": "rtmp://live-dfw.twitch.tv/app" }, + { + "name": "US Central: Denver, CO", + "url": "rtmp://live-den.twitch.tv/app" + }, + { + "name": "US Central: Houston, TX", + "url": "rtmp://live-hou.twitch.tv/app" + }, { "name": "US East: Ashburn, VA", "url": "rtmp://live-iad.twitch.tv/app" }, + { + "name": "US East: Atlanta, GA", + "url": "rtmp://live-atl.twitch.tv/app" + }, { "name": "US East: Chicago", "url": "rtmp://live-ord.twitch.tv/app" @@ -125,6 +145,10 @@ "name": "US West: Los Angeles, CA", "url": "rtmp://live-lax.twitch.tv/app" }, + { + "name": "US West: Phoenix, AZ", + "url": "rtmp://live-phx.twitch.tv/app" + }, { "name": "US West: San Jose, CA", "url": "rtmp://live-sjc.twitch.tv/app" @@ -136,7 +160,7 @@ ], "recommended": { "keyint": 2, - "max video bitrate": 3500, + "max video bitrate": 6000, "max audio bitrate": 160, "x264opts": "scenecut=0" } @@ -161,7 +185,7 @@ } }, { - "name": "hitbox.tv", + "name": "Smashcast", "common": true, "servers": [ { @@ -346,27 +370,6 @@ } ] }, - { - "name": "Livecoding.tv", - "common": true, - "servers": [ - { - "name": "United States", - "url": "rtmp://usmedia3.livecoding.tv/livecodingtv" - }, - { - "name": "EU", - "url": "rtmp://eumedia1.livecoding.tv/livecodingtv" - }, - { - "name": "Asia Pacific", - "url": "rtmp://apmedia1.livecoding.tv/livecodingtv" - } - ], - "recommended": { - "max video bitrate": 2300 - } - }, { "name": "WatchPeopleCode.com", "servers": [ @@ -514,15 +517,6 @@ "max audio bitrate": 160 } }, - { - "name": "connectcast.tv", - "servers": [ - { - "name": "Default", - "url": "rtmp://stream.connectcast.tv/live" - } - ] - }, { "name": "CyberGame.TV", "servers": [ @@ -536,19 +530,6 @@ } ] }, - { - "name": "CashPlay.tv", - "servers": [ - { - "name": "Primary, UK", - "url": "rtmp://live.cashplay.tv/live" - }, - { - "name": "Low Priority, DE", - "url": "rtmp://de.live.cashplay.tv/live" - } - ] - }, { "name": "DJlive.pl", "servers": [ @@ -612,10 +593,6 @@ "name": "US-West (San Jose, CA)", "url": "rtmp://us-west.restream.io/live" }, - { - "name": "US-West (Los Angeles, CA)", - "url": "rtmp://us-la.restream.io/live" - }, { "name": "US-Central (Dallas, TX)", "url": "rtmp://us-central.restream.io/live" @@ -636,6 +613,10 @@ "name": "Asia (Singapore)", "url": "rtmp://singapore.restream.io/live" }, + { + "name": "Asia (Seoul, South Korea)", + "url": "rtmp://seoul.restream.io/live" + }, { "name": "Australia (Sydney)", "url": "rtmp://au.restream.io/live" @@ -694,11 +675,11 @@ ] }, { - "name": "Stre.am", + "name": "Stream.live", "servers": [ { "name": "Default", - "url": "rtmp://media.stre.am:1935/live" + "url": "rtmp://media.stream.live:1935/live" } ], "recommended": { @@ -838,8 +819,8 @@ "recommended": { "keyint": 2, "profile": "main", - "max video bitrate": 2000, - "max audio bitrate": 160 + "max video bitrate": 3500, + "max audio bitrate": 128 } }, { @@ -850,6 +831,15 @@ "url": "rtmp://plive.pandora.tv:80/mediaHub" } ] + }, + { + "name": "LiveStream", + "servers": [ + { + "name": "Primary", + "url": "rtmp://rtmpin.livestreamingest.com/rtmpin" + } + ] } ] } diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index a15790c..5194345 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -111,6 +111,8 @@ static void add_service(obs_property_t *list, json_t *service, bool show_all, obs_property_list_add_string(list, name, name); } +static inline json_t *find_service(json_t *root, const char *name); + static void add_services(obs_property_t *list, json_t *root, bool show_all, const char *cur_service) { @@ -126,6 +128,13 @@ static void add_services(obs_property_t *list, json_t *root, bool show_all, json_array_foreach (root, index, service) { add_service(list, service, show_all, cur_service); } + + service = find_service(root, cur_service); + if (!service && cur_service && *cur_service) { + obs_property_list_insert_string(list, 0, cur_service, + cur_service); + obs_property_list_item_disable(list, 0, true); + } } static json_t *open_json_file(const char *file) @@ -263,12 +272,20 @@ static bool service_selected(obs_properties_t *props, obs_property_t *p, return false; service = find_service(root, name); - if (!service) - return false; + if (!service) { + const char *server = obs_data_get_string(settings, "server"); + + obs_property_list_insert_string(p, 0, name, name); + obs_property_list_item_disable(p, 0, true); + + p = obs_properties_get(props, "server"); + obs_property_list_insert_string(p, 0, server, server); + obs_property_list_item_disable(p, 0, true); + return true; + } fill_servers(obs_properties_get(props, "server"), service, name); - UNUSED_PARAMETER(p); return true; } diff --git a/plugins/text-freetype2/data/locale/bn-BD.ini b/plugins/text-freetype2/data/locale/bn-BD.ini new file mode 100644 index 0000000..53e2ec6 --- /dev/null +++ b/plugins/text-freetype2/data/locale/bn-BD.ini @@ -0,0 +1,8 @@ +TextFreetype2="টেক্সট (FreeType 2)" +Font="ফন্ট" +TextFileFilter="টেক্সট ফাইল (*.txt);;" +ChatLogMode="আড্ডার লগ মোড (6 শেষ লাইন)" +Outline="রূপরেখা" +DropShadow="ছায়া ফেলে" +ReadFromFile="ফাইল থেকে পড়া" + diff --git a/plugins/text-freetype2/find-font-cocoa.m b/plugins/text-freetype2/find-font-cocoa.m index 4e4f55b..9583cb6 100644 --- a/plugins/text-freetype2/find-font-cocoa.m +++ b/plugins/text-freetype2/find-font-cocoa.m @@ -32,7 +32,16 @@ static void add_path_fonts(NSFileManager *file_manager, NSString *path) for (NSString *file in files) { NSString *full_path = [path stringByAppendingPathComponent:file]; - add_path_font(full_path.fileSystemRepresentation); + BOOL is_dir = FALSE; + bool folder_exists = [file_manager + fileExistsAtPath:full_path + isDirectory:&is_dir]; + + if (folder_exists && is_dir) { + add_path_fonts(file_manager, full_path); + } else { + add_path_font(full_path.fileSystemRepresentation); + } } } diff --git a/plugins/vlc-video/data/locale/bn-BD.ini b/plugins/vlc-video/data/locale/bn-BD.ini new file mode 100644 index 0000000..5646279 --- /dev/null +++ b/plugins/vlc-video/data/locale/bn-BD.ini @@ -0,0 +1,4 @@ +VLCSource="VLC ভিডিও উৎস" +PlaybackBehavior="দৃশ্যমানতা আচরণ" +PlaybackBehavior.StopRestart="পুনরায় শুরু করা না দেখা যায়, যখন দেখা যায় যখন বন্ধ" + diff --git a/plugins/vlc-video/data/locale/ca-ES.ini b/plugins/vlc-video/data/locale/ca-ES.ini index fbdf5fb..8722612 100644 --- a/plugins/vlc-video/data/locale/ca-ES.ini +++ b/plugins/vlc-video/data/locale/ca-ES.ini @@ -1,6 +1,7 @@ VLCSource="Origen de vídeo VLC" Playlist="Llista de reproducció" LoopPlaylist="Repetir la llista de reproducció" +Shuffle="Barreja la llista de reproducció" PlaybackBehavior="Comportament de la visibilitat" PlaybackBehavior.StopRestart="Aturar quan no sigui visible, reiniciar quan sigui visible" PlaybackBehavior.PauseUnpause="Pausa quan no sigui visible, reprendre quan sigui visible" diff --git a/plugins/vlc-video/data/locale/cs-CZ.ini b/plugins/vlc-video/data/locale/cs-CZ.ini index 97581e7..f3e69c0 100644 --- a/plugins/vlc-video/data/locale/cs-CZ.ini +++ b/plugins/vlc-video/data/locale/cs-CZ.ini @@ -1,6 +1,7 @@ VLCSource="Video zdroj VLC" Playlist="Seznam skladeb" LoopPlaylist="Opakovat seznam skladeb" +Shuffle="Náhodné řazení playlistu" PlaybackBehavior="Závislost na viditelnosti" PlaybackBehavior.StopRestart="Zastavit při skrytém, restartovat při obnovení" PlaybackBehavior.PauseUnpause="Pozastavit při skrytém, pokračovat při obnovení" diff --git a/plugins/vlc-video/data/locale/da-DK.ini b/plugins/vlc-video/data/locale/da-DK.ini index 89ebb8e..dfa30a2 100644 --- a/plugins/vlc-video/data/locale/da-DK.ini +++ b/plugins/vlc-video/data/locale/da-DK.ini @@ -1,6 +1,7 @@ VLCSource="VLC videokilde" Playlist="Afspilningsliste" LoopPlaylist="Loop afspilningsliste" +Shuffle="Bland playliste" PlaybackBehavior="Synligheds opførsel" PlaybackBehavior.StopRestart="Stop når ikke synlig, genstart når synlig" PlaybackBehavior.PauseUnpause="Paus når ikke synlig, genoptag når synlig" diff --git a/plugins/vlc-video/data/locale/de-DE.ini b/plugins/vlc-video/data/locale/de-DE.ini index dfa0cf2..51bf8c4 100644 --- a/plugins/vlc-video/data/locale/de-DE.ini +++ b/plugins/vlc-video/data/locale/de-DE.ini @@ -1,6 +1,7 @@ VLCSource="VLC Videoquelle" Playlist="Wiedergabeliste" LoopPlaylist="Wiedergabeliste wiederholen" +Shuffle="Wiedergabeliste zufällig wiedergeben" PlaybackBehavior="Sichtbarkeitsverhalten" PlaybackBehavior.StopRestart="Anhalten wenn nicht sichtbar, neustarten wenn sichtbar" PlaybackBehavior.PauseUnpause="Pausieren wenn nicht sichtbar, fortsetzen wenn sichtbar" diff --git a/plugins/vlc-video/data/locale/en-US.ini b/plugins/vlc-video/data/locale/en-US.ini index cd5a768..5846fe4 100644 --- a/plugins/vlc-video/data/locale/en-US.ini +++ b/plugins/vlc-video/data/locale/en-US.ini @@ -1,6 +1,7 @@ VLCSource="VLC Video Source" Playlist="Playlist" LoopPlaylist="Loop Playlist" +Shuffle="Shuffle Playlist" PlaybackBehavior="Visibility behavior" PlaybackBehavior.StopRestart="Stop when not visible, restart when visible" PlaybackBehavior.PauseUnpause="Pause when not visible, unpause when visible" diff --git a/plugins/vlc-video/data/locale/es-ES.ini b/plugins/vlc-video/data/locale/es-ES.ini index 1ec000d..7449a94 100644 --- a/plugins/vlc-video/data/locale/es-ES.ini +++ b/plugins/vlc-video/data/locale/es-ES.ini @@ -1,6 +1,7 @@ VLCSource="Fuente de vídeo VLC" Playlist="Lista de reproducción" LoopPlaylist="Repetir lista de reproducción" +Shuffle="Mezclar lista de reproducción" PlaybackBehavior="Comportamiento de la visibilidad" PlaybackBehavior.StopRestart="Detener cuando no sea visible, reiniciar cuando sea visible" PlaybackBehavior.PauseUnpause="Pausar cuando no sea visible, reanudar cuando sea visible" diff --git a/plugins/vlc-video/data/locale/eu-ES.ini b/plugins/vlc-video/data/locale/eu-ES.ini index 445885b..0e73630 100644 --- a/plugins/vlc-video/data/locale/eu-ES.ini +++ b/plugins/vlc-video/data/locale/eu-ES.ini @@ -1,6 +1,7 @@ VLCSource="VLC bideo-iturburua" Playlist="Erreprodukzio-zerrenda" LoopPlaylist="Errepikatu erreprodukzio-zerrenda" +Shuffle="Nahasi erreprodukzio-zerrenda" PlaybackBehavior="Ikuste-jokabidea" PlaybackBehavior.StopRestart="Ikusten ez bada gelditu, ikusten denean berrabiarazi" PlaybackBehavior.PauseUnpause="Ikusten ez bada pausatu, ikusten denean jarraitu" diff --git a/plugins/vlc-video/data/locale/fi-FI.ini b/plugins/vlc-video/data/locale/fi-FI.ini index 4760d2d..b54bdcf 100644 --- a/plugins/vlc-video/data/locale/fi-FI.ini +++ b/plugins/vlc-video/data/locale/fi-FI.ini @@ -1,6 +1,7 @@ VLCSource="VLC Video-lähde" Playlist="Soittolista" LoopPlaylist="Toista soittolistaa jatkuvasti" +Shuffle="Sekoita soittolista" PlaybackBehavior="Näkyvyyden käyttäytyminen" PlaybackBehavior.StopRestart="Pysäytä toisto kun lähde ei näy. Käynnistä toisto uudelleen, kun se on taas näkyvissä" PlaybackBehavior.PauseUnpause="Keskeytä toisto kun lähde ei näy. Jatka toistoa, kun se on taas näkyvissä" diff --git a/plugins/vlc-video/data/locale/fr-FR.ini b/plugins/vlc-video/data/locale/fr-FR.ini index 12b0628..ca097ce 100644 --- a/plugins/vlc-video/data/locale/fr-FR.ini +++ b/plugins/vlc-video/data/locale/fr-FR.ini @@ -1,6 +1,7 @@ VLCSource="Source vidéo VLC" Playlist="Liste de lecture" LoopPlaylist="Répéter la liste de lecture" +Shuffle="Playlist Hazard" PlaybackBehavior="Comportement de visibilité" PlaybackBehavior.StopRestart="Arrêter quand elle n'est pas visible, redémarrer lorsqu'elle est visible" PlaybackBehavior.PauseUnpause="Suspendre lorsqu'elle n'est pas visible, reprendre lorsqu'elle est visible" diff --git a/plugins/vlc-video/data/locale/hu-HU.ini b/plugins/vlc-video/data/locale/hu-HU.ini index 8a4e627..cb96564 100644 --- a/plugins/vlc-video/data/locale/hu-HU.ini +++ b/plugins/vlc-video/data/locale/hu-HU.ini @@ -1,6 +1,7 @@ VLCSource="VLC Videoforrás" Playlist="Lejátszási lista" LoopPlaylist="Ismétlődő lejátszási lista" +Shuffle="Véletlenszerű lejátszási lista" PlaybackBehavior="Láthatósági opció" PlaybackBehavior.StopRestart="Leállítás, ha nem látható, újraindul, ha látható" PlaybackBehavior.PauseUnpause="Szüneteltet, ha nem látható, folytatás, ha látható" diff --git a/plugins/vlc-video/data/locale/ja-JP.ini b/plugins/vlc-video/data/locale/ja-JP.ini index c5677a5..ef8ac5b 100644 --- a/plugins/vlc-video/data/locale/ja-JP.ini +++ b/plugins/vlc-video/data/locale/ja-JP.ini @@ -1,6 +1,7 @@ VLCSource="VLC ビデオソース" Playlist="プレイリスト" LoopPlaylist="プレイリストをループ再生" +Shuffle="プレイリストをシャッフルする" PlaybackBehavior="表示の動作" PlaybackBehavior.StopRestart="表示されていないときに停止、表示時に再開" PlaybackBehavior.PauseUnpause="表示されていないときに一時停止、表示時に一時停止を解除" diff --git a/plugins/vlc-video/data/locale/ko-KR.ini b/plugins/vlc-video/data/locale/ko-KR.ini index b0e4df7..df7838f 100644 --- a/plugins/vlc-video/data/locale/ko-KR.ini +++ b/plugins/vlc-video/data/locale/ko-KR.ini @@ -1,6 +1,7 @@ VLCSource="VLC 비디오 소스" Playlist="재생목록" LoopPlaylist="반복 재생" +Shuffle="재생목록 섞기" PlaybackBehavior="표시 동작 설정" PlaybackBehavior.StopRestart="보이지 않을 때 중단, 보이면 재시작" PlaybackBehavior.PauseUnpause="보이지 않을 때 일시 중지, 보이면 일시 중지 해제" diff --git a/plugins/vlc-video/data/locale/nl-NL.ini b/plugins/vlc-video/data/locale/nl-NL.ini index b1669a7..284f1d3 100644 --- a/plugins/vlc-video/data/locale/nl-NL.ini +++ b/plugins/vlc-video/data/locale/nl-NL.ini @@ -1,6 +1,7 @@ VLCSource="VLC Videobron" Playlist="Playlist" LoopPlaylist="Playlist herhalen" +Shuffle="Hussel afspeellijst" PlaybackBehavior="Zichtbaarheidsgedrag" PlaybackBehavior.StopRestart="Stop wanneer niet zichtbaar, herstart wanneer zichtbaar" PlaybackBehavior.PauseUnpause="Pauzeer wanneer niet zichtbaar, hervat wanneer zichtbaar" diff --git a/plugins/vlc-video/data/locale/pl-PL.ini b/plugins/vlc-video/data/locale/pl-PL.ini index cc74c42..404e825 100644 --- a/plugins/vlc-video/data/locale/pl-PL.ini +++ b/plugins/vlc-video/data/locale/pl-PL.ini @@ -1,6 +1,7 @@ VLCSource="Odtwarzacz VLC" Playlist="Lista odtwarzania" LoopPlaylist="Powtarzaj listę odtwarzania" +Shuffle="Przetasuj listę odtwarzania" PlaybackBehavior="Zachowanie" PlaybackBehavior.StopRestart="Zatrzymaj, gdy niewidoczne. Odtwarzaj od początku, gdy widoczne." PlaybackBehavior.PauseUnpause="Wstrzymaj, gdy niewidoczne. Wznów, gdy widoczne." diff --git a/plugins/vlc-video/data/locale/pt-BR.ini b/plugins/vlc-video/data/locale/pt-BR.ini index 3c72768..d93756d 100644 --- a/plugins/vlc-video/data/locale/pt-BR.ini +++ b/plugins/vlc-video/data/locale/pt-BR.ini @@ -1,6 +1,7 @@ VLCSource="Fonte de vídeo do VLC" Playlist="Lista de reprodução" LoopPlaylist="Repetir a Lista de reprodução" +Shuffle="Misturar a Lista" PlaybackBehavior="Comportamento de visibilidade" PlaybackBehavior.StopRestart="Parar quando não visível, reiniciar quando visível" PlaybackBehavior.PauseUnpause="Pausa quando não visível, resumir quando visível" diff --git a/plugins/vlc-video/data/locale/ru-RU.ini b/plugins/vlc-video/data/locale/ru-RU.ini index b7085c6..588153a 100644 --- a/plugins/vlc-video/data/locale/ru-RU.ini +++ b/plugins/vlc-video/data/locale/ru-RU.ini @@ -1,6 +1,7 @@ VLCSource="Источник VLC видео" Playlist="Плейлист" LoopPlaylist="Циклическое воспроизведение" +Shuffle="Перемешать плейлист" PlaybackBehavior="Поведение видимости" PlaybackBehavior.StopRestart="Остановить, когда не видно, перезагрузить, когда видно" PlaybackBehavior.PauseUnpause="Пауза, когда не видно, возобновить, когда видно" diff --git a/plugins/vlc-video/data/locale/sv-SE.ini b/plugins/vlc-video/data/locale/sv-SE.ini index 9581e6b..d3b333c 100644 --- a/plugins/vlc-video/data/locale/sv-SE.ini +++ b/plugins/vlc-video/data/locale/sv-SE.ini @@ -1,6 +1,7 @@ VLCSource="VLC-videokälla" Playlist="Spellista" LoopPlaylist="Slinga spellista" +Shuffle="Slumpa spellista" PlaybackBehavior="Synlighetsbeteende" PlaybackBehavior.StopRestart="Stoppa när den inte syns, starta om när den syns" PlaybackBehavior.PauseUnpause="Pausa när den inte syns, återuppta när den syns" diff --git a/plugins/vlc-video/data/locale/tr-TR.ini b/plugins/vlc-video/data/locale/tr-TR.ini index b4c35b7..b72d8b3 100644 --- a/plugins/vlc-video/data/locale/tr-TR.ini +++ b/plugins/vlc-video/data/locale/tr-TR.ini @@ -1,6 +1,7 @@ VLCSource="VLC Video Kaynağı" Playlist="Oynatma Listesi" LoopPlaylist="Oynatma Listesini Yinele" +Shuffle="Oynatma Listesini Karıştır" PlaybackBehavior="Görünürlük davranışı" PlaybackBehavior.StopRestart="Görünür değilken durdur, görünür olunca yeniden başlat" PlaybackBehavior.PauseUnpause="Görünür değilken duraklat, görünür olunca oynat" diff --git a/plugins/vlc-video/data/locale/uk-UA.ini b/plugins/vlc-video/data/locale/uk-UA.ini index d0eb06c..7bbca4e 100644 --- a/plugins/vlc-video/data/locale/uk-UA.ini +++ b/plugins/vlc-video/data/locale/uk-UA.ini @@ -1,6 +1,7 @@ VLCSource="VLC-відео" Playlist="Список відтворення" LoopPlaylist="Повторювати список відтворювання" +Shuffle="Перемішати список відтворення" PlaybackBehavior="Видимість та відтворення" PlaybackBehavior.StopRestart="Зупинити, коли не видимий. Грати з початку, коли видимий" PlaybackBehavior.PauseUnpause="Пизупинити, коли не видимий. Грати далі, коли видимий" diff --git a/plugins/vlc-video/data/locale/zh-CN.ini b/plugins/vlc-video/data/locale/zh-CN.ini index 307572c..dccca5f 100644 --- a/plugins/vlc-video/data/locale/zh-CN.ini +++ b/plugins/vlc-video/data/locale/zh-CN.ini @@ -1,6 +1,7 @@ VLCSource="VLC 视频源" Playlist="播放列表" LoopPlaylist="循环播放列表" +Shuffle="随机播放列表" PlaybackBehavior="可见性的行为" PlaybackBehavior.StopRestart="不可见时停止, 可见时重启" PlaybackBehavior.PauseUnpause="不可见时暂停, 可见时取消暂停" diff --git a/plugins/vlc-video/data/locale/zh-TW.ini b/plugins/vlc-video/data/locale/zh-TW.ini index c8c005e..bd8ea40 100644 --- a/plugins/vlc-video/data/locale/zh-TW.ini +++ b/plugins/vlc-video/data/locale/zh-TW.ini @@ -1,6 +1,7 @@ VLCSource="VLC視訊來源" Playlist="播放清單" LoopPlaylist="循環播放清單" +Shuffle="隨機播放" PlaybackBehavior="播放行為" PlaybackBehavior.StopRestart="不可見時停止,可見時重新開始" PlaybackBehavior.PauseUnpause="不可見時暫停,可見時取消暫停" diff --git a/plugins/vlc-video/vlc-video-source.c b/plugins/vlc-video/vlc-video-source.c index bac5de3..2fe8945 100644 --- a/plugins/vlc-video/vlc-video-source.c +++ b/plugins/vlc-video/vlc-video-source.c @@ -12,6 +12,7 @@ #define S_PLAYLIST "playlist" #define S_LOOP "loop" +#define S_SHUFFLE "shuffle" #define S_BEHAVIOR "playback_behavior" #define S_BEHAVIOR_STOP_RESTART "stop_restart" #define S_BEHAVIOR_PAUSE_UNPAUSE "pause_unpause" @@ -20,6 +21,7 @@ #define T_(text) obs_module_text(text) #define T_PLAYLIST T_("Playlist") #define T_LOOP T_("LoopPlaylist") +#define T_SHUFFLE T_("shuffle") #define T_BEHAVIOR T_("PlaybackBehavior") #define T_BEHAVIOR_STOP_RESTART T_("PlaybackBehavior.StopRestart") #define T_BEHAVIOR_PAUSE_UNPAUSE T_("PlaybackBehavior.PauseUnpause") @@ -52,6 +54,7 @@ struct vlc_source { DARRAY(struct media_file_data) files; enum behavior behavior; bool loop; + bool shuffle; }; static libvlc_media_t *get_media(struct darray *array, const char *path) @@ -542,6 +545,22 @@ static void vlcs_update(void *data, obs_data_t *settings) c->files.da = new_files.da; pthread_mutex_unlock(&c->mutex); + /* ------------------------------------- */ + /* shuffle playlist */ + + c->shuffle = obs_data_get_bool(settings, S_SHUFFLE); + + if (c->files.num > 1 && c->shuffle) { + for (size_t i = 0; i < c->files.num - 1; i++) { + size_t j = i + rand() / (RAND_MAX + / (c->files.num - i) + 1); + + struct media_file_data t = c->files.array[j]; + c->files.array[j] = c->files.array[i]; + c->files.array[i] = t; + } + } + /* ------------------------------------- */ /* clean up and restart playback */ @@ -658,6 +677,7 @@ static void vlcs_deactivate(void *data) static void vlcs_defaults(obs_data_t *settings) { obs_data_set_default_bool(settings, S_LOOP, true); + obs_data_set_default_bool(settings, S_SHUFFLE, false); obs_data_set_default_string(settings, S_BEHAVIOR, S_BEHAVIOR_STOP_RESTART); } @@ -672,6 +692,7 @@ static obs_properties_t *vlcs_properties(void *data) obs_property_t *p; obs_properties_add_bool(ppts, S_LOOP, T_LOOP); + obs_properties_add_bool(ppts, S_SHUFFLE, T_SHUFFLE); if (c) { pthread_mutex_lock(&c->mutex);