From b14f9eae6d3b7227c83de7ab71669b8370eaf4de Mon Sep 17 00:00:00 2001 From: Simon Chopin Date: Sat, 27 Jul 2019 14:47:10 +0200 Subject: [PATCH 1/4] New upstream version 23.2.1+dfsg1 --- .github/FUNDING.yml | 2 + .gitignore | 1 + .gitmodules | 2 +- .travis.yml | 15 +- AUTHORS | 1230 +++++++--- CI/before-deploy-osx.sh | 34 +- CI/before-deploy-win.cmd | 6 +- CI/before-script-osx.sh | 4 +- CI/install-dependencies-linux-ubuntu16.sh | 59 + CI/install-dependencies-linux.sh | 20 +- CI/install-dependencies-osx.sh | 15 +- CI/install-qt-win.cmd | 4 + CI/install-script-linux.sh | 4 + CI/install-script-win.cmd | 29 + CMakeLists.txt | 40 +- COMMITMENT | 46 + CONTRIBUTING.rst | 15 +- README.rst | 28 +- UI/CMakeLists.txt | 134 +- UI/adv-audio-control.cpp | 109 +- UI/adv-audio-control.hpp | 14 +- UI/api-interface.cpp | 36 +- UI/audio-encoders.hpp | 2 + UI/auth-base.cpp | 71 + UI/auth-base.hpp | 58 + UI/auth-mixer.cpp | 336 +++ UI/auth-mixer.hpp | 30 + UI/auth-oauth.cpp | 311 +++ UI/auth-oauth.hpp | 78 + UI/auth-restream.cpp | 283 +++ UI/auth-restream.hpp | 29 + UI/auth-twitch.cpp | 483 ++++ UI/auth-twitch.hpp | 48 + UI/balance-slider.hpp | 21 + UI/clickable-label.hpp | 21 + UI/combobox-ignorewheel.cpp | 14 + UI/combobox-ignorewheel.hpp | 15 + UI/data/images/overflow.png | Bin 0 -> 386 bytes UI/data/locale.ini | 9 + UI/data/locale/af-ZA.ini | 4 + UI/data/locale/ar-SA.ini | 244 +- UI/data/locale/bg-BG.ini | 205 +- UI/data/locale/bn-BD.ini | 170 +- UI/data/locale/ca-ES.ini | 233 +- UI/data/locale/cs-CZ.ini | 159 +- UI/data/locale/da-DK.ini | 743 +++--- UI/data/locale/de-DE.ini | 503 ++-- UI/data/locale/el-GR.ini | 457 ++-- UI/data/locale/en-US.ini | 201 +- UI/data/locale/es-ES.ini | 291 ++- UI/data/locale/et-EE.ini | 78 +- UI/data/locale/eu-ES.ini | 279 ++- UI/data/locale/fa-IR.ini | 114 +- UI/data/locale/fi-FI.ini | 232 +- UI/data/locale/fil-PH.ini | 75 +- UI/data/locale/fr-FR.ini | 381 +-- UI/data/locale/gd-GB.ini | 155 +- UI/data/locale/gl-ES.ini | 83 +- UI/data/locale/he-IL.ini | 141 +- UI/data/locale/hi-IN.ini | 10 + UI/data/locale/hr-HR.ini | 116 +- UI/data/locale/hu-HU.ini | 221 +- UI/data/locale/it-IT.ini | 878 ++++--- UI/data/locale/ja-JP.ini | 183 +- UI/data/locale/ka-GE.ini | 392 +-- UI/data/locale/ko-KR.ini | 149 +- UI/data/locale/ku-TR.ini | 190 ++ UI/data/locale/lt-LT.ini | 59 +- UI/data/locale/mn-MN.ini | 335 +++ UI/data/locale/ms-MY.ini | 57 +- UI/data/locale/nb-NO.ini | 242 +- UI/data/locale/nl-NL.ini | 153 +- UI/data/locale/nn-NO.ini | 29 +- UI/data/locale/pa-IN.ini | 118 + UI/data/locale/pl-PL.ini | 211 +- UI/data/locale/pt-BR.ini | 339 ++- UI/data/locale/pt-PT.ini | 218 +- UI/data/locale/ro-RO.ini | 436 ++-- UI/data/locale/ru-RU.ini | 230 +- UI/data/locale/sk-SK.ini | 209 +- UI/data/locale/sl-SI.ini | 93 +- UI/data/locale/sq-AL.ini | 47 + UI/data/locale/sr-CS.ini | 388 ++- UI/data/locale/sr-SP.ini | 405 +++- UI/data/locale/sv-SE.ini | 287 ++- UI/data/locale/ta-IN.ini | 708 +++++- UI/data/locale/th-TH.ini | 124 +- UI/data/locale/tl-PH.ini | 94 +- UI/data/locale/tr-TR.ini | 256 +- UI/data/locale/uk-UA.ini | 248 +- UI/data/locale/ur-PK.ini | 38 + UI/data/locale/vi-VN.ini | 236 +- UI/data/locale/zh-CN.ini | 625 ++--- UI/data/locale/zh-TW.ini | 148 +- UI/data/themes/Acri.qss | 186 +- UI/data/themes/Acri/cogwheel.png | Bin 255 -> 0 bytes UI/data/themes/Acri/down_arrow.png | Bin 527 -> 0 bytes UI/data/themes/Acri/minus.png | Bin 110 -> 0 bytes UI/data/themes/Acri/mute.png | Bin 216 -> 0 bytes UI/data/themes/Acri/plus.png | Bin 115 -> 0 bytes UI/data/themes/Acri/unmute.png | Bin 289 -> 0 bytes UI/data/themes/Acri/up_arrow.png | Bin 505 -> 0 bytes UI/data/themes/Acri/updown.png | Bin 965 -> 0 bytes UI/data/themes/Dark.qss | 265 +- UI/data/themes/Dark/cogwheel.png | Bin 231 -> 0 bytes UI/data/themes/Dark/collapse.png | Bin 3746 -> 0 bytes UI/data/themes/Dark/down.svg | 1 + UI/data/themes/Dark/down_arrow.png | Bin 527 -> 0 bytes UI/data/themes/Dark/expand.png | Bin 3545 -> 0 bytes UI/data/themes/Dark/expand.svg | 1 + UI/data/themes/Dark/locked.svg | 6 + UI/data/themes/Dark/minus.png | Bin 110 -> 0 bytes UI/data/themes/Dark/minus.svg | 1 + UI/data/themes/Dark/mute.png | Bin 216 -> 0 bytes UI/data/themes/Dark/mute.svg | 1 + UI/data/themes/Dark/no_sources.svg | 18 + UI/data/themes/Dark/plus.png | Bin 115 -> 0 bytes UI/data/themes/Dark/plus.svg | 1 + UI/data/themes/Dark/refresh.png | Bin 3275 -> 0 bytes UI/data/themes/Dark/refresh.svg | 1 + UI/data/themes/Dark/revert.svg | 1 + UI/data/themes/Dark/settings/advanced.svg | 1 + UI/data/themes/Dark/settings/audio.svg | 1 + UI/data/themes/Dark/settings/general.svg | 1 + UI/data/themes/Dark/settings/hotkeys.svg | 1 + UI/data/themes/Dark/settings/output.svg | 5 + UI/data/themes/Dark/settings/stream.svg | 1 + UI/data/themes/Dark/settings/video.svg | 1 + UI/data/themes/Dark/trash.svg | 1 + UI/data/themes/Dark/unmute.png | Bin 268 -> 0 bytes UI/data/themes/Dark/up.svg | 1 + UI/data/themes/Dark/up_arrow.png | Bin 505 -> 0 bytes UI/data/themes/Dark/updown.png | Bin 965 -> 0 bytes UI/data/themes/Dark/updown.svg | 1 + UI/data/themes/Dark/visible.svg | 1 + UI/data/themes/Default.qss | 117 - UI/data/themes/Rachni.qss | 213 +- UI/data/themes/System.qss | 206 ++ UI/display-helpers.hpp | 8 + UI/double-slider.cpp | 2 +- UI/double-slider.hpp | 3 +- UI/expand-checkbox.hpp | 2 + UI/forms/AutoConfigStreamPage.ui | 613 +++-- UI/forms/OBSAbout.ui | 219 ++ UI/forms/OBSBasic.ui | 65 +- UI/forms/OBSBasicFilters.ui | 36 + UI/forms/OBSBasicSettings.ui | 2156 +++++++++++------ UI/forms/OBSLicenseAgreement.ui | 108 - UI/forms/OBSRemux.ui | 117 +- UI/forms/images/add.png | Bin 101 -> 0 bytes UI/forms/images/collapse.png | Bin 3442 -> 0 bytes UI/forms/images/configuration21_16.png | Bin 239 -> 0 bytes UI/forms/images/down.png | Bin 515 -> 0 bytes UI/forms/images/down.svg | 1 + UI/forms/images/editscene.png | Bin 511 -> 0 bytes UI/forms/images/expand.png | Bin 2962 -> 0 bytes UI/forms/images/expand.svg | 1 + UI/forms/images/invisible.svg | 1 + UI/forms/images/invisible_mask.png | Bin 265 -> 0 bytes UI/forms/images/list_remove.png | Bin 96 -> 0 bytes UI/forms/images/live.png | Bin 710 -> 0 bytes UI/forms/images/locked.svg | 6 + UI/forms/images/locked_mask.png | Bin 289 -> 0 bytes UI/forms/images/minus.svg | 1 + UI/forms/images/mute.png | Bin 215 -> 0 bytes UI/forms/images/mute.svg | 1 + UI/forms/images/no_sources.svg | 18 + UI/forms/images/plus.svg | 1 + UI/forms/images/properties.png | Bin 239 -> 0 bytes UI/forms/images/refresh.png | Bin 2712 -> 0 bytes UI/forms/images/refresh.svg | 1 + UI/forms/images/revert.svg | 1 + UI/forms/images/settings/advanced.png | Bin 1271 -> 0 bytes UI/forms/images/settings/advanced.svg | 1 + .../images/settings/applications-system-2.png | Bin 4616 -> 0 bytes UI/forms/images/settings/audio.svg | 1 + .../images/settings/decibel_audio_player.png | Bin 4613 -> 0 bytes UI/forms/images/settings/general.svg | 1 + UI/forms/images/settings/hotkeys.svg | 1 + .../images/settings/network-bluetooth.png | Bin 2358 -> 0 bytes UI/forms/images/settings/network.png | Bin 3738 -> 0 bytes UI/forms/images/settings/output.svg | 5 + ...preferences-desktop-keyboard-shortcuts.png | Bin 3523 -> 0 bytes .../settings/preferences-system-network-3.png | Bin 4887 -> 0 bytes UI/forms/images/settings/stream.svg | 1 + .../images/settings/system-settings-3.png | Bin 4173 -> 0 bytes UI/forms/images/settings/video-display-3.png | Bin 2456 -> 0 bytes UI/forms/images/settings/video.svg | 1 + UI/forms/images/sound.ico | Bin 1150 -> 0 bytes UI/forms/images/sound_muted.ico | Bin 1150 -> 0 bytes UI/forms/images/trash.svg | 1 + UI/forms/images/unlocked.svg | 6 + UI/forms/images/unlocked_mask.png | Bin 297 -> 0 bytes UI/forms/images/unmute.png | Bin 296 -> 0 bytes UI/forms/images/up.png | Bin 489 -> 0 bytes UI/forms/images/up.svg | 1 + UI/forms/images/visible.svg | 1 + UI/forms/images/visible_mask.png | Bin 180 -> 0 bytes UI/forms/obs.qrc | 46 +- UI/frontend-plugins/CMakeLists.txt | 1 + .../decklink-output-ui/CMakeLists.txt | 61 + .../decklink-output-ui/DecklinkOutputUI.cpp | 139 ++ .../decklink-output-ui/DecklinkOutputUI.h | 34 + .../decklink-output-ui/data/.keepme | 0 .../decklink-output-ui/decklink-ui-main.cpp | 286 +++ .../decklink-output-ui/decklink-ui-main.h | 8 + .../decklink-output-ui/forms/output.ui | 128 + .../frontend-tools/CMakeLists.txt | 6 + .../frontend-tools/auto-scene-switcher.cpp | 2 + .../frontend-tools/captions-mssapi-stream.cpp | 5 +- .../frontend-tools/captions.cpp | 2 + .../frontend-tools/data/locale/ar-SA.ini | 42 + .../frontend-tools/data/locale/bg-BG.ini | 35 + .../frontend-tools/data/locale/da-DK.ini | 20 +- .../frontend-tools/data/locale/de-DE.ini | 12 +- .../frontend-tools/data/locale/es-ES.ini | 12 +- .../frontend-tools/data/locale/fa-IR.ini | 25 + .../frontend-tools/data/locale/fr-FR.ini | 14 +- .../frontend-tools/data/locale/hr-HR.ini | 2 +- .../frontend-tools/data/locale/it-IT.ini | 22 +- .../frontend-tools/data/locale/ka-GE.ini | 2 +- .../frontend-tools/data/locale/mn-MN.ini | 32 + .../frontend-tools/data/locale/pl-PL.ini | 2 +- .../frontend-tools/data/locale/pt-PT.ini | 11 + .../frontend-tools/data/locale/ro-RO.ini | 29 +- .../frontend-tools/data/locale/ru-RU.ini | 6 +- .../frontend-tools/data/locale/sk-SK.ini | 13 + .../frontend-tools/data/locale/sr-CS.ini | 22 +- .../frontend-tools/data/locale/sr-SP.ini | 22 +- .../frontend-tools/data/locale/ta-IN.ini | 27 + .../frontend-tools/data/locale/th-TH.ini | 13 + .../frontend-tools/data/locale/zh-CN.ini | 2 +- .../data/scripts/instant-replay.lua | 48 +- .../frontend-tools/output-timer.cpp | 2 + .../frontend-tools/scripts.cpp | 17 + .../frontend-tools/scripts.hpp | 2 + UI/hotkey-edit.cpp | 25 +- UI/hotkey-edit.hpp | 2 + UI/installer/mp-installer.nsi | 4 + UI/locked-checkbox.cpp | 36 - UI/locked-checkbox.hpp | 14 +- UI/obf.c | 27 + UI/obf.h | 13 + UI/obs-app.cpp | 165 +- UI/obs-app.hpp | 10 + UI/obs-frontend-api/obs-frontend-api.cpp | 33 + UI/obs-frontend-api/obs-frontend-api.h | 7 + UI/obs-frontend-api/obs-frontend-internal.hpp | 7 + UI/obs.rc | 1 - UI/obs.rc.in | 26 + UI/properties-view.cpp | 95 +- UI/properties-view.hpp | 4 + UI/qt-display.cpp | 43 +- UI/qt-display.hpp | 14 +- UI/qt-wrappers.cpp | 112 + UI/qt-wrappers.hpp | 26 + UI/remote-text.cpp | 16 +- UI/remote-text.hpp | 28 +- UI/slider-ignorewheel.cpp | 22 + UI/slider-ignorewheel.hpp | 16 + UI/source-label.hpp | 2 + UI/source-list-widget.cpp | 46 - UI/source-list-widget.hpp | 22 - UI/source-tree.cpp | 123 +- UI/source-tree.hpp | 14 + UI/spinbox-ignorewheel.cpp | 14 + UI/spinbox-ignorewheel.hpp | 15 + UI/ui-config.h.in | 31 + UI/visibility-checkbox.cpp | 36 - UI/visibility-checkbox.hpp | 14 +- UI/volume-control.cpp | 56 +- UI/volume-control.hpp | 3 + UI/win-update/updater/CMakeLists.txt | 3 +- UI/win-update/updater/updater.cpp | 34 +- UI/win-update/updater/updater.manifest | 25 + UI/win-update/win-update-helpers.cpp | 2 +- UI/win-update/win-update.cpp | 4 +- UI/window-basic-about.cpp | 169 ++ UI/window-basic-about.hpp | 20 + UI/window-basic-adv-audio.cpp | 3 +- UI/window-basic-auto-config-test.cpp | 28 +- UI/window-basic-auto-config.cpp | 239 +- UI/window-basic-auto-config.hpp | 20 + UI/window-basic-filters.cpp | 49 +- UI/window-basic-filters.hpp | 4 + UI/window-basic-main-browser.cpp | 164 ++ UI/window-basic-main-dropfiles.cpp | 2 +- UI/window-basic-main-outputs.cpp | 201 +- UI/window-basic-main-outputs.hpp | 1 + UI/window-basic-main-profiles.cpp | 42 +- UI/window-basic-main-scene-collections.cpp | 6 +- UI/window-basic-main-transitions.cpp | 111 +- UI/window-basic-main.cpp | 1071 ++++++-- UI/window-basic-main.hpp | 111 +- UI/window-basic-preview.cpp | 333 ++- UI/window-basic-preview.hpp | 14 + UI/window-basic-properties.cpp | 217 +- UI/window-basic-properties.hpp | 9 + UI/window-basic-settings-stream.cpp | 505 ++++ UI/window-basic-settings.cpp | 477 ++-- UI/window-basic-settings.hpp | 52 +- UI/window-basic-source-select.cpp | 4 +- UI/window-basic-stats.cpp | 117 +- UI/window-basic-stats.hpp | 23 +- UI/window-basic-transform.cpp | 2 + UI/window-dock.cpp | 38 + UI/window-dock.hpp | 12 + UI/window-license-agreement.cpp | 25 - UI/window-license-agreement.hpp | 14 - UI/window-namedialog.cpp | 1 + UI/window-projector.cpp | 40 +- UI/window-remux.cpp | 962 +++++++- UI/window-remux.hpp | 136 +- UI/xdg-data/CMakeLists.txt | 24 + .../com.obsproject.Studio.appdata.xml.in | 23 + .../com.obsproject.Studio.desktop} | 6 +- appveyor.yml | 52 +- azure-pipelines.yml | 108 + cmake/Modules/CopyMSVCBins.cmake | 63 +- cmake/Modules/FindLibVLC.cmake | 2 +- cmake/Modules/ObsHelpers.cmake | 24 + deps/ipc-util/CMakeLists.txt | 2 +- deps/media-playback/CMakeLists.txt | 7 +- .../media-playback/closest-format.h | 2 + deps/media-playback/media-playback/media.c | 1 + deps/obs-scripting/CMakeLists.txt | 75 +- deps/obs-scripting/obs-scripting-config.h.in | 1 + deps/obs-scripting/obslua/obslua.i | 2 +- deps/obs-scripting/obspython/obspython.i | 2 +- docs/sphinx/reference-frontend-api.rst | 18 + .../reference-libobs-graphics-effects.rst | 66 + docs/sphinx/reference-outputs.rst | 32 +- docs/sphinx/reference-scenes.rst | 6 + libobs-d3d11/CMakeLists.txt | 1 + libobs-d3d11/d3d11-rebuild.cpp | 85 +- libobs-d3d11/d3d11-shader.cpp | 3 + libobs-d3d11/d3d11-stagesurf.cpp | 25 + libobs-d3d11/d3d11-subsystem.cpp | 321 ++- libobs-d3d11/d3d11-subsystem.hpp | 55 +- libobs-d3d11/d3d11-texture2d.cpp | 108 +- libobs-opengl/gl-helpers.c | 43 +- libobs-opengl/gl-helpers.h | 9 +- libobs-opengl/gl-indexbuffer.c | 2 +- libobs-opengl/gl-shader.c | 35 +- libobs-opengl/gl-stagesurf.c | 4 +- libobs-opengl/gl-subsystem.c | 72 +- libobs-opengl/gl-subsystem.h | 15 +- libobs-opengl/gl-texture2d.c | 3 + libobs-opengl/gl-texturecube.c | 9 +- libobs-opengl/gl-windows.c | 2 +- libobs/CMakeLists.txt | 19 +- .../osx/coreaudio-enum-devices.c | 19 +- .../pulse/pulseaudio-wrapper.c | 5 +- libobs/audio-monitoring/win32/wasapi-output.h | 2 + libobs/data/area.effect | 64 + libobs/data/bicubic_scale.effect | 25 +- libobs/data/bilinear_lowres_scale.effect | 26 +- libobs/data/default.effect | 25 +- libobs/data/deinterlace_base.effect | 20 +- libobs/data/deinterlace_blend.effect | 2 +- libobs/data/deinterlace_blend_2x.effect | 2 +- libobs/data/deinterlace_discard.effect | 2 +- libobs/data/deinterlace_discard_2x.effect | 2 +- libobs/data/deinterlace_linear.effect | 2 +- libobs/data/deinterlace_linear_2x.effect | 2 +- libobs/data/deinterlace_yadif.effect | 2 +- libobs/data/deinterlace_yadif_2x.effect | 2 +- libobs/data/format_conversion.effect | 136 +- libobs/data/lanczos_scale.effect | 25 +- libobs/data/repeat.effect | 36 + libobs/graphics/device-exports.h | 3 + libobs/graphics/effect-parser.c | 614 ++++- libobs/graphics/effect-parser.h | 7 + libobs/graphics/effect.c | 144 ++ libobs/graphics/effect.h | 13 +- libobs/graphics/graphics-imports.c | 10 + libobs/graphics/graphics-internal.h | 16 + libobs/graphics/graphics.c | 161 +- libobs/graphics/graphics.h | 74 +- libobs/graphics/image-file.c | 42 +- libobs/graphics/image-file.h | 38 + libobs/graphics/shader-parser.c | 2 + libobs/libobs.pc.in | 11 + libobs/media-io/audio-resampler-ffmpeg.c | 15 + libobs/media-io/video-io.c | 108 +- libobs/media-io/video-io.h | 25 +- libobs/obs-audio-controls.c | 9 + libobs/obs-audio-controls.h | 3 + libobs/obs-audio.c | 32 +- libobs/obs-config.h | 6 +- libobs/obs-defs.h | 1 + libobs/obs-display.c | 29 +- libobs/obs-encoder.c | 220 +- libobs/obs-encoder.h | 22 + libobs/obs-hotkey.c | 1 + libobs/obs-hotkey.h | 1 + libobs/obs-internal.h | 49 +- libobs/obs-module.c | 20 +- libobs/obs-nix.c | 13 + libobs/obs-output.c | 131 +- libobs/obs-output.h | 3 + libobs/obs-properties.c | 213 +- libobs/obs-properties.h | 34 + libobs/obs-scene.c | 40 +- libobs/obs-scene.h | 1 + libobs/obs-source-deinterlace.c | 23 +- libobs/obs-source-transition.c | 13 +- libobs/obs-source.c | 488 ++-- libobs/obs-source.h | 5 + libobs/obs-video-gpu-encode.c | 228 ++ libobs/obs-video.c | 280 ++- libobs/obs-windows.c | 8 +- libobs/obs.c | 265 +- libobs/obs.h | 132 +- libobs/util/apple/cfstring-utils.h | 17 + libobs/util/circlebuf.h | 4 +- libobs/util/config-file.c | 15 +- libobs/util/pipe-posix.c | 9 + libobs/util/pipe-windows.c | 41 +- libobs/util/pipe.h | 2 + libobs/util/platform-cocoa.m | 123 +- libobs/util/platform-nix.c | 33 +- libobs/util/platform-windows.c | 38 +- libobs/util/platform.c | 19 +- libobs/util/platform.h | 2 + libobs/util/threading-posix.c | 8 +- libobs/util/utf8.c | 2 +- libobs/util/util_uint128.h | 20 +- libobs/util/windows/ComPtr.hpp | 2 +- libobs/util/windows/WinHandle.hpp | 4 +- libobs/util/windows/win-version.h | 1 + plugins/CMakeLists.txt | 4 + .../coreaudio-encoder/data/locale/bg-BG.ini | 1 + .../coreaudio-encoder/data/locale/da-DK.ini | 8 +- .../coreaudio-encoder/data/locale/de-DE.ini | 4 +- .../coreaudio-encoder/data/locale/eu-ES.ini | 2 +- .../coreaudio-encoder/data/locale/fa-IR.ini | 4 + .../coreaudio-encoder/data/locale/fr-FR.ini | 4 +- .../coreaudio-encoder/data/locale/it-IT.ini | 8 +- .../coreaudio-encoder/data/locale/mn-MN.ini | 6 + .../coreaudio-encoder/data/locale/pt-PT.ini | 2 +- .../coreaudio-encoder/data/locale/ro-RO.ini | 2 +- .../coreaudio-encoder/data/locale/sr-CS.ini | 6 +- .../coreaudio-encoder/data/locale/sr-SP.ini | 4 +- .../coreaudio-encoder/data/locale/ur-PK.ini | 2 + plugins/coreaudio-encoder/encoder.cpp | 16 +- plugins/decklink/DecklinkBase.cpp | 20 + plugins/decklink/DecklinkBase.h | 33 + .../{decklink.cpp => DecklinkInput.cpp} | 64 +- .../{decklink.hpp => DecklinkInput.hpp} | 40 +- plugins/decklink/DecklinkOutput.cpp | 110 + plugins/decklink/DecklinkOutput.hpp | 34 + plugins/decklink/audio-repack.c | 108 +- plugins/decklink/audio-repack.h | 13 +- plugins/decklink/const.h | 41 + plugins/decklink/data/locale/ar-SA.ini | 14 + plugins/decklink/data/locale/bn-BD.ini | 3 + plugins/decklink/data/locale/ca-ES.ini | 10 +- plugins/decklink/data/locale/cs-CZ.ini | 10 +- plugins/decklink/data/locale/da-DK.ini | 16 +- plugins/decklink/data/locale/de-DE.ini | 14 +- plugins/decklink/data/locale/el-GR.ini | 4 +- plugins/decklink/data/locale/en-US.ini | 10 +- plugins/decklink/data/locale/es-ES.ini | 10 +- plugins/decklink/data/locale/eu-ES.ini | 16 +- plugins/decklink/data/locale/fa-IR.ini | 22 + plugins/decklink/data/locale/fi-FI.ini | 10 +- plugins/decklink/data/locale/fil-PH.ini | 2 - plugins/decklink/data/locale/fr-FR.ini | 8 +- plugins/decklink/data/locale/gd-GB.ini | 2 - plugins/decklink/data/locale/hu-HU.ini | 10 +- plugins/decklink/data/locale/it-IT.ini | 12 +- plugins/decklink/data/locale/ja-JP.ini | 10 +- plugins/decklink/data/locale/ka-GE.ini | 24 +- plugins/decklink/data/locale/ko-KR.ini | 10 +- plugins/decklink/data/locale/mn-MN.ini | 2 + plugins/decklink/data/locale/nb-NO.ini | 10 +- plugins/decklink/data/locale/nl-NL.ini | 10 +- plugins/decklink/data/locale/pl-PL.ini | 10 +- plugins/decklink/data/locale/pt-BR.ini | 10 +- plugins/decklink/data/locale/pt-PT.ini | 11 + plugins/decklink/data/locale/ro-RO.ini | 4 +- plugins/decklink/data/locale/ru-RU.ini | 10 +- plugins/decklink/data/locale/sk-SK.ini | 13 +- plugins/decklink/data/locale/sr-CS.ini | 14 + plugins/decklink/data/locale/sr-SP.ini | 14 + plugins/decklink/data/locale/sv-SE.ini | 10 +- plugins/decklink/data/locale/tl-PH.ini | 2 - plugins/decklink/data/locale/tr-TR.ini | 8 +- plugins/decklink/data/locale/uk-UA.ini | 8 +- plugins/decklink/data/locale/zh-CN.ini | 24 +- plugins/decklink/data/locale/zh-TW.ini | 12 +- .../decklink/decklink-device-discovery.hpp | 5 +- plugins/decklink/decklink-device-instance.cpp | 216 +- plugins/decklink/decklink-device-instance.hpp | 30 +- plugins/decklink/decklink-device-mode.cpp | 16 + plugins/decklink/decklink-device-mode.hpp | 3 + plugins/decklink/decklink-device.cpp | 175 +- plugins/decklink/decklink-device.hpp | 39 +- plugins/decklink/decklink-devices.cpp | 17 + plugins/decklink/decklink-devices.hpp | 8 + plugins/decklink/decklink-output.cpp | 262 ++ plugins/decklink/decklink-source.cpp | 318 +++ plugins/decklink/linux/CMakeLists.txt | 24 +- .../decklink/linux/decklink-sdk/DeckLinkAPI.h | 278 ++- .../decklink-sdk/DeckLinkAPIConfiguration.h | 30 +- .../DeckLinkAPIConfiguration_v10_2.h | 6 +- .../DeckLinkAPIConfiguration_v10_4.h | 2 +- .../DeckLinkAPIConfiguration_v10_5.h | 6 +- .../DeckLinkAPIConfiguration_v10_9.h | 62 + .../decklink-sdk/DeckLinkAPIDeckControl.h | 14 +- .../linux/decklink-sdk/DeckLinkAPIDiscovery.h | 12 +- .../decklink-sdk/DeckLinkAPIDispatch.cpp | 44 +- .../DeckLinkAPIDispatch_v10_8.cpp | 146 ++ .../decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp | 20 +- .../decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp | 24 +- .../linux/decklink-sdk/DeckLinkAPIModes.h | 75 +- .../linux/decklink-sdk/DeckLinkAPITypes.h | 15 +- .../linux/decklink-sdk/DeckLinkAPIVersion.h | 8 +- .../linux/decklink-sdk/DeckLinkAPI_v10_2.h | 6 +- .../linux/decklink-sdk/DeckLinkAPI_v10_9.h | 45 + .../linux/decklink-sdk/DeckLinkAPI_v7_1.h | 54 +- .../linux/decklink-sdk/DeckLinkAPI_v7_3.h | 12 +- .../linux/decklink-sdk/DeckLinkAPI_v7_6.h | 70 +- .../linux/decklink-sdk/DeckLinkAPI_v7_9.h | 8 +- .../linux/decklink-sdk/DeckLinkAPI_v8_0.h | 10 +- .../linux/decklink-sdk/DeckLinkAPI_v8_1.h | 12 +- .../linux/decklink-sdk/DeckLinkAPI_v9_2.h | 6 +- .../linux/decklink-sdk/DeckLinkAPI_v9_9.h | 6 +- .../decklink/linux/decklink-sdk/LinuxCOM.h | 21 +- plugins/decklink/mac/CMakeLists.txt | 36 +- .../decklink/mac/decklink-sdk/DeckLinkAPI.h | 282 ++- .../decklink-sdk/DeckLinkAPIConfiguration.h | 30 +- .../DeckLinkAPIConfiguration_v10_2.h | 4 +- .../DeckLinkAPIConfiguration_v10_5.h | 4 +- .../DeckLinkAPIConfiguration_v10_9.h | 62 + .../mac/decklink-sdk/DeckLinkAPIDeckControl.h | 14 +- .../mac/decklink-sdk/DeckLinkAPIDiscovery.h | 12 +- .../mac/decklink-sdk/DeckLinkAPIDispatch.cpp | 53 +- .../DeckLinkAPIDispatch_v10_8.cpp | 193 ++ .../decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp | 22 +- .../decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp | 28 +- .../mac/decklink-sdk/DeckLinkAPIModes.h | 75 +- .../mac/decklink-sdk/DeckLinkAPIStreaming.h | 40 +- .../mac/decklink-sdk/DeckLinkAPITypes.h | 15 +- .../mac/decklink-sdk/DeckLinkAPIVersion.h | 9 +- .../mac/decklink-sdk/DeckLinkAPI_v10_2.h | 6 +- .../mac/decklink-sdk/DeckLinkAPI_v10_9.h | 45 + .../mac/decklink-sdk/DeckLinkAPI_v7_1.h | 36 +- .../mac/decklink-sdk/DeckLinkAPI_v7_3.h | 4 +- .../mac/decklink-sdk/DeckLinkAPI_v7_6.h | 36 +- .../mac/decklink-sdk/DeckLinkAPI_v7_9.h | 4 +- .../mac/decklink-sdk/DeckLinkAPI_v8_0.h | 4 +- .../mac/decklink-sdk/DeckLinkAPI_v8_1.h | 8 +- .../mac/decklink-sdk/DeckLinkAPI_v9_2.h | 4 +- .../mac/decklink-sdk/DeckLinkAPI_v9_9.h | 36 +- plugins/decklink/mac/platform.cpp | 15 +- plugins/decklink/plugin-main.cpp | 299 +-- plugins/decklink/util.cpp | 43 + plugins/decklink/util.hpp | 7 + plugins/decklink/win/CMakeLists.txt | 27 +- .../decklink/win/decklink-sdk/DeckLinkAPI.idl | 256 +- .../decklink-sdk/DeckLinkAPIConfiguration.idl | 26 +- .../decklink-sdk/DeckLinkAPIDeckControl.idl | 6 +- .../win/decklink-sdk/DeckLinkAPIDiscovery.idl | 6 +- .../win/decklink-sdk/DeckLinkAPIModes.idl | 67 +- .../win/decklink-sdk/DeckLinkAPIStreaming.idl | 12 +- .../DeckLinkAPIStreaming_v10_8.idl | 40 + .../win/decklink-sdk/DeckLinkAPITypes.idl | 9 +- .../win/decklink-sdk/DeckLinkAPIVersion.h | 8 +- .../win/decklink-sdk/DeckLinkAPI_v10_2.idl | 6 +- .../win/decklink-sdk/DeckLinkAPI_v10_5.idl | 2 +- .../win/decklink-sdk/DeckLinkAPI_v10_8.idl | 46 + .../win/decklink-sdk/DeckLinkAPI_v10_9.idl | 61 + .../win/decklink-sdk/DeckLinkAPI_v7_1.idl | 42 +- .../win/decklink-sdk/DeckLinkAPI_v7_3.idl | 8 +- .../win/decklink-sdk/DeckLinkAPI_v7_6.idl | 18 +- .../win/decklink-sdk/DeckLinkAPI_v7_9.idl | 4 +- .../win/decklink-sdk/DeckLinkAPI_v8_0.idl | 4 +- .../win/decklink-sdk/DeckLinkAPI_v8_1.idl | 4 +- .../win/decklink-sdk/DeckLinkAPI_v9_2.idl | 4 +- .../win/decklink-sdk/DeckLinkAPI_v9_9.idl | 6 +- plugins/image-source/color-source.c | 7 +- plugins/image-source/data/locale/ar-SA.ini | 3 + plugins/image-source/data/locale/bg-BG.ini | 18 + plugins/image-source/data/locale/da-DK.ini | 18 +- plugins/image-source/data/locale/de-DE.ini | 12 +- plugins/image-source/data/locale/fa-IR.ini | 33 + plugins/image-source/data/locale/fr-FR.ini | 8 +- plugins/image-source/data/locale/it-IT.ini | 38 +- plugins/image-source/data/locale/mn-MN.ini | 22 + plugins/image-source/data/locale/pt-PT.ini | 9 + plugins/image-source/data/locale/ro-RO.ini | 18 +- plugins/image-source/data/locale/ru-RU.ini | 2 +- plugins/image-source/data/locale/sr-CS.ini | 20 + plugins/image-source/data/locale/sr-SP.ini | 20 + plugins/image-source/data/locale/zh-CN.ini | 10 +- plugins/image-source/image-source.c | 58 +- plugins/image-source/obs-slideshow.c | 34 +- plugins/linux-alsa/data/locale/da-DK.ini | 2 +- plugins/linux-alsa/data/locale/eu-ES.ini | 2 +- plugins/linux-alsa/data/locale/fa-IR.ini | 3 + plugins/linux-alsa/linux-alsa.c | 4 + plugins/linux-capture/CMakeLists.txt | 2 +- plugins/linux-capture/data/locale/da-DK.ini | 2 +- plugins/linux-capture/data/locale/de-DE.ini | 8 +- plugins/linux-capture/data/locale/fa-IR.ini | 15 + plugins/linux-capture/data/locale/fr-FR.ini | 4 +- plugins/linux-capture/data/locale/it-IT.ini | 22 +- plugins/linux-capture/data/locale/ro-RO.ini | 2 +- plugins/linux-capture/data/locale/sr-CS.ini | 6 +- plugins/linux-capture/data/locale/sr-SP.ini | 6 +- plugins/linux-capture/data/locale/sv-SE.ini | 2 +- plugins/linux-capture/linux-capture.c | 4 + plugins/linux-capture/xcompcap-main.cpp | 141 +- plugins/linux-capture/xhelpers.c | 73 + plugins/linux-capture/xhelpers.h | 33 + plugins/linux-capture/xshm-input.c | 29 +- plugins/linux-jack/data/locale/da-DK.ini | 6 +- plugins/linux-jack/data/locale/de-DE.ini | 4 +- plugins/linux-jack/data/locale/fa-IR.ini | 3 + plugins/linux-jack/data/locale/it-IT.ini | 4 +- plugins/linux-jack/data/locale/ur-PK.ini | 4 + plugins/linux-jack/jack-wrapper.c | 2 +- plugins/linux-jack/linux-jack.c | 4 + .../linux-pulseaudio/data/locale/da-DK.ini | 4 +- .../linux-pulseaudio/data/locale/de-DE.ini | 4 +- .../linux-pulseaudio/data/locale/fa-IR.ini | 4 + .../linux-pulseaudio/data/locale/fr-FR.ini | 4 +- .../linux-pulseaudio/data/locale/it-IT.ini | 4 +- .../linux-pulseaudio/data/locale/ka-GE.ini | 2 +- plugins/linux-pulseaudio/linux-pulseaudio.c | 4 + plugins/linux-v4l2/data/locale/ar-SA.ini | 3 + plugins/linux-v4l2/data/locale/ca-ES.ini | 3 + plugins/linux-v4l2/data/locale/cs-CZ.ini | 3 + plugins/linux-v4l2/data/locale/da-DK.ini | 17 +- plugins/linux-v4l2/data/locale/de-DE.ini | 5 +- plugins/linux-v4l2/data/locale/en-US.ini | 3 + plugins/linux-v4l2/data/locale/es-ES.ini | 3 + plugins/linux-v4l2/data/locale/eu-ES.ini | 3 + plugins/linux-v4l2/data/locale/fa-IR.ini | 14 + plugins/linux-v4l2/data/locale/fi-FI.ini | 3 + plugins/linux-v4l2/data/locale/fr-FR.ini | 7 +- plugins/linux-v4l2/data/locale/hu-HU.ini | 3 + plugins/linux-v4l2/data/locale/it-IT.ini | 15 +- plugins/linux-v4l2/data/locale/ja-JP.ini | 3 + plugins/linux-v4l2/data/locale/ka-GE.ini | 5 +- plugins/linux-v4l2/data/locale/ko-KR.ini | 3 + plugins/linux-v4l2/data/locale/nb-NO.ini | 3 + plugins/linux-v4l2/data/locale/nl-NL.ini | 3 + plugins/linux-v4l2/data/locale/pl-PL.ini | 3 + plugins/linux-v4l2/data/locale/pt-BR.ini | 3 + plugins/linux-v4l2/data/locale/ro-RO.ini | 2 +- plugins/linux-v4l2/data/locale/ru-RU.ini | 3 + plugins/linux-v4l2/data/locale/sk-SK.ini | 3 + plugins/linux-v4l2/data/locale/sr-CS.ini | 2 +- plugins/linux-v4l2/data/locale/sr-SP.ini | 2 +- plugins/linux-v4l2/data/locale/sv-SE.ini | 3 + plugins/linux-v4l2/data/locale/tr-TR.ini | 3 + plugins/linux-v4l2/data/locale/uk-UA.ini | 3 + plugins/linux-v4l2/data/locale/zh-CN.ini | 3 + plugins/linux-v4l2/data/locale/zh-TW.ini | 3 + plugins/linux-v4l2/linux-v4l2.c | 4 + plugins/linux-v4l2/v4l2-input.c | 17 +- plugins/mac-avcapture/av-capture.mm | 4 + plugins/mac-avcapture/data/locale/ar-SA.ini | 7 + plugins/mac-avcapture/data/locale/bg-BG.ini | 10 + plugins/mac-avcapture/data/locale/da-DK.ini | 14 +- plugins/mac-avcapture/data/locale/de-DE.ini | 4 +- plugins/mac-avcapture/data/locale/eu-ES.ini | 2 +- plugins/mac-avcapture/data/locale/fa-IR.ini | 14 + plugins/mac-avcapture/data/locale/fr-FR.ini | 10 +- plugins/mac-avcapture/data/locale/it-IT.ini | 16 +- plugins/mac-avcapture/data/locale/ka-GE.ini | 2 +- plugins/mac-avcapture/data/locale/ro-RO.ini | 2 +- plugins/mac-avcapture/data/locale/sr-CS.ini | 6 +- plugins/mac-avcapture/data/locale/sr-SP.ini | 6 +- plugins/mac-avcapture/data/locale/uk-UA.ini | 2 +- plugins/mac-avcapture/data/locale/vi-VN.ini | 1 + plugins/mac-capture/CMakeLists.txt | 1 - plugins/mac-capture/audio-device-enum.c | 7 +- plugins/mac-capture/data/locale/da-DK.ini | 10 +- plugins/mac-capture/data/locale/de-DE.ini | 12 +- plugins/mac-capture/data/locale/fa-IR.ini | 21 + plugins/mac-capture/data/locale/fr-FR.ini | 14 +- plugins/mac-capture/data/locale/it-IT.ini | 16 +- plugins/mac-capture/data/locale/ka-GE.ini | 2 +- plugins/mac-capture/data/locale/nl-NL.ini | 2 +- plugins/mac-capture/data/locale/ro-RO.ini | 4 +- plugins/mac-capture/data/locale/sr-CS.ini | 10 +- plugins/mac-capture/data/locale/sr-SP.ini | 10 +- plugins/mac-capture/mac-audio.c | 9 +- plugins/mac-capture/mac-helpers.h | 34 - plugins/mac-capture/plugin-main.c | 4 + plugins/mac-syphon/data/locale/ar-SA.ini | 1 - plugins/mac-syphon/data/locale/bn-BD.ini | 1 - plugins/mac-syphon/data/locale/ca-ES.ini | 2 +- plugins/mac-syphon/data/locale/cs-CZ.ini | 2 +- plugins/mac-syphon/data/locale/da-DK.ini | 6 +- plugins/mac-syphon/data/locale/de-DE.ini | 4 +- plugins/mac-syphon/data/locale/el-GR.ini | 1 - plugins/mac-syphon/data/locale/en-US.ini | 2 +- plugins/mac-syphon/data/locale/es-ES.ini | 2 +- plugins/mac-syphon/data/locale/et-EE.ini | 1 - plugins/mac-syphon/data/locale/eu-ES.ini | 2 +- plugins/mac-syphon/data/locale/fa-IR.ini | 12 + plugins/mac-syphon/data/locale/fi-FI.ini | 2 +- plugins/mac-syphon/data/locale/fil-PH.ini | 1 - plugins/mac-syphon/data/locale/fr-FR.ini | 4 +- plugins/mac-syphon/data/locale/gl-ES.ini | 1 - plugins/mac-syphon/data/locale/hr-HR.ini | 1 - plugins/mac-syphon/data/locale/hu-HU.ini | 2 +- plugins/mac-syphon/data/locale/it-IT.ini | 8 +- plugins/mac-syphon/data/locale/ja-JP.ini | 2 +- plugins/mac-syphon/data/locale/ka-GE.ini | 2 +- plugins/mac-syphon/data/locale/ko-KR.ini | 2 +- plugins/mac-syphon/data/locale/nb-NO.ini | 2 +- plugins/mac-syphon/data/locale/nl-NL.ini | 2 +- plugins/mac-syphon/data/locale/pl-PL.ini | 2 +- plugins/mac-syphon/data/locale/pt-BR.ini | 2 +- plugins/mac-syphon/data/locale/pt-PT.ini | 1 - plugins/mac-syphon/data/locale/ro-RO.ini | 1 - plugins/mac-syphon/data/locale/ru-RU.ini | 2 +- plugins/mac-syphon/data/locale/sk-SK.ini | 4 +- plugins/mac-syphon/data/locale/sl-SI.ini | 1 - plugins/mac-syphon/data/locale/sr-CS.ini | 2 +- plugins/mac-syphon/data/locale/sr-SP.ini | 2 +- plugins/mac-syphon/data/locale/sv-SE.ini | 2 +- plugins/mac-syphon/data/locale/tr-TR.ini | 2 +- plugins/mac-syphon/data/locale/uk-UA.ini | 2 +- plugins/mac-syphon/data/locale/vi-VN.ini | 6 + plugins/mac-syphon/data/locale/zh-CN.ini | 2 +- plugins/mac-syphon/data/locale/zh-TW.ini | 2 +- plugins/mac-syphon/plugin-main.c | 4 + plugins/mac-vth264/data/locale/da-DK.ini | 2 +- plugins/mac-vth264/data/locale/de-DE.ini | 14 +- plugins/mac-vth264/data/locale/fa-IR.ini | 14 + plugins/mac-vth264/data/locale/fr-FR.ini | 8 +- plugins/mac-vth264/data/locale/it-IT.ini | 22 +- plugins/mac-vth264/data/locale/zh-CN.ini | 2 +- plugins/mac-vth264/encoder.c | 23 +- plugins/obs-ffmpeg/CMakeLists.txt | 16 + plugins/obs-ffmpeg/data/locale/ar-SA.ini | 6 +- plugins/obs-ffmpeg/data/locale/bg-BG.ini | 36 +- plugins/obs-ffmpeg/data/locale/bn-BD.ini | 5 - plugins/obs-ffmpeg/data/locale/ca-ES.ini | 26 +- plugins/obs-ffmpeg/data/locale/cs-CZ.ini | 20 +- plugins/obs-ffmpeg/data/locale/da-DK.ini | 44 +- plugins/obs-ffmpeg/data/locale/de-DE.ini | 40 +- plugins/obs-ffmpeg/data/locale/el-GR.ini | 9 - plugins/obs-ffmpeg/data/locale/en-US.ini | 26 +- plugins/obs-ffmpeg/data/locale/es-ES.ini | 24 +- plugins/obs-ffmpeg/data/locale/et-EE.ini | 8 - plugins/obs-ffmpeg/data/locale/eu-ES.ini | 30 +- plugins/obs-ffmpeg/data/locale/fa-IR.ini | 43 + plugins/obs-ffmpeg/data/locale/fi-FI.ini | 20 +- plugins/obs-ffmpeg/data/locale/fil-PH.ini | 9 - plugins/obs-ffmpeg/data/locale/fr-FR.ini | 38 +- plugins/obs-ffmpeg/data/locale/gd-GB.ini | 18 +- plugins/obs-ffmpeg/data/locale/gl-ES.ini | 2 - plugins/obs-ffmpeg/data/locale/he-IL.ini | 8 - plugins/obs-ffmpeg/data/locale/hr-HR.ini | 8 - plugins/obs-ffmpeg/data/locale/hu-HU.ini | 26 +- plugins/obs-ffmpeg/data/locale/it-IT.ini | 64 +- plugins/obs-ffmpeg/data/locale/ja-JP.ini | 28 +- plugins/obs-ffmpeg/data/locale/ka-GE.ini | 28 +- plugins/obs-ffmpeg/data/locale/ko-KR.ini | 24 +- plugins/obs-ffmpeg/data/locale/nb-NO.ini | 24 +- plugins/obs-ffmpeg/data/locale/nl-NL.ini | 24 +- plugins/obs-ffmpeg/data/locale/pl-PL.ini | 30 +- plugins/obs-ffmpeg/data/locale/pt-BR.ini | 26 +- plugins/obs-ffmpeg/data/locale/pt-PT.ini | 18 +- plugins/obs-ffmpeg/data/locale/ro-RO.ini | 14 +- plugins/obs-ffmpeg/data/locale/ru-RU.ini | 20 +- plugins/obs-ffmpeg/data/locale/sk-SK.ini | 20 +- plugins/obs-ffmpeg/data/locale/sl-SI.ini | 1 - plugins/obs-ffmpeg/data/locale/sr-CS.ini | 35 +- plugins/obs-ffmpeg/data/locale/sr-SP.ini | 35 +- plugins/obs-ffmpeg/data/locale/sv-SE.ini | 24 +- plugins/obs-ffmpeg/data/locale/tl-PH.ini | 9 - plugins/obs-ffmpeg/data/locale/tr-TR.ini | 19 +- plugins/obs-ffmpeg/data/locale/uk-UA.ini | 22 +- plugins/obs-ffmpeg/data/locale/vi-VN.ini | 16 +- plugins/obs-ffmpeg/data/locale/zh-CN.ini | 30 +- plugins/obs-ffmpeg/data/locale/zh-TW.ini | 24 +- plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt | 22 +- plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c | 18 +- plugins/obs-ffmpeg/jim-nvenc-helpers.c | 149 ++ plugins/obs-ffmpeg/jim-nvenc.c | 945 ++++++++ plugins/obs-ffmpeg/jim-nvenc.h | 14 + plugins/obs-ffmpeg/nvEncodeAPI.h | 131 +- plugins/obs-ffmpeg/obs-ffmpeg-mux.c | 70 +- plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c | 108 +- plugins/obs-ffmpeg/obs-ffmpeg-output.c | 287 ++- plugins/obs-ffmpeg/obs-ffmpeg-source.c | 4 + plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c | 539 +++++ plugins/obs-ffmpeg/obs-ffmpeg.c | 184 +- plugins/obs-filters/CMakeLists.txt | 30 +- plugins/obs-filters/chroma-key-filter.c | 13 +- plugins/obs-filters/color-key-filter.c | 3 +- .../obs-filters/data/chroma_key_filter.effect | 24 +- .../obs-filters/data/color_key_filter.effect | 8 - plugins/obs-filters/data/locale/bg-BG.ini | 4 + plugins/obs-filters/data/locale/ca-ES.ini | 24 + plugins/obs-filters/data/locale/cs-CZ.ini | 24 + plugins/obs-filters/data/locale/da-DK.ini | 68 +- plugins/obs-filters/data/locale/de-DE.ini | 48 +- plugins/obs-filters/data/locale/en-US.ini | 24 + plugins/obs-filters/data/locale/es-ES.ini | 32 +- plugins/obs-filters/data/locale/eu-ES.ini | 30 +- plugins/obs-filters/data/locale/fa-IR.ini | 18 + plugins/obs-filters/data/locale/fi-FI.ini | 24 + plugins/obs-filters/data/locale/fr-FR.ini | 44 +- plugins/obs-filters/data/locale/hu-HU.ini | 24 + plugins/obs-filters/data/locale/it-IT.ini | 100 +- plugins/obs-filters/data/locale/ja-JP.ini | 24 + plugins/obs-filters/data/locale/ka-GE.ini | 26 +- plugins/obs-filters/data/locale/ko-KR.ini | 26 +- plugins/obs-filters/data/locale/nb-NO.ini | 24 + plugins/obs-filters/data/locale/nl-NL.ini | 19 + plugins/obs-filters/data/locale/pl-PL.ini | 24 + plugins/obs-filters/data/locale/pt-BR.ini | 24 + plugins/obs-filters/data/locale/pt-PT.ini | 30 + plugins/obs-filters/data/locale/ro-RO.ini | 22 +- plugins/obs-filters/data/locale/ru-RU.ini | 24 + plugins/obs-filters/data/locale/sk-SK.ini | 26 + plugins/obs-filters/data/locale/sr-CS.ini | 35 +- plugins/obs-filters/data/locale/sr-SP.ini | 35 +- plugins/obs-filters/data/locale/sv-SE.ini | 24 + plugins/obs-filters/data/locale/tr-TR.ini | 16 + plugins/obs-filters/data/locale/uk-UA.ini | 24 + plugins/obs-filters/data/locale/zh-CN.ini | 26 +- plugins/obs-filters/data/locale/zh-TW.ini | 24 + .../obs-filters/data/luma_key_filter.effect | 51 + plugins/obs-filters/data/sharpness.effect | 25 +- plugins/obs-filters/expander-filter.c | 419 ++++ plugins/obs-filters/invert-audio-polarity.c | 49 + plugins/obs-filters/limiter-filter.c | 215 ++ plugins/obs-filters/luma-key-filter.c | 153 ++ plugins/obs-filters/mask-filter.c | 4 +- plugins/obs-filters/obs-filters.c | 13 +- plugins/obs-filters/scale-filter.c | 50 +- plugins/obs-libfdk/data/locale/da-DK.ini | 4 +- plugins/obs-libfdk/data/locale/de-DE.ini | 4 +- plugins/obs-libfdk/data/locale/fr-FR.ini | 2 +- plugins/obs-libfdk/data/locale/it-IT.ini | 6 +- plugins/obs-libfdk/obs-libfdk.c | 14 +- plugins/obs-outputs/data/locale/bg-BG.ini | 11 + plugins/obs-outputs/data/locale/ca-ES.ini | 4 +- plugins/obs-outputs/data/locale/cs-CZ.ini | 4 +- plugins/obs-outputs/data/locale/da-DK.ini | 18 +- plugins/obs-outputs/data/locale/de-DE.ini | 18 +- plugins/obs-outputs/data/locale/el-GR.ini | 2 - plugins/obs-outputs/data/locale/en-US.ini | 4 +- plugins/obs-outputs/data/locale/es-ES.ini | 4 +- plugins/obs-outputs/data/locale/fi-FI.ini | 4 +- plugins/obs-outputs/data/locale/fil-PH.ini | 4 +- plugins/obs-outputs/data/locale/fr-FR.ini | 18 +- plugins/obs-outputs/data/locale/gd-GB.ini | 6 + plugins/obs-outputs/data/locale/hu-HU.ini | 4 +- plugins/obs-outputs/data/locale/it-IT.ini | 19 +- plugins/obs-outputs/data/locale/ja-JP.ini | 10 +- plugins/obs-outputs/data/locale/ka-GE.ini | 8 +- plugins/obs-outputs/data/locale/ko-KR.ini | 4 +- plugins/obs-outputs/data/locale/mn-MN.ini | 3 + plugins/obs-outputs/data/locale/nb-NO.ini | 4 +- plugins/obs-outputs/data/locale/nl-NL.ini | 4 +- plugins/obs-outputs/data/locale/pl-PL.ini | 6 +- plugins/obs-outputs/data/locale/pt-BR.ini | 4 +- plugins/obs-outputs/data/locale/pt-PT.ini | 9 + plugins/obs-outputs/data/locale/ro-RO.ini | 1 + plugins/obs-outputs/data/locale/ru-RU.ini | 6 +- plugins/obs-outputs/data/locale/sk-SK.ini | 1 - plugins/obs-outputs/data/locale/sr-CS.ini | 10 +- plugins/obs-outputs/data/locale/sr-SP.ini | 10 +- plugins/obs-outputs/data/locale/sv-SE.ini | 4 +- plugins/obs-outputs/data/locale/tl-PH.ini | 2 - plugins/obs-outputs/data/locale/tr-TR.ini | 4 +- plugins/obs-outputs/data/locale/uk-UA.ini | 4 +- plugins/obs-outputs/data/locale/vi-VN.ini | 4 +- plugins/obs-outputs/data/locale/zh-CN.ini | 14 +- plugins/obs-outputs/data/locale/zh-TW.ini | 4 +- plugins/obs-outputs/flv-output.c | 15 +- plugins/obs-outputs/ftl-stream.c | 15 + plugins/obs-outputs/librtmp/handshake.h | 28 +- plugins/obs-outputs/librtmp/hashswf.c | 16 +- plugins/obs-outputs/librtmp/rtmp.c | 15 +- plugins/obs-outputs/librtmp/rtmp.h | 1 + plugins/obs-outputs/net-if.c | 2 +- plugins/obs-outputs/obs-outputs.c | 4 + plugins/obs-outputs/rtmp-stream.c | 15 + plugins/obs-outputs/rtmp-stream.h | 1 + plugins/obs-text/data/locale/ar-SA.ini | 34 + plugins/obs-text/data/locale/ca-ES.ini | 4 + plugins/obs-text/data/locale/cs-CZ.ini | 4 + plugins/obs-text/data/locale/da-DK.ini | 24 +- plugins/obs-text/data/locale/de-DE.ini | 10 +- plugins/obs-text/data/locale/en-US.ini | 4 + plugins/obs-text/data/locale/es-ES.ini | 4 + plugins/obs-text/data/locale/eu-ES.ini | 4 + plugins/obs-text/data/locale/fa-IR.ini | 38 + plugins/obs-text/data/locale/fi-FI.ini | 4 + plugins/obs-text/data/locale/fr-FR.ini | 6 +- plugins/obs-text/data/locale/hu-HU.ini | 4 + plugins/obs-text/data/locale/it-IT.ini | 30 +- plugins/obs-text/data/locale/ja-JP.ini | 4 + plugins/obs-text/data/locale/ka-GE.ini | 14 +- plugins/obs-text/data/locale/ko-KR.ini | 4 + plugins/obs-text/data/locale/mn-MN.ini | 25 + plugins/obs-text/data/locale/nb-NO.ini | 4 + plugins/obs-text/data/locale/nl-NL.ini | 4 + plugins/obs-text/data/locale/pl-PL.ini | 4 + plugins/obs-text/data/locale/pt-BR.ini | 4 + plugins/obs-text/data/locale/pt-PT.ini | 2 + plugins/obs-text/data/locale/ru-RU.ini | 4 + plugins/obs-text/data/locale/sk-SK.ini | 4 + plugins/obs-text/data/locale/sv-SE.ini | 4 + plugins/obs-text/data/locale/tr-TR.ini | 4 + plugins/obs-text/data/locale/uk-UA.ini | 4 + plugins/obs-text/data/locale/zh-CN.ini | 4 + plugins/obs-text/data/locale/zh-TW.ini | 4 + plugins/obs-text/gdiplus/obs-text.cpp | 51 +- .../data/fade_to_color_transition.effect | 5 +- .../data/fade_transition.effect | 6 +- plugins/obs-transitions/data/locale/da-DK.ini | 26 +- plugins/obs-transitions/data/locale/de-DE.ini | 10 +- plugins/obs-transitions/data/locale/eu-ES.ini | 4 +- plugins/obs-transitions/data/locale/fr-FR.ini | 12 +- plugins/obs-transitions/data/locale/it-IT.ini | 62 +- plugins/obs-transitions/data/locale/ka-GE.ini | 12 +- plugins/obs-transitions/data/locale/pt-PT.ini | 6 + plugins/obs-transitions/data/locale/ro-RO.ini | 19 +- plugins/obs-transitions/data/locale/sk-SK.ini | 9 + plugins/obs-transitions/data/locale/sr-CS.ini | 18 +- plugins/obs-transitions/data/locale/sr-SP.ini | 18 +- plugins/obs-transitions/data/locale/zh-CN.ini | 36 +- .../data/luma_wipe_transition.effect | 6 +- .../obs-transitions/data/premultiplied.inc | 9 - .../data/slide_transition.effect | 4 +- .../data/swipe_transition.effect | 4 +- plugins/obs-transitions/obs-transitions.c | 5 +- plugins/obs-x264/data/locale/da-DK.ini | 16 +- plugins/obs-x264/data/locale/de-DE.ini | 2 +- plugins/obs-x264/data/locale/fa-IR.ini | 13 + plugins/obs-x264/data/locale/fr-FR.ini | 8 +- plugins/obs-x264/data/locale/it-IT.ini | 20 +- plugins/obs-x264/data/locale/ko-KR.ini | 2 +- plugins/obs-x264/data/locale/mn-MN.ini | 13 + plugins/obs-x264/data/locale/ro-RO.ini | 3 +- plugins/obs-x264/data/locale/sk-SK.ini | 8 +- plugins/obs-x264/data/locale/zh-CN.ini | 6 +- plugins/obs-x264/obs-x264-plugin-main.c | 4 + plugins/obs-x264/obs-x264.c | 4 +- plugins/rtmp-services/data/locale/da-DK.ini | 14 +- plugins/rtmp-services/data/locale/it-IT.ini | 12 +- plugins/rtmp-services/data/locale/ja-JP.ini | 2 +- plugins/rtmp-services/data/locale/ka-GE.ini | 2 +- plugins/rtmp-services/data/locale/mn-MN.ini | 11 + plugins/rtmp-services/data/locale/pt-PT.ini | 1 + plugins/rtmp-services/data/locale/ro-RO.ini | 11 +- plugins/rtmp-services/data/locale/sr-CS.ini | 1 + plugins/rtmp-services/data/locale/sr-SP.ini | 1 + plugins/rtmp-services/data/locale/uk-UA.ini | 2 +- plugins/rtmp-services/data/locale/vi-VN.ini | 2 +- plugins/rtmp-services/data/package.json | 4 +- plugins/rtmp-services/data/services.json | 492 ++-- plugins/rtmp-services/rtmp-common.c | 41 +- plugins/rtmp-services/rtmp-format-ver.h | 2 +- plugins/rtmp-services/rtmp-services-main.c | 4 + plugins/text-freetype2/data/locale/bg-BG.ini | 12 + plugins/text-freetype2/data/locale/da-DK.ini | 6 +- plugins/text-freetype2/data/locale/de-DE.ini | 4 +- plugins/text-freetype2/data/locale/fa-IR.ini | 15 + plugins/text-freetype2/data/locale/fr-FR.ini | 2 +- plugins/text-freetype2/data/locale/it-IT.ini | 12 +- plugins/text-freetype2/data/locale/mn-MN.ini | 15 + plugins/text-freetype2/data/locale/pt-PT.ini | 2 + plugins/text-freetype2/data/locale/sk-SK.ini | 2 + plugins/text-freetype2/data/locale/sr-CS.ini | 2 + plugins/text-freetype2/data/locale/sr-SP.ini | 2 + plugins/text-freetype2/obs-convenience.h | 2 + plugins/text-freetype2/text-freetype2.c | 4 + plugins/text-freetype2/text-freetype2.h | 2 + plugins/vlc-video/data/locale/ar-SA.ini | 2 + plugins/vlc-video/data/locale/bg-BG.ini | 15 + plugins/vlc-video/data/locale/ca-ES.ini | 3 + plugins/vlc-video/data/locale/cs-CZ.ini | 3 + plugins/vlc-video/data/locale/da-DK.ini | 7 +- plugins/vlc-video/data/locale/de-DE.ini | 11 +- plugins/vlc-video/data/locale/en-US.ini | 3 + plugins/vlc-video/data/locale/es-ES.ini | 3 + plugins/vlc-video/data/locale/eu-ES.ini | 3 + plugins/vlc-video/data/locale/fa-IR.ini | 14 + plugins/vlc-video/data/locale/fi-FI.ini | 3 + plugins/vlc-video/data/locale/fr-FR.ini | 11 +- plugins/vlc-video/data/locale/he-IL.ini | 2 +- plugins/vlc-video/data/locale/hu-HU.ini | 3 + plugins/vlc-video/data/locale/it-IT.ini | 19 +- plugins/vlc-video/data/locale/ja-JP.ini | 3 + plugins/vlc-video/data/locale/ka-GE.ini | 3 + plugins/vlc-video/data/locale/ko-KR.ini | 3 + plugins/vlc-video/data/locale/nb-NO.ini | 3 + plugins/vlc-video/data/locale/nl-NL.ini | 3 + plugins/vlc-video/data/locale/pl-PL.ini | 3 + plugins/vlc-video/data/locale/pt-BR.ini | 3 + plugins/vlc-video/data/locale/ro-RO.ini | 13 + plugins/vlc-video/data/locale/ru-RU.ini | 3 + plugins/vlc-video/data/locale/sk-SK.ini | 5 +- plugins/vlc-video/data/locale/sr-CS.ini | 7 + plugins/vlc-video/data/locale/sr-SP.ini | 7 + plugins/vlc-video/data/locale/sv-SE.ini | 3 + plugins/vlc-video/data/locale/tr-TR.ini | 3 + plugins/vlc-video/data/locale/uk-UA.ini | 3 + plugins/vlc-video/data/locale/zh-CN.ini | 3 + plugins/vlc-video/data/locale/zh-TW.ini | 3 + plugins/vlc-video/vlc-video-plugin.c | 4 + plugins/vlc-video/vlc-video-source.c | 48 +- 1017 files changed, 37232 insertions(+), 11111 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100755 CI/install-dependencies-linux-ubuntu16.sh create mode 100644 CI/install-qt-win.cmd create mode 100755 CI/install-script-linux.sh create mode 100644 CI/install-script-win.cmd create mode 100644 COMMITMENT create mode 100644 UI/auth-base.cpp create mode 100644 UI/auth-base.hpp create mode 100644 UI/auth-mixer.cpp create mode 100644 UI/auth-mixer.hpp create mode 100644 UI/auth-oauth.cpp create mode 100644 UI/auth-oauth.hpp create mode 100644 UI/auth-restream.cpp create mode 100644 UI/auth-restream.hpp create mode 100644 UI/auth-twitch.cpp create mode 100644 UI/auth-twitch.hpp create mode 100644 UI/balance-slider.hpp create mode 100644 UI/clickable-label.hpp create mode 100644 UI/combobox-ignorewheel.cpp create mode 100644 UI/combobox-ignorewheel.hpp create mode 100644 UI/data/images/overflow.png create mode 100644 UI/data/locale/ku-TR.ini create mode 100644 UI/data/locale/mn-MN.ini create mode 100644 UI/data/locale/pa-IN.ini delete mode 100644 UI/data/themes/Acri/cogwheel.png delete mode 100644 UI/data/themes/Acri/down_arrow.png delete mode 100644 UI/data/themes/Acri/minus.png delete mode 100644 UI/data/themes/Acri/mute.png delete mode 100644 UI/data/themes/Acri/plus.png delete mode 100644 UI/data/themes/Acri/unmute.png delete mode 100644 UI/data/themes/Acri/up_arrow.png delete mode 100644 UI/data/themes/Acri/updown.png delete mode 100644 UI/data/themes/Dark/cogwheel.png delete mode 100644 UI/data/themes/Dark/collapse.png create mode 100644 UI/data/themes/Dark/down.svg delete mode 100644 UI/data/themes/Dark/down_arrow.png delete mode 100644 UI/data/themes/Dark/expand.png create mode 100644 UI/data/themes/Dark/expand.svg create mode 100644 UI/data/themes/Dark/locked.svg delete mode 100644 UI/data/themes/Dark/minus.png create mode 100644 UI/data/themes/Dark/minus.svg delete mode 100644 UI/data/themes/Dark/mute.png create mode 100644 UI/data/themes/Dark/mute.svg create mode 100644 UI/data/themes/Dark/no_sources.svg delete mode 100644 UI/data/themes/Dark/plus.png create mode 100644 UI/data/themes/Dark/plus.svg delete mode 100644 UI/data/themes/Dark/refresh.png create mode 100644 UI/data/themes/Dark/refresh.svg create mode 100644 UI/data/themes/Dark/revert.svg create mode 100644 UI/data/themes/Dark/settings/advanced.svg create mode 100644 UI/data/themes/Dark/settings/audio.svg create mode 100644 UI/data/themes/Dark/settings/general.svg create mode 100644 UI/data/themes/Dark/settings/hotkeys.svg create mode 100644 UI/data/themes/Dark/settings/output.svg create mode 100644 UI/data/themes/Dark/settings/stream.svg create mode 100644 UI/data/themes/Dark/settings/video.svg create mode 100644 UI/data/themes/Dark/trash.svg delete mode 100644 UI/data/themes/Dark/unmute.png create mode 100644 UI/data/themes/Dark/up.svg delete mode 100644 UI/data/themes/Dark/up_arrow.png delete mode 100644 UI/data/themes/Dark/updown.png create mode 100644 UI/data/themes/Dark/updown.svg create mode 100644 UI/data/themes/Dark/visible.svg delete mode 100644 UI/data/themes/Default.qss create mode 100644 UI/data/themes/System.qss create mode 100644 UI/forms/OBSAbout.ui delete mode 100644 UI/forms/OBSLicenseAgreement.ui delete mode 100644 UI/forms/images/add.png delete mode 100644 UI/forms/images/collapse.png delete mode 100644 UI/forms/images/configuration21_16.png delete mode 100644 UI/forms/images/down.png create mode 100644 UI/forms/images/down.svg delete mode 100644 UI/forms/images/editscene.png delete mode 100644 UI/forms/images/expand.png create mode 100644 UI/forms/images/expand.svg create mode 100644 UI/forms/images/invisible.svg delete mode 100644 UI/forms/images/invisible_mask.png delete mode 100644 UI/forms/images/list_remove.png delete mode 100644 UI/forms/images/live.png create mode 100644 UI/forms/images/locked.svg delete mode 100644 UI/forms/images/locked_mask.png create mode 100644 UI/forms/images/minus.svg delete mode 100644 UI/forms/images/mute.png create mode 100644 UI/forms/images/mute.svg create mode 100644 UI/forms/images/no_sources.svg create mode 100644 UI/forms/images/plus.svg delete mode 100644 UI/forms/images/properties.png delete mode 100644 UI/forms/images/refresh.png create mode 100644 UI/forms/images/refresh.svg create mode 100644 UI/forms/images/revert.svg delete mode 100644 UI/forms/images/settings/advanced.png create mode 100644 UI/forms/images/settings/advanced.svg delete mode 100644 UI/forms/images/settings/applications-system-2.png create mode 100644 UI/forms/images/settings/audio.svg delete mode 100644 UI/forms/images/settings/decibel_audio_player.png create mode 100644 UI/forms/images/settings/general.svg create mode 100644 UI/forms/images/settings/hotkeys.svg delete mode 100644 UI/forms/images/settings/network-bluetooth.png delete mode 100644 UI/forms/images/settings/network.png create mode 100644 UI/forms/images/settings/output.svg delete mode 100644 UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png delete mode 100644 UI/forms/images/settings/preferences-system-network-3.png create mode 100644 UI/forms/images/settings/stream.svg delete mode 100644 UI/forms/images/settings/system-settings-3.png delete mode 100644 UI/forms/images/settings/video-display-3.png create mode 100644 UI/forms/images/settings/video.svg delete mode 100644 UI/forms/images/sound.ico delete mode 100644 UI/forms/images/sound_muted.ico create mode 100644 UI/forms/images/trash.svg create mode 100644 UI/forms/images/unlocked.svg delete mode 100644 UI/forms/images/unlocked_mask.png delete mode 100644 UI/forms/images/unmute.png delete mode 100644 UI/forms/images/up.png create mode 100644 UI/forms/images/up.svg create mode 100644 UI/forms/images/visible.svg delete mode 100644 UI/forms/images/visible_mask.png create mode 100644 UI/frontend-plugins/decklink-output-ui/CMakeLists.txt create mode 100644 UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.cpp create mode 100644 UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h create mode 100644 UI/frontend-plugins/decklink-output-ui/data/.keepme create mode 100644 UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp create mode 100644 UI/frontend-plugins/decklink-output-ui/decklink-ui-main.h create mode 100644 UI/frontend-plugins/decklink-output-ui/forms/output.ui create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/ar-SA.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/bg-BG.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/fa-IR.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/mn-MN.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/ta-IN.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini delete mode 100644 UI/locked-checkbox.cpp create mode 100644 UI/obf.c create mode 100644 UI/obf.h delete mode 100644 UI/obs.rc create mode 100644 UI/obs.rc.in create mode 100644 UI/slider-ignorewheel.cpp create mode 100644 UI/slider-ignorewheel.hpp delete mode 100644 UI/source-list-widget.cpp delete mode 100644 UI/source-list-widget.hpp create mode 100644 UI/spinbox-ignorewheel.cpp create mode 100644 UI/spinbox-ignorewheel.hpp create mode 100644 UI/ui-config.h.in delete mode 100644 UI/visibility-checkbox.cpp create mode 100644 UI/win-update/updater/updater.manifest create mode 100644 UI/window-basic-about.cpp create mode 100644 UI/window-basic-about.hpp create mode 100644 UI/window-basic-main-browser.cpp create mode 100644 UI/window-basic-settings-stream.cpp create mode 100644 UI/window-dock.cpp create mode 100644 UI/window-dock.hpp delete mode 100644 UI/window-license-agreement.cpp delete mode 100644 UI/window-license-agreement.hpp create mode 100644 UI/xdg-data/CMakeLists.txt create mode 100644 UI/xdg-data/com.obsproject.Studio.appdata.xml.in rename UI/{dist/obs.desktop => xdg-data/com.obsproject.Studio.desktop} (67%) create mode 100644 azure-pipelines.yml create mode 100644 libobs/data/area.effect create mode 100644 libobs/data/repeat.effect create mode 100644 libobs/libobs.pc.in create mode 100644 libobs/obs-video-gpu-encode.c create mode 100644 libobs/util/apple/cfstring-utils.h create mode 100644 plugins/coreaudio-encoder/data/locale/fa-IR.ini create mode 100644 plugins/coreaudio-encoder/data/locale/mn-MN.ini create mode 100644 plugins/coreaudio-encoder/data/locale/ur-PK.ini create mode 100644 plugins/decklink/DecklinkBase.cpp create mode 100644 plugins/decklink/DecklinkBase.h rename plugins/decklink/{decklink.cpp => DecklinkInput.cpp} (57%) rename plugins/decklink/{decklink.hpp => DecklinkInput.hpp} (51%) create mode 100644 plugins/decklink/DecklinkOutput.cpp create mode 100644 plugins/decklink/DecklinkOutput.hpp create mode 100644 plugins/decklink/const.h create mode 100644 plugins/decklink/data/locale/fa-IR.ini create mode 100644 plugins/decklink/data/locale/mn-MN.ini create mode 100644 plugins/decklink/decklink-devices.cpp create mode 100644 plugins/decklink/decklink-devices.hpp create mode 100644 plugins/decklink/decklink-output.cpp create mode 100644 plugins/decklink/decklink-source.cpp create mode 100644 plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h create mode 100644 plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp create mode 100644 plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_9.h create mode 100644 plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h create mode 100644 plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp create mode 100644 plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_9.h create mode 100644 plugins/decklink/util.cpp create mode 100644 plugins/decklink/util.hpp create mode 100644 plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming_v10_8.idl create mode 100644 plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_8.idl create mode 100644 plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_9.idl create mode 100644 plugins/image-source/data/locale/fa-IR.ini create mode 100644 plugins/image-source/data/locale/mn-MN.ini create mode 100644 plugins/linux-alsa/data/locale/fa-IR.ini create mode 100644 plugins/linux-capture/data/locale/fa-IR.ini create mode 100644 plugins/linux-jack/data/locale/fa-IR.ini create mode 100644 plugins/linux-jack/data/locale/ur-PK.ini create mode 100644 plugins/linux-pulseaudio/data/locale/fa-IR.ini create mode 100644 plugins/linux-v4l2/data/locale/fa-IR.ini create mode 100644 plugins/mac-avcapture/data/locale/fa-IR.ini create mode 100644 plugins/mac-capture/data/locale/fa-IR.ini delete mode 100644 plugins/mac-capture/mac-helpers.h create mode 100644 plugins/mac-syphon/data/locale/fa-IR.ini create mode 100644 plugins/mac-vth264/data/locale/fa-IR.ini create mode 100644 plugins/obs-ffmpeg/data/locale/fa-IR.ini create mode 100644 plugins/obs-ffmpeg/jim-nvenc-helpers.c create mode 100644 plugins/obs-ffmpeg/jim-nvenc.c create mode 100644 plugins/obs-ffmpeg/jim-nvenc.h create mode 100644 plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c create mode 100644 plugins/obs-filters/data/locale/bg-BG.ini create mode 100644 plugins/obs-filters/data/locale/fa-IR.ini create mode 100644 plugins/obs-filters/data/luma_key_filter.effect create mode 100644 plugins/obs-filters/expander-filter.c create mode 100644 plugins/obs-filters/invert-audio-polarity.c create mode 100644 plugins/obs-filters/limiter-filter.c create mode 100644 plugins/obs-filters/luma-key-filter.c create mode 100644 plugins/obs-outputs/data/locale/bg-BG.ini create mode 100644 plugins/obs-outputs/data/locale/mn-MN.ini create mode 100644 plugins/obs-text/data/locale/ar-SA.ini create mode 100644 plugins/obs-text/data/locale/fa-IR.ini create mode 100644 plugins/obs-text/data/locale/mn-MN.ini delete mode 100644 plugins/obs-transitions/data/premultiplied.inc create mode 100644 plugins/obs-x264/data/locale/fa-IR.ini create mode 100644 plugins/obs-x264/data/locale/mn-MN.ini create mode 100644 plugins/rtmp-services/data/locale/mn-MN.ini create mode 100644 plugins/text-freetype2/data/locale/bg-BG.ini create mode 100644 plugins/text-freetype2/data/locale/fa-IR.ini create mode 100644 plugins/text-freetype2/data/locale/mn-MN.ini create mode 100644 plugins/vlc-video/data/locale/bg-BG.ini create mode 100644 plugins/vlc-video/data/locale/fa-IR.ini diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..4b0e759 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +open_collective: obsproject +patreon: obsproject diff --git a/.gitignore b/.gitignore index 19fb10b..f78f47a 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ x64/ ipch/ GeneratedFiles/ .moc/ +/UI/obs.rc /other/ diff --git a/.gitmodules b/.gitmodules index 498a346..fa1c4f1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/palana/Syphon-Framework.git [submodule "plugins/enc-amf"] path = plugins/enc-amf - url = https://github.com/Xaymar/obs-studio_amf-encoder-plugin.git + url = https://github.com/obsproject/obs-amd-encoder.git [submodule "plugins/obs-browser"] path = plugins/obs-browser url = https://github.com/obsproject/obs-browser.git diff --git a/.travis.yml b/.travis.yml index 9100196..939917e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ matrix: before_deploy: "./CI/before-deploy-osx.sh" - os: linux - dist: trusty + dist: xenial sudo: required before_install: "./CI/install-dependencies-linux.sh" before_script: "./CI/before-script-linux.sh" @@ -50,21 +50,10 @@ deploy: condition: "$TRAVIS_OS_NAME = osx" 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: webhooks: urls: - - secure: T5RBY818nO40nr5eC8pdrCfAdQKGkjQdbyYw7mfFrhxWxgt/U5tyKXpX0l9zNGfobS0SnLSqF71OrfW04V97oijXx3q5Y24xV6mSrlLQZOq19+XvGp82LDpkVd4yi2N0kBYpoANB9Pkof4jWT/rKfdQCQttluOLjgr5SM0uWHRg= - secure: EVI2cu5OnNxVTl4jdVppps7O869gGN1PDcSi8fqq/HJVM5kif8iDe4wCrIKv6yWrK3dSNwRgBAwpcPZglRJnKRh23PdFoCdnTjgzBQlmjUR6BYlunQvoKR9mVX6AdT8zrFDgmtC4aOtGD2paptpqt+Equo25KrLwv+qOHJOTrSQ= - on_success: change + on_success: always on_failure: always diff --git a/AUTHORS b/AUTHORS index a2f5192..e0167d4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,513 +1,939 @@ +Original Author: Hugh Bailey ("Jim") + Contributors are sorted by their amount of commits / translated strings. Contributors: Jim Palana fryshorts +R1CH BtbN -John Bradley +DDRBoxman Gol-D-Ace -Richard Stanway -Colin Edwards +Shaolin +kc5nra +Ryan Foster Zachary Lund -Michael Fabian Dirks -Martell Malone cg2121 +Xaymar +SuslikV +Rodney Christoph Hohmann +pkv +Martell Malone HomeWorld dodgepong -Ryan Foster -derrod +Alex Anderson +juvester +Dmitry-Me +Fenrir Radzaquiel Socapex -juvester -Skyler Lipthay -SuslikV -Arkkis Cephas Reis -GoaLitiuM +Dillon Pentz +Kurt Kartaltepe +Luke Yelavich +mntone +sorayuki +Andrew Surzhynskyi +Skyler Lipthay +Arkkis Danni +GoaLitiuM +Hunter L. Allen +Marvin Scholz +Michel Snippe +Quinn Damerell +Tjienta Vara +craftwar +Clayton Groeneveld +Ilya Melamed Jess Mayo +Jimi Huotari Kris Moore +Alexandre Vicenzi Carl Fürstenberg +Kilian von Pflugk Anry CoDEmanX +H4ndy Ján Mlynek -Kurt Kartaltepe -Manuel Kroeber -sorayuki -Alexandre Vicenzi -Andrew Surzhynskyi +nleseul Benjamin Klettbach -bl +Bird, Christopher Bl00drav3n Blackhive Charles Ray Shisler III +Daniel Lopez +David Cooper +Exeldro Jeremiah Senkpiel -John R. Bradley -Kilian von Pflugk -mntone +Joseph El-Khouri +Matt WizardCM +Palakis +Philip Haynes +Robin Hielscher Serge Paquet +Timo Gurr +bl shiina424 shousa -Timo Gurr -adray +vokama +Alexander Schittler +Andreas Reischuck Andrei Nistor Azat Khasanshin -Ben Torell -bootkiller Brian S. Stephan -e00E +Chaturbate +Dead133 +DungFu Eric Bataille -Joseph El-Khouri -jpk +Igor Bochkariov +Justin Love Lexsus Lionheart Zhang +Lucian Poston +Matt Morrissette +Matthew McNamara +Michael Goulet +Skid-Inc +Taylor Blau +Tristan Matthews +Warchamp7 +Warren Turkal +adray +bootkiller +e00E +jpk paibox -Robin Hielscher skwerlman taesheren -Take Vos -Taylor Blau -Warren Turkal yogpstop Aarni Koskela +Abelardo E. Mendoza Aesen Vismea +Aidan Epstein Akagi201 +Alcaros Alexander Uhlmann Alexandre Biny -Andreas Reischuck +Alexandre Paré +Ali Kaviani Anthony Catel Anthony Super +Artem Merkulov Asgeir Mortensen Autumin Aydin Akan +Bas van Meel +Bazhenoff +Beau Russell +Ben Krueger +Ben Stahl +Benjamin Schubert Bernd Buschinski -bla -boombatower +Bjorn +Bongalive +C. Daniel Sanchez R Caitlin Potter Caleb Anderson CallumHoward Cam +Can Bal +Cheeseness CommanderRoot Copy Liu -craftwar Cray Elliott -cryptonaut Dan Dascalescu -David Cooper David McMackins II -dennis Derrick Lambert +Dmitry Odintsov Emil Sayahi EpicCoder Ethan Lee +Fazrad Karkhani Frank Gehann +Fred Emmott +Garrett Giorgio Pellero +Gregory Luneau Grigorii Chirkov Guillermo A. Amaral Gökberk Yaltıraklı Haden F +Han-Tai Chen +Hicham LEMGHARI Iblis Lin -Igor Bochkariov +J Lynn +J.D. Purcell Jake Probst +James Hurley Jamy Timmermans +JetMeta Jimmy Berry Jkoan -Joel Bethke +John Cheng +Jonathan +Joshua Rowe Julian Miller Kazuki Oishi -ka’imi Kevin Kevin Tardif Lasse Dalegaard -lemmi Lioncash -Lucian Poston Lukas Monka -Luke Yelavich -mape +Makeenon Marc Chambers Mark Vaughn Mathias Panzenböck -Matthew McNamara +Matthew Szatmary MedicMomcilo -michael bishop +Micah Elizabeth Scott Michael Hoang Momcilo Medic -nd +Murnux Nicolas F Night +Olivier Humbert Olle Kelderman -Palakis -pantonvich -partouf Patrick Ancillotti Peter SZTANOJEV -pipll -raincomplex -repeat +Philip Loche Ricardo Constantino Rodrigo Ipince -rpslack Ryan Sullivan -sam8641 +SammyJames +Serge Martial Nguetta Seth Murphy Seung-Woo Kim Shamun -Shaolin Simon +Sophie Hamilton SoraYuki +Steve Wills +Tatsuyuki Ishi +Taylor Teemu Kauhanen -thekrzos +Terje Gundersen +Thomas Crider +Thomas De Schampheleire Thomas McGrew +Thomas Schnitzler +Tom Peeters +Tom Pichler TotalCaesar659 -trwnh -vic -vividnightmare -VodBox -wayne wang +Wahaj Dar Weikardzaena Will Jamieson William Casarin +Wouter Younes SERRAJ +Yvo Ziemas +adocilesloth +astudios123 +bla +bluekirby0 +boombatower +bth +chinasarft +comex +cryptonaut +dennis +eastkiki +fefine +jamacanbacn +jeremiah +ka’imi +lemmi +mape +mhabedinpour +michael bishop +nd +notmark +pantonvich +partouf +pipll +pkuznetsov +probonopd +raincomplex +repeat +rkfg +rpslack +sam8641 +thekrzos +trwnh +vic +vividnightmare +wangrui05 +wayne wang -Translations: -Arabic - Abdullah AL-Qahtani (Za7ef_SA) - majdcomp - ZILZAL - BWU Wheelman (Wheelman) - Saleh Luxmaroc (salehoukiki) - Gol D. Ace (goldace) - معتصم دعنا (rozana-media) - chaironeko - dodgepong - FC Barcelona HD (kurdnews) -Basque - Alexander Gabilondo (alexgabi) - Xabier Aramendi (azpidatziak) - Osoitz - txaro - etxondoko - Gol D. Ace (goldace) - dodgepong -Bengali - shamuntohamd -Bulgarian - kalmarin - Seyhan Halil (yildirim17) - Viktor Kitov (viktorkitov) - Stanislav_Evtimov - Ivan (SKDown) -Catalan - Jaime Muñoz Martín (jmmartin_5) - jmontane - Nil Campamà (Soifam) - Aleix Vidal i Gaya (leixet) - Gol D. Ace (goldace) -Chinese Simplified - Bob Liu (Akagi201) - PabloLiu (719018105) - Sasasu - cai_miao - David Kuo (s50407s) - Boyuan Yang (073plan) - 鲜童 (xiananjyzy) - copyliu - wwj402_github - Bing Feng (fengbing123) - Hexcolyte - Gol D. Ace (goldace) - dodgepong - WaterOtaku -Chinese Traditional - TzeKei Lee (chikei) - dodgepong - Julian_Lai - Chien-Yu Lin (u900011) - David Kuo (s50407s) - You-Ruei Tzeng (e222et) - 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 - tomoe-musashi -Croatian - medicmomcilo - Gol D. Ace (goldace) - dodgepong -Czech - Jirka 'Venty' Michel (VentyCZ) - Sawanyo - dodgepong - Kiznoh -Danish - Jens Hyllegaard (Hyllegaard) - NCAA - Anders G. Jørgensen (spirit55555) - MaltahlGaming (maltahlgaming) - Anders Urban (minikaliffen) - Christian Henriksen (cnhenriksen) - Gol D. Ace (goldace) - Johan Keller Jensen (JKeller) -Dutch - Eric Bataille (ThoNohT) - Michel Snippe (michelsnippe) - 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) - Jarska - dodgepong - Obama (Obama44) - Gol D. Ace (goldace) - chaironeko -French - radzaquiel - Nunzio Conte (nunzioconte54) - 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 - Xesús M. Mosquera Carregal (xesusmosquera) - Gol D. Ace (goldace) - chaironeko -German - Gol D. Ace (goldace) - Michael Fabian Dirks (Xaymar) - dodgepong - Benjamin Klettbach (benklett) - Sven Kirschbaum (fallobst22) (fallobst22) - random31415 - Palana - Dennis Giebert (Isegrim) (isegrimderwolf) - Jonathan (macburgerjunior) - Robin Hielscher (Jack0r) - Tim (robske_110) (robske110) - BoJustus - Jonas Otto (jottosmail) - mdod - Prince_of_Raop - Tiim - WurstOnAir -Greek - Mepharees - Tasos Sahanidis (tatokis) - Alex Kalles (alexakis1997) - iosifidis - chaironeko - dodgepong -Hebrew - amirsher - Chemi - epic_ziver_D -Hindi - shamuntohamd -Hungarian - Gige - Adam Liszkai (adamos42) - dodgepong - Gol D. Ace (goldace) -Italian - LordShadow95 - Marocco2 - smart2128 - dodgepong - Edoardo Macrì (edomacri) - Edoardo “OfficialDJMela” Macrì (agersforum) - ScemEnzo - Gol D. Ace (goldace) - Fisherozzo - Sergio Beneduce (sbeneduce) - SkyLion -Japanese - shousa - Kenta (kenta0644) - dodgepong - chaironeko -Korean - Wonjoo Noh (ynetwork) - Gol D. Ace (goldace) - antome -Lithuanian - Justas Vilimas (tyntas) -Malay - amsyar ZeRo (amsyarminer555) - dodgepong -Norwegian Bokmal - Taesh (magnusmbratteng) - dodgepong - mgKaiztra - Tommy (nwgat) - Oddbjørn Grytdal (Fooshi) - Decicus - Sander Skjegstad (r530er) - Legend27 - Gol D. Ace (goldace) - areedw - chaironeko - Mats Andreassen (MatsA) -Pirate English - Matthew Hatcher (MatthewSH) - jkcoaster - Charlie W. (wallichc) - iltrof - chaironeko - Gol D. Ace (goldace) -Polish - grocal - Michał Durak (micechal) - Damian Korcz (damikiller) - opl - Gol D. Ace (goldace) - dodgepong - Mateusz (Silesianek) - Michał Lewczak (michal200507) -Portuguese - André Biscaia (LazP) - dodgepong - joaoboia - Gol D. Ace (goldace) -Portuguese, Brazilian - Shaolin (admshao) - Ramon Mendes (rbrgameplays) - Fabio Madia (Shaolin) - TFSThiagoBR98 - CaioWzy - clr0dr1g - mizifih - aalonsomb - André Gama (ToeOficial) - Gol D. Ace (goldace) - ThisGuy - dodgepong -Romanian - Cristian Silaghi (stelistcristi) - banrek - Hisashi - Gol D. Ace (goldace) - chaironeko - dodgepong -Russian - iltrof - Alek Nirov (dectanova) - VNGXR - dodgepong - Pavel (Shevalie) - Maxim Gribanov (MaximGribanov) - Bugo - Andy (anry025) - fromgate - Gol D. Ace (goldace) - Andrei Stepanov (adem4ik) - Vlad (KoTmaxHo) - Mixaill - Sergei Fug1t1v3 (fug) - Walt Gee (vovanych) - Sigge Stjärnholm (Kladdy) - Yuri Mihaqlov (yurijmi) -Serbian (Cyrillic) - medicmomcilo - Gol D. Ace (goldace) -Serbian (Latin) - medicmomcilo - Gol D. Ace (goldace) - dodgepong -Slovak - Ján M (longmoped) - Anton Lokaj (anlo) - LoLLy Nka (lollynka279) - Gol D. Ace (goldace) -Slovenian - kristjan.krusic (krusic22) - Gol D. Ace (goldace) - ArcaneWater - dodgepong -Spanish - Roberto Lorenzo (HonzoNebro) - 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) - chaironeko - dodgepong - Rodrigo Ipince (ipince) - Rubén Pérez (RixzZ) -Swedish - Anton R (FirePhoenix) - Sigge Stjärnholm (Kladdy) - Laccy IEST (Laccy) - Olle Dahström (odahlstrom) - Gustav Ekner (ekner) - Gol D. Ace (goldace) - Henrik Mattsson-Mårn (rchk) - chaironeko - Jonas Svensson (jonassanojj99) -Tamil - Kolappan Nathan (kolappannathan) -Thai - sakuhanachan* (sakuhanachanloli) - 盛凤阁 (execzero) - nongnoobjung (kitcharuk_4) - dodgepong - Gol D. Ace (goldace) -Turkish - Ali Kömesöğütlü (Mobile46) (byzlo685) - omer.karagoz (mrkaragoz) - monolifed - Cemal Dursun (cmldrs) - Savas Tokmak (Laserist) - Murat Karagöz (anemon_1994) - gecebekcisi1 - Gol D. Ace (goldace) - Hydroboost - mustafaa -Ukrainian - SuslikV - Юрій (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 +Translators: +Afrikaans: + - Samuel Nthoroane (Samuel_Nthoroane) +Albanian: + - Albin Pllana (albinnpllanaa) + - Aredio Vani (aredio.vani) +Arabic: + - ZILZAL + - Abdullah AL-Qahtani (Za7ef_SA) + - majdcomp + - aaakjt + - Gol D. Ace (goldace) + - BWU Wheelman (Wheelman) + - Saleh Luxmaroc (salehoukiki) + - معتصم دعنا (rozana-media) + - Hani Sweileh (hno.sweileh) + - Mustafa2018 + - Tensai + - Vainock (ivo.lemmert) + - Mnsor The-Ghost (mnsor1722011) + - FC Barcelona HD (kurdnews) + - Ndalabo Taema (hake_bsowq) + - dodgepong + - chaironeko +Azerbaijani: + - Shahin Farzaliyev (Khan27) +Basque: + - Alexander Gabilondo (alexgabi) + - Osoitz (Osoitz) + - Gol D. Ace (goldace) + - txaro (txaro) + - EG Gamer (eggamer131) + - etxondoko + - Vainock (ivo.lemmert) + - dodgepong +Bengali: + - shamuntohamd + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - Nurul Huda (nurulhuda859) + - Md Arafat (zonoiko) +Bulgarian: + - j3dy + - kalmarin + - Zakxaev68 + - Seyhan Halil (yildirim17) + - Dremski + - Martin Georgiev (DivideByNone) + - Viktor Kitov (viktorkitov) + - Stanislav_Evtimov + - Stoyan Stoyanov (sstoyanov) + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - Ivan (SKDown) + - Warchamp7 +Catalan: + - Jaime Muñoz Martín (jmmartin_5) + - jmontane + - Nil Campamà (Soifam) + - Benet R. i Camps (BennyBeat) (BennyBeat) + - Aleix Vidal i Gaya (leixet) + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - Pere O. (gotrunks) + - Aniol Pagès (aniolpages) +Chinese Simplified: + - Bob Liu (Akagi201) + - AlexGuo1998 (AlexGuo1998) + - wwj402_github + - PabloLiu (719018105) + - Gol D. Ace (goldace) + - Licardo (Licardo) + - AthlonHD (AthlonHD) + - Hexcolyte (Hexcolyte) + - Sasasu (Sasasu) + - cai_miao + - Boyuan Yang (073plan) + - David Kuo (s50407s) + - 鲜童 (xiananjyzy) + - Opportunity (OpportunityLiu) + - Forbidden (cptbl00dra1n) + - yunluzhang + - Inku Xuan (inkuxuan) + - copyliu + - Bing Feng (fengbing123) + - pan93412 + - WeiYuanStudio + - 科技小白堂 (lipeng0820) + - Richard Stanway (r1ch) + - WaterOtaku + - dodgepong + - MarsYoung + - sorayuki + - cylin + - Vainock (ivo.lemmert) + - 赵杭灵 (h1679083640) + - wordlessWind (wordlesswind) + - OYYZ + - FaZe Fakay (fazefakay) + - Bob Wei (BobWaver) +Chinese Traditional: + - TzeKei Lee (chikei) + - dodgepong + - Julian_Lai + - Chien-Yu Lin (u900011) + - pan93412 + - David Kuo (s50407s) + - Michael Yeh (hinet60613) + - You-Ruei Tzeng (e222et) + - myjourney in Steemit (myjourney) + - craftwar + - Gol D. Ace (goldace) + - Inndy.Lin (inndy) + - Han-Jen Cheng (notexist) + - Watson Tsai (ashaneba) + - Meng Hao Li (GazCore) + - abc0922001 + - Thomas (thomassth) + - louis921222 + - ak-47root + - Jimmy Huang (f56112000) + - cai_miao + - Append Huang (append) + - 曹恩逢 (nelson22768384) + - Vainock (ivo.lemmert) + - xixiaofan (Hs0) + - tomoe-musashi + - FaZe Fakay (fazefakay) + - chaironeko + - JackYeah +Croatian: + - medicmomcilo + - srdjan_m + - Runicar (dajtisina) + - OfficialwobY + - Gol D. Ace (goldace) + - Wildrage + - Maky (the.real.maky) + - Vainock (ivo.lemmert) + - dodgepong +Czech: + - Jirka 'Venty' Michel (VentyCZ) + - Kryštof Černý (cleverline1mc) + - Sawanyo + - Kiznoh + - Gol D. Ace (goldace) + - Erik Bročko (ericek111) + - Vainock (ivo.lemmert) + - dodgepong +Danish: + - NCAA (NCAA) + - Jens Hyllegaard (Hyllegaard) + - Anders G. Jørgensen (spirit55555) + - MaltahlGaming (maltahlgaming) + - Gol D. Ace (goldace) + - Anders Urban (minikaliffen) + - Richard Stanway (r1ch) + - Johan Keller Jensen (JKeller) + - Christian Henriksen (cnhenriksen) + - Vainock (ivo.lemmert) + - Marque Ziqulr (lugtelort) + - HeroGamers (Fido2603) + - Nicolai (Nicolai9852) + - Daniel Aundal (aundal) +Dutch: + - Eric Bataille (ThoNohT) + - Michel Snippe (michelsnippe) + - Greendweller + - Coen (Trigstur) + - exeldro (exeldro) + - Nicole (Dutchess_Nicole) + - robbert0891 (robbertoorschot38) + - Danny (Dkamps18) + - Gol D. Ace (goldace) + - Albakham (albakham) + - Jasper J (JassieJ) + - Bond-009 + - b__dm + - F_Producktions + - Harm van den Hoek (harm27) + - markpc + - JorRy + - Julian Meijboom (julianmeijboom) + - Bo Alsemgeest (bo.alsemgeest.wausie) + - andymidside + - JustMaffie (JustMaffie) + - RyyzQ + - Vainock (ivo.lemmert) +Estonian: + - MartinEwing + - AndresTraks + - Gol D. Ace (goldace) +Filipino: + - dwaeji-aizelle (dwaeji-aizelle) + - jermel + - nyakayed (nyakayed) + - Loyd Stephen Jayme (loydjayme25) + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - Raylir +Finnish: + - Arkkis (j) + - Jarska + - dodgepong + - Pyscowicz (Pyscowicz) + - Gol D. Ace (goldace) + - Obama (Obama44) + - Tero Keso (tero.keso) + - Vainock (ivo.lemmert) + - chaironeko + - Ville Närhi (daimaah) +French: + - radzaquiel + - pkviet + - Stéphane Lepin (Palakis) + - Yberion + - Tocram2 (tocram2) + - DoK_- + - BoboopTeam + - Léo (leeo97one) + - Benjamin Cambour (lesinfox) + - RisedSky (THEMINECRAFT951) + - DarkInFire + - Youtubeur FR│Giaco35 (Giaco35) + - Pikana (Pikana) + - steve_fr + - Ben Turner (ben-turner) + - Deski_ (Deski_) + - DarckCrystale + - Gol D. Ace (goldace) + - Grisou2907 + - Theguiguix + - Gabriel Dugny (Gabigabigo) + - McGuygnol + - Yolopix + - GANGAT Naeem (zboggum) + - kyllian (tardigradeus) + - BaguetteDePain_ + - Richard Stanway (r1ch) + - Mathieu Hautebas (matteyeux) + - Zalki + - dodgepong + - 🌠 DarK | #Hello 🌠 (DarKTV_FR) + - Vainock (ivo.lemmert) + - illusdidi + - Adrien “GameZone Tv” de Decker (redcraft007) + - Warchamp7 + - Albakham (albakham) + - SkylixX + - chaironeko + - Camille Nury (kamsdu30) + - Alexis Brandner (Alexinfos) + - tburette +Galician: + - Xesús M. Mosquera Carregal (xesusmosquera) + - Gol D. Ace (goldace) + - chaironeko + - Vainock (ivo.lemmert) +Georgian: + - georgianizator + - EduCare Razmik Badalyan (badalyanrazmik) + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) +German: + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - Michael Fabian Dirks (Xaymar) + - dodgepong + - Dennis Giebert (Isegrim) (isegrimderwolf) + - Benjamin Klettbach (benklett) + - Gregor Bigalke (gregtcltk) + - Sven Kirschbaum (fallobst22) (fallobst22) + - Manuel (ElectronicWar) + - eZ_KrieG3R | Der Fabse (fabian.schwarz.26.06.1998) + - Palana + - Tim (robske_110) (robske110) + - Jonas Otto (jottosmail) + - mdod + - Jonathan (macburgerjunior) + - Prince_of_Raop + - Enderdrache LP (enderdrachelp) + - Robin Hielscher (Jack0r) + - lebaston100.de + - Richard Stanway (r1ch) + - Patrick Frings (Ragnos) + - WurstOnAir + - BoJustus + - AndreLeonardo (andreleonardoyt) + - Tiim +Greek: + - Scourgemcdak + - Mepharees + - Katerina (katerinaramm) + - Tasos Sahanidis (tatokis) + - George T. (tzikas97) + - Alex Kalles (alexakis1997) + - iosifidis + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - dodgepong + - Themis T. (Deminho) + - chaironeko +Hebrew: + - amirsher + - lonelywolf11 + - Chemi (Chemi) + - epic_ziver_D (epic_ziver_D) + - Uri Ben Yossef (uribenyossef) + - Gol D. Ace (goldace) + - Light1c3 + - ghsi + - yair (5shekel) + - TheOver (upmeboost) + - djsavta + - אפיק רוזנר (afikr333) + - Vainock (ivo.lemmert) + - Tal Machani (talmachani) +Hindi: + - SneakyFish5 + - shamuntohamd +Hungarian: + - Gige + - Gol D. Ace (goldace) + - Adam Liszkai (adamos42) + - abydosan (abydoshun) + - dodgepong + - vargag159 + - Vainock (ivo.lemmert) +Italian: + - Manfre#9262 (manfre) + - LordShadow95 + - Marocco2 + - smart2128 + - Tiwi90 (tiwi90) + - dodgepong + - imcesca + - Michele (ScrappyCocco) + - Edoardo Macrì (edomacri) + - ScemEnzo + - Tommaso Cammelli (tomganguz) (tomganguz) + - Edoardo “OfficialDJMela” Macrì (agersforum) + - Martazza (Martazza) + - Gol D. Ace (goldace) + - gianni morandi (strabbioboy) + - Fisherozzo + - Alessandro Sarto (alesarto03) + - Sergio Beneduce (sbeneduce) + - Albakham (albakham) + - Andrea-M3 + - SkyLion + - Vainock (ivo.lemmert) + - Federico Tensi (habby1337) + - Silpheel + - Alessandro Iepure (alessandro_iepure) + - riccardotornesello +Japanese: + - shousa + - Kenta Takumi (kenta0644) + - dodgepong + - SplatRT + - Gol D. Ace (goldace) + - 神成フィルム (kami00nari) + - ato lash (hal_shu_sato) + - Noi (Noi_noel2647) + - CKK COBALT (CKKCOBALT) + - Yuki Yu (Yukiyu) + - chaironeko + - Bob Liu (Akagi201) + - Alex Shafer (enzanki-ars) + - Vainock (ivo.lemmert) +Khmer: + - បងមាន តែអូន (cheaiphone267) +Korean: + - ynetwork (ynetwork) + - Gol D. Ace (goldace) + - RedditRook + - Jong Kwon Choi (dailypro) + - antome +Kurdish: + - Omer Kurdish (OmerKurd) +Lithuanian: + - Justas Vilimas (tyntas) + - xNaii (lyrikas5) + - Vainock (ivo.lemmert) + - Gol D. Ace (goldace) +Malay: + - amsyar ZeRo (amsyarminer555) + - WeingHong + - Gol D. Ace (goldace) + - dodgepong + - Vainock (ivo.lemmert) +Mongolian: + - begjan + - Vainock (ivo.lemmert) +Nigerian Pidgin: +Norwegian Bokmal: + - Taesh (magnusmbratteng) + - Imre Kristoffer Eilertsen (DandelionSprout) + - dodgepong + - Oddbjørn Grytdal (Fooshi) + - Tommy (nwgat) + - Patrick Williamson (wpatrick59) + - Mats Edvin Aarø (matsedvin) + - mgKaiztra + - Syver Stensholt (sssandum) + - Alex Thomassen (Decicus) + - OsteHovel + - areedw + - Sander Skjegstad (r530er) + - Gol D. Ace (goldace) + - Legend27 + - Vainock (ivo.lemmert) + - chaironeko + - Mats Andreassen (MatsA) +Norwegian Nynorsk: + - Imre Kristoffer Eilertsen (DandelionSprout) +Persian: + - MZ MAXIMUM (mahdigamermax) + - Alireza Firouzi (pikhoshorg) + - peymanr34 +Pirate English: + - Matt Gajownik (WizardCM) + - Uaiquqjwnsns + - Matthew Hatcher (MatthewSH) + - jkcoaster + - Emu-Phoenix + - Coen (Trigstur) + - Charlie W. (wallichc) + - Vainock (ivo.lemmert) + - chaironeko + - Gol D. Ace (goldace) + - ncb +Polish: + - grocal (grocal) + - Michał Durak (micechal) + - The Syntox (TheSyntox) + - Vassamo (jotpl69) + - Damian Korcz (damikiller) + - opl + - Gol D. Ace (goldace) + - Łukasz Wójcik (lwojcik) + - Daniel Wieczorek (Kennyluz) + - Albakham (albakham) + - popek069 + - sebek1pan + - Mateusz (Silesianek) + - Julia Drewniak (ewagsi) + - dodgepong + - Vainock (ivo.lemmert) + - Patryk Kunda (ner.i.ol) + - Michał Lewczak (michal200507) +Portuguese: + - André Biscaia (LazP) + - Ev1lbl0w + - dodgepong + - joaofvieira + - Tomás Antunes (tomasantunes) + - joaoboia + - Albakham (albakham) + - alexandre433 + - Gol D. Ace (goldace) +Portuguese, Brazilian: + - Shaolin (admshao) + - Ramon Mendes (rbrgameplays) + - Fabio Madia (Shaolin) + - TFSThiagoBR98 + - CaioWzy + - clr0dr1g + - aalonsomb + - André Gama (ToeOficial) + - Gol D. Ace (goldace) + - Emanoel Lopes (emanoelopes) + - mizifih + - DevilLorde + - flor.com (florretardada) + - Vainock (ivo.lemmert) + - dodgepong + - Esdras Tarsis (esdrastarsis) + - Ramon Gonzalez (ramon200000) +Punjabi: + - manjotsingh0202 +Romanian: + - Cristian Silaghi (stelistcristi) + - banrek + - Hisashi + - BlakeNowah + - Andrei Ionescu (abcdedjdmddx) + - Victor Paul (corvinpaul) + - Skellytone + - Mihai G (babasghenciu) + - Gol D. Ace (goldace) + - dodgepong + - chaironeko + - Vainock (ivo.lemmert) +Russian: + - Alek Nirov (dectanova) + - Pavel (Shevalie) + - dodgepong + - VNGXR + - Gol D. Ace (goldace) + - Bugo (Bugo) + - Andy (anry025) + - Strange Grey Cat (StrangeGreyCat) + - Yaroslav (MrYadro) + - Andrei Stepanov (adem4ik) + - fromgate (fromgate) + - Vlad (KoTmaxHo) + - Runoff Screen (glebpozbnakov62) + - Mixaill + - Vainock (ivo.lemmert) + - Sergei Fug1t1v3 (fug) + - Walt Gee (vovanych) + - Serge Sklyarov (sergesklyarov) + - Yuri Mihaqlov (yurijmi) + - MUHADDIS MEDIA (muhaddismedia) + - TR1D + - Sigge Stjärnholm (Kladdy) + - SandoBY + - Kuji Kitamura (KujiKita) +Scottish Gaelic: + - GunChleoc + - Vainock (ivo.lemmert) +Serbian (Cyrillic): + - medicmomcilo + - Acamicamacaraca + - Gol D. Ace (goldace) + - dodgepong + - LittleGirl_WithPonyTail (alexs1320) + - Vainock (ivo.lemmert) +Serbian (Latin): + - medicmomcilo + - LittleGirl_WithPonyTail (alexs1320) + - Gol D. Ace (goldace) + - Rale Sarcevic (ralesarcevic) + - dodgepong + - Vainock (ivo.lemmert) +Slovak: + - Luki (luki1412) + - Ján M (longmoped) + - Erik Bročko (ericek111) + - Anton Lokaj (anlo) + - LoLLy Nka (lollynka279) + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) +Slovenian: + - kristjan.krusic (krusic22) + - MG lolenstine (mglolenstine) + - Grimpy + - Gol D. Ace (goldace) + - ArcaneWater + - Vainock (ivo.lemmert) + - dodgepong +Southern Sotho: + - Samuel Nthoroane (Samuel_Nthoroane) +Spanish: + - Roberto Lorenzo (HonzoNebro) + - Jaime Martinez Rincon (mrjaime1999) + - Marcos Vidal Martinez (M4RK22) + - Jaime Muñoz Martín (jmmartin_5) + - EG Gamer (eggamer131) + - Dalia Sofía Magallón Páramo (SweetSofiMC) + - Gol D. Ace (goldace) + - Monsteer + - Pilar G. (TheMadnessLady) + - Maximiliano Schtroumpftech Pena-Roig (som2tokmynam) + - Carlos Plata (carlosesgenial33) + - Ruben Deig Ramos (rdeigramos) + - Marcos Vidal (markitos.maki22) + - eskaidom (sergiomalagonmartin) + - Alex E. D. B. (alexedb) + - Santiago Pereyra (SannttVIII) + - Eleazar Córcoles (MtrElee3) + - eemiroj + - Ray (Ipsumry) + - makiza1 (micosil_2) + - Sigge Stjärnholm (Kladdy) + - henrycontreras + - Vainock (ivo.lemmert) + - chaironeko + - dodgepong + - amssusgameplays (willifake052) + - Rodrigo Ipince (ipince) + - David Sonico (davidsubsonico) +Swedish: + - Anton R (FirePhoenix) + - Sigge Stjärnholm (Kladdy) + - Laccy IEST (Laccy) + - Olle Dahström (odahlstrom) + - Gustav Ekner (ekner) + - Gol D. Ace (goldace) + - Jonatan Nyberg (sweuser) + - Henrik Mattsson-Mårn (rchk) + - chaironeko + - Vainock (ivo.lemmert) + - Jonas Svensson (jonassanojj99) + - TacticalKebab + - Hannes Blåman (thebluis) +Tagalog: + - dandalion + - philiparniebinag + - jermel + - jbeguna04 + - Red Dayao (steemitph) + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - Raylir +Tamil: + - anto27 + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) +Thai: + - sakuhanachan* (sakuhanachanloli) + - ธีรภัทร์ โยชนา (Gataro) + - Sakia Normal Human (arcanaarcana5) + - 盛凤阁 (execzero) + - nongnoobjung (kitcharuk_4) + - Vainock (ivo.lemmert) + - dodgepong + - Gol D. Ace (goldace) +Turkish: + - Ali Kömesöğütlü (Mobile46) (byzlo685) + - omer.karagoz (mrkaragoz) + - Cemal Dursun (cmldrs) + - Savas Tokmak (Laserist) + - Murat Karagöz (anemon_1994) + - BruhSoundeffect2 (DoomerDoge) + - gecebekcisi1 + - Gol D. Ace (goldace) + - Umut kılıç (kilic190787) + - berkcan uçan (ibnehayati) + - Khedi + - mustafaa + - Richard Stanway (r1ch) + - Hydroboost (Hydroboost) + - Vainock (ivo.lemmert) + - Türker Yıldırım (turkeryildirim) + - chaironeko + - basakbk + - Alican Gultekin (Vitaefinis) +Ukrainian: + - SuslikV + - Юрій (Devinit) + - Andy (anry025) + - Sergey (LegionAnon) + - L1Q + - Gol D. Ace (goldace) + - Vainock (ivo.lemmert) + - Maksym Tymoshyk (maximillian_) + - NoPressure + - powerdef + - geimfis + - បងមាន តែអូន (cheaiphone267) +Urdu (Pakistan): + - Rana Awais (ehtisham) + - tahirsada + - shamuntohamd +Vietnamese: + - Johnny “max20091” Utah (boostyourprogram) + - Hưng Nguyễn (hoyostudio) + - ngoisaosang (ngoisaosang) + - Drake Strike (phjtieudoc) + - Gol D. Ace (goldace) + - IoeCmcomc (Ioe2015) (hopdaigia2004) + - BIGO - 지혜 (parkjihye96) + - Hà Phi Hùng (haphihungcom) + - Vainock (ivo.lemmert) + - Vũ Hải Tây (tayngungo1999) + - NCAA (NCAA) + - dodgepong diff --git a/CI/before-deploy-osx.sh b/CI/before-deploy-osx.sh index 38528d6..2c1fd69 100755 --- a/CI/before-deploy-osx.sh +++ b/CI/before-deploy-osx.sh @@ -9,7 +9,7 @@ set -e # Generate file name variables export GIT_HASH=$(git rev-parse --short HEAD) -export FILE_DATE=$(date +%Y-%m-%d.%H:%M:%S) +export FILE_DATE=$(date +%Y-%m-%d.%H-%M-%S) export FILENAME=$FILE_DATE-$GIT_HASH-$TRAVIS_BRANCH-osx.pkg cd ./build @@ -49,20 +49,24 @@ sudo install_name_tool -change \ hr "Generating .pkg" packagesbuild ../CI/install/osx/CMakeLists.pkgproj -# Signing stuff -hr "Decrypting Cert" -openssl aes-256-cbc -K $encrypted_dd3c7f5e9db9_key -iv $encrypted_dd3c7f5e9db9_iv -in ../CI/osxcert/Certificates.p12.enc -out Certificates.p12 -d -hr "Creating Keychain" -security create-keychain -p mysecretpassword build.keychain -security default-keychain -s build.keychain -security unlock-keychain -p mysecretpassword build.keychain -security set-keychain-settings -t 3600 -u build.keychain -hr "Importing certs into keychain" -security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P "" -# macOS 10.12+ -security set-key-partition-list -S apple-tool:,apple: -s -k mysecretpassword build.keychain -hr "Signing Package" -productsign --sign 2MMRE5MTB8 ./OBS.pkg ./$FILENAME +if [ -v "$TRAVIS" ]; then + # Signing stuff + hr "Decrypting Cert" + openssl aes-256-cbc -K $encrypted_dd3c7f5e9db9_key -iv $encrypted_dd3c7f5e9db9_iv -in ../CI/osxcert/Certificates.p12.enc -out Certificates.p12 -d + hr "Creating Keychain" + security create-keychain -p mysecretpassword build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p mysecretpassword build.keychain + security set-keychain-settings -t 3600 -u build.keychain + hr "Importing certs into keychain" + security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P "" + # macOS 10.12+ + security set-key-partition-list -S apple-tool:,apple: -s -k mysecretpassword build.keychain + hr "Signing Package" + productsign --sign 2MMRE5MTB8 ./OBS.pkg ./$FILENAME +else + cp ./OBS.pkg ./$FILENAME +fi # Move to the folder that travis uses to upload artifacts from hr "Moving package to nightly folder for distribution" diff --git a/CI/before-deploy-win.cmd b/CI/before-deploy-win.cmd index 0597fe0..e9dcb48 100644 --- a/CI/before-deploy-win.cmd +++ b/CI/before-deploy-win.cmd @@ -1,3 +1,3 @@ -robocopy C:\projects\obs-studio\build32\rundir\RelWithDebInfo C:\projects\obs-studio\build\ /E /XF .gitignore -robocopy C:\projects\obs-studio\build64\rundir\RelWithDebInfo C:\projects\obs-studio\build\ /E /XC /XN /XO /XF .gitignore -7z a build.zip C:\projects\obs-studio\build\* \ No newline at end of file +robocopy .\build32\rundir\RelWithDebInfo .\build\ /E /XF .gitignore +robocopy .\build64\rundir\RelWithDebInfo .\build\ /E /XC /XN /XO /XF .gitignore +7z a build.zip .\build\* \ No newline at end of file diff --git a/CI/before-script-osx.sh b/CI/before-script-osx.sh index a0cf54b..50a9a19 100755 --- a/CI/before-script-osx.sh +++ b/CI/before-script-osx.sh @@ -9,6 +9,8 @@ cmake -DENABLE_SPARKLE_UPDATER=ON \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \ -DQTDIR=/usr/local/Cellar/qt/5.10.1 \ -DDepsPath=/tmp/obsdeps \ --DVLCPath=$PWD/../../vlc-master \ +-DVLCPath=$PWD/../../vlc-3.0.4 \ -DBUILD_BROWSER=ON \ +-DBROWSER_DEPLOY=ON \ +-DBUILD_CAPTIONS=ON \ -DCEF_ROOT_DIR=$PWD/../../cef_binary_${CEF_BUILD_VERSION}_macosx64 .. diff --git a/CI/install-dependencies-linux-ubuntu16.sh b/CI/install-dependencies-linux-ubuntu16.sh new file mode 100755 index 0000000..52e8d22 --- /dev/null +++ b/CI/install-dependencies-linux-ubuntu16.sh @@ -0,0 +1,59 @@ +#!/bin/sh +set -ex + +sudo apt-get -qq update +sudo apt-get install -y \ + build-essential \ + checkinstall \ + cmake \ + libasound2-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavformat-dev \ + libavutil-dev \ + libcurl4-openssl-dev \ + libfdk-aac-dev \ + libfontconfig-dev \ + libfreetype6-dev \ + libgl1-mesa-dev \ + libjack-jackd2-dev \ + libjansson-dev \ + libluajit-5.1-dev \ + libpulse-dev \ + libqt5x11extras5-dev \ + libspeexdsp-dev \ + libswresample-dev \ + libswscale-dev \ + libudev-dev \ + libv4l-dev \ + libvlc-dev \ + libx11-dev \ + libx264-dev \ + libxcb-randr0-dev \ + libxcb-shm0-dev \ + libxcb-xinerama0-dev \ + libxcomposite-dev \ + libxinerama-dev \ + pkg-config \ + python3-dev \ + qtbase5-dev \ + libqt5svg5-dev \ + swig + + +# build mbedTLS +cd ~/projects +mkdir mbedtls +cd mbedtls +mbedtlsPath=$PWD +curl -L -O https://github.com/ARMmbed/mbedtls/archive/mbedtls-2.12.0.tar.gz +tar -xf mbedtls-2.12.0.tar.gz +mkdir build +cd ./build +cmake -DENABLE_TESTING=Off -DUSE_SHARED_MBEDTLS_LIBRARY=On ../mbedtls-mbedtls-2.12.0 +make -j 12 +sudo make install + +# return to OBS build dir +cd $APPVEYOR_BUILD_FOLDER diff --git a/CI/install-dependencies-linux.sh b/CI/install-dependencies-linux.sh index 2cbd8de..4ab0219 100755 --- a/CI/install-dependencies-linux.sh +++ b/CI/install-dependencies-linux.sh @@ -1,18 +1,19 @@ #!/bin/sh set -ex -sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-next -y +sudo add-apt-repository ppa:jonathonf/ffmpeg-3 -y +curl -L https://packagecloud.io/github/git-lfs/gpgkey | sudo apt-key add - sudo apt-get -qq update sudo apt-get install -y \ build-essential \ checkinstall \ cmake \ libasound2-dev \ - libavcodec-ffmpeg-dev \ - libavdevice-ffmpeg-dev \ - libavfilter-ffmpeg-dev \ - libavformat-ffmpeg-dev \ - libavutil-ffmpeg-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavformat-dev \ + libavutil-dev \ libcurl4-openssl-dev \ libfdk-aac-dev \ libfontconfig-dev \ @@ -24,13 +25,15 @@ sudo apt-get install -y \ libpulse-dev \ libqt5x11extras5-dev \ libspeexdsp-dev \ - libswresample-ffmpeg-dev \ - libswscale-ffmpeg-dev \ + libswresample-dev \ + libswscale-dev \ libudev-dev \ libv4l-dev \ + libva-dev \ libvlc-dev \ libx11-dev \ libx264-dev \ + libxcb-randr0-dev \ libxcb-shm0-dev \ libxcb-xinerama0-dev \ libxcomposite-dev \ @@ -38,4 +41,5 @@ sudo apt-get install -y \ pkg-config \ python3-dev \ qtbase5-dev \ + libqt5svg5-dev \ swig diff --git a/CI/install-dependencies-osx.sh b/CI/install-dependencies-osx.sh index 82f3aac..24b8018 100755 --- a/CI/install-dependencies-osx.sh +++ b/CI/install-dependencies-osx.sh @@ -10,7 +10,9 @@ set -e # Echo all commands before executing set -v -git fetch --unshallow +if [[ $TRAVIS ]]; then + git fetch --unshallow +fi # Leave obs-studio folder cd ../ @@ -24,8 +26,9 @@ sudo installer -pkg ./Packages.pkg -target / brew update #Base OBS Deps and ccache -brew install jack speexdsp ccache swig mbedtls -brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/9a70413d137839de0054571e5f85fd07ee400955/Formula/qt.rb +brew install jack speexdsp ccache mbedtls +brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb +brew install https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb export PATH=/usr/local/opt/ccache/libexec:$PATH ccache -s || echo "CCache is not available." @@ -37,8 +40,8 @@ tar -xf ./osx-deps-2018-08-09.tar.gz -C /tmp # Fetch vlc codebase hr "Downloading VLC repo" -wget --quiet --retry-connrefused --waitretry=1 -O vlc-master.zip https://github.com/videolan/vlc/archive/master.zip -unzip -q ./vlc-master.zip +wget --quiet --retry-connrefused --waitretry=1 https://downloads.videolan.org/vlc/3.0.4/vlc-3.0.4.tar.xz +tar -xf vlc-3.0.4.tar.xz # Get sparkle hr "Downloading Sparkle framework" @@ -54,6 +57,8 @@ tar -xf ./cef_binary_${CEF_BUILD_VERSION}_macosx64.tar.bz2 cd ./cef_binary_${CEF_BUILD_VERSION}_macosx64 # remove a broken test sed -i '.orig' '/add_subdirectory(tests\/ceftests)/d' ./CMakeLists.txt +# target 10.11 +sed -i '.orig' s/\"10.9\"/\"10.11\"/ ./cmake/cef_variables.cmake mkdir build cd ./build cmake -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++" -DCMAKE_EXE_LINKER_FLAGS="-std=c++11 -stdlib=libc++" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 .. diff --git a/CI/install-qt-win.cmd b/CI/install-qt-win.cmd new file mode 100644 index 0000000..46e14e8 --- /dev/null +++ b/CI/install-qt-win.cmd @@ -0,0 +1,4 @@ +curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_5.10.1.7z -f --retry 5 -C - +7z x Qt_5.10.1.7z -oQt +mv Qt C:\QtDep +dir C:\QtDep \ No newline at end of file diff --git a/CI/install-script-linux.sh b/CI/install-script-linux.sh new file mode 100755 index 0000000..abbfbf0 --- /dev/null +++ b/CI/install-script-linux.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -ex + +build_config=RelWithDebInfo diff --git a/CI/install-script-win.cmd b/CI/install-script-win.cmd new file mode 100644 index 0000000..a3051c3 --- /dev/null +++ b/CI/install-script-win.cmd @@ -0,0 +1,29 @@ +if exist dependencies2017.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2017.zip -f --retry 5 -z dependencies2017.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2017.zip -f --retry 5 -C -) +if exist vlc.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/vlc.zip -f --retry 5 -z vlc.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/vlc.zip -f --retry 5 -C -) +if exist cef_binary_%CEF_VERSION%_windows32.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows32.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32.zip -f --retry 5 -C -) +if exist cef_binary_%CEF_VERSION%_windows64.zip (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows64.zip) else (curl -kLO https://cdn-fastly.obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64.zip -f --retry 5 -C -) +7z x dependencies2017.zip -odependencies2017 +7z x vlc.zip -ovlc +7z x cef_binary_%CEF_VERSION%_windows32.zip -oCEF_32 +7z x cef_binary_%CEF_VERSION%_windows64.zip -oCEF_64 +set DepsPath32=%CD%\dependencies2017\win32 +set DepsPath64=%CD%\dependencies2017\win64 +set VLCPath=%CD%\vlc +set QTDIR32=C:\QtDep\5.10.1\msvc2017 +set QTDIR64=C:\QtDep\5.10.1\msvc2017_64 +set CEF_32=%CD%\CEF_32\cef_binary_%CEF_VERSION%_windows32 +set CEF_64=%CD%\CEF_64\cef_binary_%CEF_VERSION%_windows64 +set build_config=RelWithDebInfo +mkdir build build32 build64 +if "%TWITCH-CLIENTID%"=="$(twitch_clientid)" ( +cd ./build32 +cmake -G "Visual Studio 15 2017" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% .. +cd ../build64 +cmake -G "Visual Studio 15 2017 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% .. +) else ( +cd ./build32 +cmake -G "Visual Studio 15 2017" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%" -DMIXER_CLIENTID="%MIXER-CLIENTID%" -DMIXER_HASH="%MIXER-HASH%" -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" .. +cd ../build64 +cmake -G "Visual Studio 15 2017 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%" -DMIXER_CLIENTID="%MIXER-CLIENTID%" -DMIXER_HASH="%MIXER-HASH%" -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" .. +) +cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bac27c..47dadab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,17 @@ else() set(OBS_RELEASE_CANDIDATE 0) endif() +# Binary Versioning for Windows +if(WIN32) + string(REPLACE "-" ";" UI_VERSION_SPLIT ${OBS_VERSION}) + list(GET UI_VERSION_SPLIT 0 UI_VERSION) + string(REPLACE "." ";" UI_VERSION_SEMANTIC ${UI_VERSION}) + list(GET UI_VERSION_SEMANTIC 0 UI_VERSION_MAJOR) + list(GET UI_VERSION_SEMANTIC 1 UI_VERSION_MINOR) + list(GET UI_VERSION_SEMANTIC 2 UI_VERSION_PATCH) + configure_file(UI/obs.rc.in ${PROJECT_BINARY_DIR}/obs.rc) +endif() + if(MSVC AND NOT EXISTS "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user") file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user" @@ -66,9 +77,13 @@ if(${CMAKE_C_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} MATCHES "C set(CMAKE_COMPILER_IS_CLANG TRUE) endif() +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le") + add_compile_definitions(NO_WARN_X86_INTRINSICS) +endif() + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) - set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-field-initializers ${CMAKE_CXX_FLAGS} -fno-strict-aliasing") - set(CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=gnu99 -fno-strict-aliasing") + set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-field-initializers ${CMAKE_CXX_FLAGS} -fno-strict-aliasing") + set(CMAKE_C_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=gnu99 -fno-strict-aliasing") option(USE_LIBC++ "Use libc++ instead of libstdc++" ${APPLE}) if(USE_LIBC++) @@ -90,6 +105,7 @@ if(WIN32) endif() if(MSVC) + add_compile_options("/MP") set(CMAKE_C_FLAGS_DEBUG "/DDEBUG=1 /D_DEBUG=1 ${CMAKE_C_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_DEBUG "/DDEBUG=1 /D_DEBUG=1 ${CMAKE_C_FLAGS_DEBUG}") @@ -132,6 +148,24 @@ if(NOT INSTALLER_RUN) set(UI_ENABLED FALSE) else() set(UI_ENABLED TRUE) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) + else() + set(_lib_suffix 32) + endif() + + if(DEFINED QTDIR${_lib_suffix}) + list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}") + elseif(DEFINED QTDIR) + list(APPEND CMAKE_PREFIX_PATH "${QTDIR}") + elseif(DEFINED ENV{QTDIR${_lib_suffix}}) + list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}") + elseif(DEFINED ENV{QTDIR}) + list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}") + endif() + + find_package(Qt5Widgets ${FIND_MODE}) endif() add_subdirectory(deps) @@ -142,8 +176,8 @@ if(NOT INSTALLER_RUN) add_subdirectory(libobs-opengl) add_subdirectory(libobs) - add_subdirectory(UI) add_subdirectory(plugins) + add_subdirectory(UI) if (BUILD_TESTS) add_subdirectory(test) endif() diff --git a/COMMITMENT b/COMMITMENT new file mode 100644 index 0000000..a687e0d --- /dev/null +++ b/COMMITMENT @@ -0,0 +1,46 @@ +GPL Cooperation Commitment +Version 1.0 + +Before filing or continuing to prosecute any legal proceeding or claim +(other than a Defensive Action) arising from termination of a Covered +License, we commit to extend to the person or entity ('you') accused +of violating the Covered License the following provisions regarding +cure and reinstatement, taken from GPL version 3. As used here, the +term 'this License' refers to the specific Covered License being +enforced. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly + and finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you + have received notice of violation of this License (for any work) + from that copyright holder, and you cure the violation prior to 30 + days after your receipt of the notice. + +We intend this Commitment to be irrevocable, and binding and +enforceable against us and assignees of or successors to our +copyrights. + +Definitions + +'Covered License' means the GNU General Public License, version 2 +(GPLv2), the GNU Lesser General Public License, version 2.1 +(LGPLv2.1), or the GNU Library General Public License, version 2 +(LGPLv2), all as published by the Free Software Foundation. + +'Defensive Action' means a legal proceeding or claim that We bring +against you in response to a prior proceeding or claim initiated by +you or your affiliate. + +'We' means each contributor to this repository as of the date of +inclusion of this file, including subsidiaries of a corporate +contributor. + +This work is available under a Creative Commons Attribution-ShareAlike +4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/). diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6ea05c2..43c1934 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -10,7 +10,9 @@ Quick Links for Contributing - Our bug tracker (linked to forum accounts): https://obsproject.com/mantis/ - - Development IRC channel: #obs-dev on QuakeNet + - Discord Server: https://obsproject.com/discord + + - Development chat: #development on the Discord server (see above) - Development forum: https://obsproject.com/forum/list/general-development.21/ @@ -50,10 +52,11 @@ Commit Guidelines - 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:: + - Prefix each commit's titles with the module name, followed by a colon + and a space (unless modifying a file in the base directory). After + that, the first word should be capitalized. + + So for example, if you are modifying the obs-ffmpeg plugin:: obs-ffmpeg: Fix bug with audio output @@ -61,4 +64,6 @@ Commit Guidelines libobs: Fix source not displaying + Note: When modifying cmake modules, just prefix with "cmake". + - If you still need examples, please view the commit history. diff --git a/README.rst b/README.rst index 3e0e02d..92c4938 100644 --- a/README.rst +++ b/README.rst @@ -1,14 +1,30 @@ OBS Studio =================================== +.. image:: https://travis-ci.org/obsproject/obs-studio.svg?branch=master + :alt: OBS Studio Build Status - Travis CI + :target: https://travis-ci.org/obsproject/obs-studio + +.. image:: https://ci.appveyor.com/api/projects/status/github/obsproject/obs-studio?branch=master&svg=true + :alt: OBS Studio Build Status - AppVeyor CI + :target: https://ci.appveyor.com/project/jp9000/obs-studio/branch/master + +.. image:: https://dev.azure.com/obsjim/obsjim/_apis/build/status/obsproject.obs-studio?branchName=master + :alt: OBS Studio Build Status - Azure Pipelines + :target: https://dev.azure.com/obsjim/obsjim/_build/latest?definitionId=1&branchName=master + +.. image:: https://discordapp.com/api/guilds/348973006581923840/widget.png?style=shield + :alt: OBS Studio Discord Server + :target: https://obsproject.com/discord + 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. + It's distributed under the GNU General Public License v2 (or any later + version) - see the accompanying COPYING file for more details. Quick Links ----------- @@ -23,6 +39,8 @@ Quick Links - Developer/API Documentation: https://obsproject.com/docs + - Donating/backing/sponsoring: https://obsproject.com/contribute + - Bug Tracker: https://obsproject.com/mantis/ (Note: The bug tracker is linked to forum accounts. To use the bug @@ -31,6 +49,12 @@ Quick Links Contributing ------------ + - If you would like to help fund or sponsor the project, you can do so + via `Patreon `_, `OpenCollective + `_, or `PayPal + `_. See our `contribute page + `_ for more information. + - If you wish to contribute code to the project, please make sure to read the coding and commit guidelines: https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.rst diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index f414488..ac54110 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -7,6 +7,10 @@ else() set(FIND_MODE QUIET) endif() +if(BROWSER_AVAILABLE_INTERNAL) + add_definitions(-DBROWSER_AVAILABLE) +endif() + add_subdirectory(obs-frontend-api) # ---------------------------------------------------------------------------- @@ -15,26 +19,45 @@ project(obs) set(DISABLE_UPDATE_MODULE TRUE CACHE BOOL "Disables building the update module") -if(DEFINED QTDIR${_lib_suffix}) - list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}") -elseif(DEFINED QTDIR) - list(APPEND CMAKE_PREFIX_PATH "${QTDIR}") -elseif(DEFINED ENV{QTDIR${_lib_suffix}}) - list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}") -elseif(DEFINED ENV{QTDIR}) - list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}") +if(NOT DEFINED TWITCH_CLIENTID OR "${TWITCH_CLIENTID}" STREQUAL "" OR + NOT DEFINED TWITCH_HASH OR "${TWITCH_HASH}" STREQUAL "" OR + NOT BROWSER_AVAILABLE_INTERNAL) + set(TWITCH_ENABLED FALSE) + set(TWITCH_CLIENTID "") + set(TWITCH_HASH "0") +else() + set(TWITCH_ENABLED TRUE) endif() +if(NOT DEFINED MIXER_CLIENTID OR "${MIXER_CLIENTID}" STREQUAL "" OR + NOT DEFINED MIXER_HASH OR "${MIXER_HASH}" STREQUAL "" OR + NOT BROWSER_AVAILABLE_INTERNAL) + set(MIXER_ENABLED FALSE) + set(MIXER_CLIENTID "") + set(MIXER_HASH "0") +else() + set(MIXER_ENABLED TRUE) +endif() + +if(NOT DEFINED RESTREAM_CLIENTID OR "${RESTREAM_CLIENTID}" STREQUAL "" OR + NOT DEFINED RESTREAM_HASH OR "${RESTREAM_HASH}" STREQUAL "" OR + NOT BROWSER_AVAILABLE_INTERNAL) + set(RESTREAM_ENABLED FALSE) + set(RESTREAM_CLIENTID "") + set(RESTREAM_HASH "0") +else() + set(RESTREAM_ENABLED TRUE) +endif() + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/ui-config.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/ui-config.h") + set(CMAKE_INCLUDE_CURRENT_DIR TRUE) set(CMAKE_AUTOMOC TRUE) -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() +find_package(Qt5Svg ${FIND_MODE}) -find_package(Qt5Widgets ${FIND_MODE}) find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat) if(APPLE) @@ -52,11 +75,14 @@ endif() include_directories(${FFMPEG_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(SYSTEM "obs-frontend-api") include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs") include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/deps/libff") include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/deps/json11") -include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/plugins/obs-browser/panel") +if(BROWSER_AVAILABLE_INTERNAL) + include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/plugins/obs-browser/panel") +endif() find_package(Libcurl REQUIRED) include_directories(${LIBCURL_INCLUDE_DIRS}) @@ -71,7 +97,7 @@ if(WIN32) win-update/update-window.cpp win-update/win-update.cpp win-update/win-update-helpers.cpp - obs.rc) + ${obs-studio_BINARY_DIR}/obs.rc) set(obs_PLATFORM_HEADERS win-update/update-window.hpp win-update/win-update.hpp @@ -80,6 +106,10 @@ if(WIN32) crypt32 blake2 ${OBS_JANSSON_IMPORT}) + + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") + endif() elseif(APPLE) set(obs_PLATFORM_SOURCES platform-osx.mm) @@ -112,6 +142,44 @@ elseif(UNIX) Qt5::X11Extras) endif() +if(BROWSER_AVAILABLE_INTERNAL) + list(APPEND obs_PLATFORM_SOURCES + obf.c + auth-oauth.cpp + ) + list(APPEND obs_PLATFORM_HEADERS + obf.h + auth-oauth.hpp + ) + + if(TWITCH_ENABLED) + list(APPEND obs_PLATFORM_SOURCES + auth-twitch.cpp + ) + list(APPEND obs_PLATFORM_HEADERS + auth-twitch.hpp + ) + endif() + + if(MIXER_ENABLED) + list(APPEND obs_PLATFORM_SOURCES + auth-mixer.cpp + ) + list(APPEND obs_PLATFORM_HEADERS + auth-mixer.hpp + ) + endif() + + if(RESTREAM_ENABLED) + list(APPEND obs_PLATFORM_SOURCES + auth-restream.cpp + ) + list(APPEND obs_PLATFORM_HEADERS + auth-restream.hpp + ) + endif() +endif() + set(obs_libffutil_SOURCES ../deps/libff/libff/ff-util.c ) @@ -131,6 +199,7 @@ set(obs_SOURCES ${obs_libffutil_SOURCES} ../deps/json11/json11.cpp obs-app.cpp + window-dock.cpp api-interface.cpp window-basic-main.cpp window-basic-stats.cpp @@ -141,35 +210,38 @@ set(obs_SOURCES window-basic-auto-config.cpp window-basic-main-outputs.cpp window-basic-source-select.cpp + window-basic-settings-stream.cpp window-basic-auto-config-test.cpp window-basic-main-scene-collections.cpp window-basic-main-transitions.cpp window-basic-main-dropfiles.cpp window-basic-main-profiles.cpp - window-license-agreement.cpp + window-basic-main-browser.cpp window-basic-status-bar.cpp window-basic-adv-audio.cpp window-basic-transform.cpp window-basic-preview.cpp + window-basic-about.cpp window-namedialog.cpp window-log-reply.cpp window-projector.cpp window-remux.cpp + auth-base.cpp source-tree.cpp properties-view.cpp focus-list.cpp menu-button.cpp double-slider.cpp + slider-ignorewheel.cpp + combobox-ignorewheel.cpp + spinbox-ignorewheel.cpp volume-control.cpp adv-audio-control.cpp item-widget-helpers.cpp - visibility-checkbox.cpp - locked-checkbox.cpp horizontal-scroll-area.cpp vertical-scroll-area.cpp visibility-item-widget.cpp slider-absoluteset-style.cpp - source-list-widget.cpp qt-display.cpp crash-report.cpp hotkey-edit.cpp @@ -184,6 +256,7 @@ set(obs_HEADERS ../deps/json11/json11.hpp obs-app.hpp platform.hpp + window-dock.hpp window-main.hpp window-basic-main.hpp window-basic-stats.hpp @@ -194,7 +267,7 @@ set(obs_HEADERS window-basic-auto-config.hpp window-basic-main-outputs.hpp window-basic-source-select.hpp - window-license-agreement.hpp + window-basic-about.hpp window-basic-status-bar.hpp window-basic-adv-audio.hpp window-basic-transform.hpp @@ -203,11 +276,16 @@ set(obs_HEADERS window-log-reply.hpp window-projector.hpp window-remux.hpp + auth-base.hpp source-tree.hpp properties-view.hpp properties-view.moc.hpp display-helpers.hpp + balance-slider.hpp double-slider.hpp + slider-ignorewheel.hpp + combobox-ignorewheel.hpp + spinbox-ignorewheel.hpp focus-list.hpp menu-button.hpp mute-checkbox.hpp @@ -221,14 +299,14 @@ set(obs_HEADERS vertical-scroll-area.hpp visibility-item-widget.hpp slider-absoluteset-style.hpp - source-list-widget.hpp qt-display.hpp crash-report.hpp hotkey-edit.hpp source-label.hpp remote-text.hpp audio-encoders.hpp - qt-wrappers.hpp) + qt-wrappers.hpp + clickable-label.hpp) set(obs_UI forms/NameDialog.ui @@ -237,7 +315,6 @@ set(obs_UI forms/AutoConfigStreamPage.ui forms/AutoConfigTestPage.ui forms/ColorSelect.ui - forms/OBSLicenseAgreement.ui forms/OBSLogReply.ui forms/OBSBasic.ui forms/OBSBasicTransform.ui @@ -246,7 +323,8 @@ set(obs_UI forms/OBSBasicSourceSelect.ui forms/OBSBasicInteraction.ui forms/OBSUpdate.ui - forms/OBSRemux.ui) + forms/OBSRemux.ui + forms/OBSAbout.ui) set(obs_QRC forms/obs.qrc) @@ -275,6 +353,7 @@ endif() target_link_libraries(obs libobs Qt5::Widgets + Qt5::Svg obs-frontend-api ${FFMPEG_LIBRARIES} ${LIBCURL_LIBRARIES} @@ -298,11 +377,10 @@ define_graphic_modules(obs) install_obs_core(obs) install_obs_data(obs data obs-studio) +install_obs_data_file(obs ../AUTHORS obs-studio/authors) if (UNIX AND UNIX_STRUCTURE AND NOT APPLE) - install(FILES dist/obs.desktop DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/applications) - install(FILES forms/images/obs.png - DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons/hicolor/256x256/apps) + add_subdirectory(xdg-data) endif() add_subdirectory(frontend-plugins) diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index 2c602b6..db97504 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -4,15 +4,18 @@ #include #include #include -#include #include "qt-wrappers.hpp" #include "obs-app.hpp" #include "adv-audio-control.hpp" +#include "window-basic-main.hpp" #ifndef NSEC_PER_MSEC #define NSEC_PER_MSEC 1000000 #endif +#define MIN_DB -96.0 +#define MAX_DB 26.0 + OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) : source(source_) { @@ -25,13 +28,13 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) forceMonoContainer = new QWidget(); mixerContainer = new QWidget(); - panningContainer = new QWidget(); + balanceContainer = new QWidget(); labelL = new QLabel(); labelR = new QLabel(); nameLabel = new QLabel(); - volume = new QSpinBox(); + volume = new QDoubleSpinBox(); forceMono = new QCheckBox(); - panning = new QSlider(Qt::Horizontal); + balance = new BalanceSlider(); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType = new QComboBox(); #endif @@ -60,8 +63,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) mixerContainer->setLayout(hlayout); hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); - panningContainer->setLayout(hlayout); - panningContainer->setMinimumWidth(100); + balanceContainer->setLayout(hlayout); + balanceContainer->setMinimumWidth(100); labelL->setText("L"); @@ -71,9 +74,15 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) nameLabel->setText(QT_UTF8(sourceName)); nameLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - volume->setMinimum(0); - volume->setMaximum(2000); - volume->setValue(int(vol * 100.0f)); + volume->setMinimum(MIN_DB - 0.1); + volume->setMaximum(MAX_DB); + volume->setSingleStep(0.1); + volume->setDecimals(1); + volume->setSuffix(" dB"); + volume->setValue(obs_mul_to_db(vol)); + + if (volume->value() < MIN_DB) + volume->setSpecialValueText("-inf dB"); forceMono->setChecked((flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0); @@ -81,14 +90,27 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) forceMonoContainer->layout()->setAlignment(forceMono, Qt::AlignHCenter | Qt::AlignVCenter); - panning->setMinimum(0); - panning->setMaximum(100); - panning->setTickPosition(QSlider::TicksAbove); - panning->setEnabled(false); - panning->setValue(50); /* XXX */ + balance->setOrientation(Qt::Horizontal); + balance->setMinimum(0); + balance->setMaximum(100); + balance->setTickPosition(QSlider::TicksAbove); + balance->setTickInterval(50); + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + const char *speakers = config_get_string(main->Config(), "Audio", + "ChannelSetup"); + + if (strcmp(speakers, "Mono") == 0) + balance->setEnabled(false); + else + balance->setEnabled(true); + + float bal = obs_source_get_balance_value(source) * 100.0f; + balance->setValue((int)bal); int64_t cur_sync = obs_source_get_sync_offset(source); - syncOffset->setMinimum(-20000); + syncOffset->setMinimum(-950); syncOffset->setMaximum(20000); syncOffset->setValue(int(cur_sync / NSEC_PER_MSEC)); @@ -118,10 +140,14 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) mixer6->setText("6"); mixer6->setChecked(mixers & (1<<5)); - panningContainer->layout()->addWidget(labelL); - panningContainer->layout()->addWidget(panning); - panningContainer->layout()->addWidget(labelR); - panningContainer->setMaximumWidth(170); + speaker_layout sl = obs_source_get_speaker_layout(source); + + if (sl == SPEAKERS_STEREO) { + balanceContainer->layout()->addWidget(labelL); + balanceContainer->layout()->addWidget(balance); + balanceContainer->layout()->addWidget(labelR); + balanceContainer->setMaximumWidth(170); + } mixerContainer->layout()->addWidget(mixer1); mixerContainer->layout()->addWidget(mixer2); @@ -130,12 +156,14 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) mixerContainer->layout()->addWidget(mixer5); mixerContainer->layout()->addWidget(mixer6); - QWidget::connect(volume, SIGNAL(valueChanged(int)), - this, SLOT(volumeChanged(int))); + QWidget::connect(volume, SIGNAL(valueChanged(double)), + this, SLOT(volumeChanged(double))); QWidget::connect(forceMono, SIGNAL(clicked(bool)), this, SLOT(downmixMonoChanged(bool))); - QWidget::connect(panning, SIGNAL(valueChanged(int)), - this, SLOT(panningChanged(int))); + QWidget::connect(balance, SIGNAL(valueChanged(int)), + this, SLOT(balanceChanged(int))); + QWidget::connect(balance, SIGNAL(doubleClicked()), + this, SLOT(ResetBalance())); QWidget::connect(syncOffset, SIGNAL(valueChanged(int)), this, SLOT(syncOffsetChanged(int))); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO @@ -163,7 +191,7 @@ OBSAdvAudioCtrl::~OBSAdvAudioCtrl() nameLabel->deleteLater(); volume->deleteLater(); forceMonoContainer->deleteLater(); - panningContainer->deleteLater(); + balanceContainer->deleteLater(); syncOffset->deleteLater(); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType->deleteLater(); @@ -179,7 +207,7 @@ void OBSAdvAudioCtrl::ShowAudioControl(QGridLayout *layout) layout->addWidget(nameLabel, lastRow, idx++); layout->addWidget(volume, lastRow, idx++); layout->addWidget(forceMonoContainer, lastRow, idx++); - layout->addWidget(panningContainer, lastRow, idx++); + layout->addWidget(balanceContainer, lastRow, idx++); layout->addWidget(syncOffset, lastRow, idx++); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO layout->addWidget(monitoringType, lastRow, idx++); @@ -240,7 +268,7 @@ void OBSAdvAudioCtrl::SourceFlagsChanged(uint32_t flags) void OBSAdvAudioCtrl::SourceVolumeChanged(float value) { volume->blockSignals(true); - volume->setValue(int(round(value * 100.0f))); + volume->setValue(obs_mul_to_db(value)); volume->blockSignals(false); } @@ -262,9 +290,14 @@ void OBSAdvAudioCtrl::SourceMixersChanged(uint32_t mixers) /* ------------------------------------------------------------------------- */ /* Qt control callbacks */ -void OBSAdvAudioCtrl::volumeChanged(int percentage) +void OBSAdvAudioCtrl::volumeChanged(double db) { - float val = float(percentage) / 100.0f; + if (db < MIN_DB) { + volume->setSpecialValueText("-inf dB"); + db = -INFINITY; + } + + float val = obs_db_to_mul(db); obs_source_set_volume(source, val); } @@ -283,12 +316,26 @@ void OBSAdvAudioCtrl::downmixMonoChanged(bool checked) } } -void OBSAdvAudioCtrl::panningChanged(int val) +void OBSAdvAudioCtrl::balanceChanged(int val) { - /* TODO */ - UNUSED_PARAMETER(val); + float bal = (float)val / 100.0f; + + if (abs(50 - val) < 10) { + balance->blockSignals(true); + balance->setValue(50); + bal = 0.5f; + balance->blockSignals(false); + } + + obs_source_set_balance_value(source, bal); } +void OBSAdvAudioCtrl::ResetBalance() +{ + balance->setValue(50); +} + + void OBSAdvAudioCtrl::syncOffsetChanged(int milliseconds) { int64_t cur_val = obs_source_get_sync_offset(source); diff --git a/UI/adv-audio-control.hpp b/UI/adv-audio-control.hpp index 1352a4b..05743a5 100644 --- a/UI/adv-audio-control.hpp +++ b/UI/adv-audio-control.hpp @@ -3,12 +3,13 @@ #include #include #include +#include +#include "balance-slider.hpp" class QGridLayout; class QLabel; class QSpinBox; class QCheckBox; -class QSlider; class QComboBox; class OBSAdvAudioCtrl : public QObject { @@ -19,12 +20,12 @@ private: QPointer forceMonoContainer; QPointer mixerContainer; - QPointer panningContainer; + QPointer balanceContainer; QPointer nameLabel; - QPointer volume; + QPointer volume; QPointer forceMono; - QPointer panning; + QPointerbalance; QPointer labelL; QPointer labelR; QPointer syncOffset; @@ -59,9 +60,9 @@ public slots: void SourceSyncChanged(int64_t offset); void SourceMixersChanged(uint32_t mixers); - void volumeChanged(int percentage); + void volumeChanged(double db); void downmixMonoChanged(bool checked); - void panningChanged(int val); + void balanceChanged(int val); void syncOffsetChanged(int milliseconds); void monitoringTypeChanged(int index); void mixer1Changed(bool checked); @@ -70,4 +71,5 @@ public slots: void mixer4Changed(bool checked); void mixer5Changed(bool checked); void mixer6Changed(bool checked); + void ResetBalance(); }; diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index e7cc836..c69b3ef 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -20,6 +20,10 @@ static T GetOBSRef(QListWidgetItem *item) void EnumProfiles(function &&cb); void EnumSceneCollections(function &&cb); +extern volatile bool streaming_active; +extern volatile bool recording_active; +extern volatile bool replaybuf_active; + /* ------------------------------------------------------------------------- */ template struct OBSStudioCallback { @@ -63,6 +67,11 @@ struct OBSStudioAPI : obs_frontend_callbacks { return (void*)main->winId(); } + void *obs_frontend_get_system_tray(void) override + { + return (void*)main->trayIcon.data(); + } + void obs_frontend_get_scenes( struct obs_frontend_source_list *sources) override { @@ -130,6 +139,17 @@ struct OBSStudioAPI : obs_frontend_callbacks { Q_ARG(OBSSource, OBSSource(transition))); } + int obs_frontend_get_transition_duration(void) override + { + return main->ui->transitionDuration->value(); + } + + void obs_frontend_set_transition_duration(int duration) override + { + QMetaObject::invokeMethod(main->ui->transitionDuration, "setValue", + Q_ARG(int, duration)); + } + void obs_frontend_get_scene_collections( std::vector &strings) override { @@ -232,7 +252,7 @@ struct OBSStudioAPI : obs_frontend_callbacks { bool obs_frontend_streaming_active(void) override { - return main->outputHandler->StreamingActive(); + return os_atomic_load_bool(&streaming_active); } void obs_frontend_recording_start(void) override @@ -247,7 +267,7 @@ struct OBSStudioAPI : obs_frontend_callbacks { bool obs_frontend_recording_active(void) override { - return main->outputHandler->RecordingActive(); + return os_atomic_load_bool(&recording_active); } void obs_frontend_replay_buffer_start(void) override @@ -267,7 +287,7 @@ struct OBSStudioAPI : obs_frontend_callbacks { bool obs_frontend_replay_buffer_active(void) override { - return main->outputHandler->ReplayBufferActive(); + return os_atomic_load_bool(&replaybuf_active); } void *obs_frontend_add_tools_menu_qaction(const char *name) override @@ -290,6 +310,11 @@ struct OBSStudioAPI : obs_frontend_callbacks { QObject::connect(action, &QAction::triggered, func); } + void *obs_frontend_add_dock(void *dock) override + { + return (void*)main->AddDockWidget((QDockWidget *)dock); + } + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) override { @@ -430,6 +455,11 @@ struct OBSStudioAPI : obs_frontend_callbacks { main->SetPreviewProgramMode(enable); } + void obs_frontend_preview_program_trigger_transition(void) override + { + QMetaObject::invokeMethod(main, "TransitionClicked"); + } + bool obs_frontend_preview_enabled(void) override { return main->previewEnabled; diff --git a/UI/audio-encoders.hpp b/UI/audio-encoders.hpp index 81c69e6..7bbde42 100644 --- a/UI/audio-encoders.hpp +++ b/UI/audio-encoders.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include diff --git a/UI/auth-base.cpp b/UI/auth-base.cpp new file mode 100644 index 0000000..6a6bcfc --- /dev/null +++ b/UI/auth-base.cpp @@ -0,0 +1,71 @@ +#include "auth-base.hpp" +#include "window-basic-main.hpp" + +#include +#include + +struct AuthInfo { + Auth::Def def; + Auth::create_cb create; +}; + +static std::vector authDefs; + +void Auth::RegisterAuth(const Def &d, create_cb create) +{ + AuthInfo info = {d, create}; + authDefs.push_back(info); +} + +std::shared_ptr Auth::Create(const std::string &service) +{ + for (auto &a : authDefs) { + if (service.find(a.def.service) != std::string::npos) { + return a.create(); + } + } + + return nullptr; +} + +Auth::Type Auth::AuthType(const std::string &service) +{ + for (auto &a : authDefs) { + if (service.find(a.def.service) != std::string::npos) { + return a.def.type; + } + } + + return Type::None; +} + +void Auth::Load() +{ + OBSBasic *main = OBSBasic::Get(); + const char *typeStr = config_get_string(main->Config(), "Auth", "Type"); + if (!typeStr) typeStr = ""; + + main->auth = Create(typeStr); + if (main->auth) { + if (main->auth->LoadInternal()) { + main->auth->LoadUI(); + } + } +} + +void Auth::Save() +{ + OBSBasic *main = OBSBasic::Get(); + Auth *auth = main->auth.get(); + if (!auth) { + if (config_has_user_value(main->Config(), "Auth", "Type")) { + config_remove_value(main->Config(), "Auth", "Type"); + config_save_safe(main->Config(), "tmp", nullptr); + } + return; + } + + config_set_string(main->Config(), "Auth", "Type", auth->service()); + auth->SaveInternal(); + config_save_safe(main->Config(), "tmp", nullptr); +} diff --git a/UI/auth-base.hpp b/UI/auth-base.hpp new file mode 100644 index 0000000..8e04ec1 --- /dev/null +++ b/UI/auth-base.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +class Auth : public QObject { + Q_OBJECT + +protected: + virtual void SaveInternal()=0; + virtual bool LoadInternal()=0; + + bool firstLoad = true; + + struct ErrorInfo { + std::string message; + std::string error; + + ErrorInfo(std::string message_, std::string error_) + : message(message_), error(error_) + {} + }; + +public: + enum class Type { + None, + OAuth_StreamKey + }; + + struct Def { + std::string service; + Type type; + }; + + typedef std::function ()> create_cb; + + inline Auth(const Def &d) : def(d) {} + virtual ~Auth() {} + + inline Type type() const {return def.type;} + inline const char *service() const {return def.service.c_str();} + + virtual void LoadUI() {} + + virtual void OnStreamConfig() {} + + static std::shared_ptr Create(const std::string &service); + static Type AuthType(const std::string &service); + static void Load(); + static void Save(); + +protected: + static void RegisterAuth(const Def &d, create_cb create); + +private: + Def def; +}; diff --git a/UI/auth-mixer.cpp b/UI/auth-mixer.cpp new file mode 100644 index 0000000..dc2cb5b --- /dev/null +++ b/UI/auth-mixer.cpp @@ -0,0 +1,336 @@ +#include "auth-mixer.hpp" + +#include +#include +#include + +#include +#include + +#include "window-basic-main.hpp" +#include "remote-text.hpp" +#include "window-dock.hpp" + +#include + +#include + +#include "ui-config.h" +#include "obf.h" + +using namespace json11; + +#include +extern QCef *cef; +extern QCefCookieManager *panel_cookies; + +/* ------------------------------------------------------------------------- */ + +#define MIXER_AUTH_URL \ + "https://obsproject.com/app-auth/mixer?action=redirect" +#define MIXER_TOKEN_URL \ + "https://obsproject.com/app-auth/mixer-token" + +#define MIXER_SCOPE_VERSION 1 + +static Auth::Def mixerDef = { + "Mixer", + Auth::Type::OAuth_StreamKey +}; + +/* ------------------------------------------------------------------------- */ + +MixerAuth::MixerAuth(const Def &d) + : OAuthStreamKey(d) +{ +} + +bool MixerAuth::GetChannelInfo(bool allow_retry) +try { + std::string client_id = MIXER_CLIENTID; + deobfuscate_str(&client_id[0], MIXER_HASH); + + if (!GetToken(MIXER_TOKEN_URL, client_id, MIXER_SCOPE_VERSION)) + return false; + if (token.empty()) + return false; + if (!key_.empty()) + return true; + + std::string auth; + auth += "Authorization: Bearer "; + auth += token; + + std::vector headers; + headers.push_back(std::string("Client-ID: ") + client_id); + headers.push_back(std::move(auth)); + + std::string output; + std::string error; + Json json; + bool success; + + if (id.empty()) { + auto func = [&] () { + success = GetRemoteFile( + "https://mixer.com/api/v1/users/current", + output, + error, + nullptr, + "application/json", + nullptr, + headers, + nullptr, + 5); + }; + + ExecThreadedWithoutBlocking( + func, + QTStr("Auth.LoadingChannel.Title"), + QTStr("Auth.LoadingChannel.Text").arg(service())); + if (!success || output.empty()) + throw ErrorInfo("Failed to get user info from remote", + error); + + Json json = Json::parse(output, error); + if (!error.empty()) + throw ErrorInfo("Failed to parse json", error); + + error = json["error"].string_value(); + if (!error.empty()) + throw ErrorInfo(error, + json["error_description"].string_value()); + + id = std::to_string(json["channel"]["id"].int_value()); + name = json["channel"]["token"].string_value(); + } + + /* ------------------ */ + + std::string url; + url += "https://mixer.com/api/v1/channels/"; + url += id; + url += "/details"; + + output.clear(); + + auto func = [&] () { + success = GetRemoteFile( + url.c_str(), + output, + error, + nullptr, + "application/json", + nullptr, + headers, + nullptr, + 5); + }; + + ExecThreadedWithoutBlocking( + func, + QTStr("Auth.LoadingChannel.Title"), + QTStr("Auth.LoadingChannel.Text").arg(service())); + if (!success || output.empty()) + throw ErrorInfo("Failed to get stream key from remote", error); + + json = Json::parse(output, error); + if (!error.empty()) + throw ErrorInfo("Failed to parse json", error); + + error = json["error"].string_value(); + if (!error.empty()) + throw ErrorInfo(error, json["error_description"].string_value()); + + std::string key_suffix = json["streamKey"].string_value(); + + /* Mixer does not throw an error; instead it gives you the channel data + * json without the data you normally have privileges for, which means + * it'll be an empty stream key usually. So treat empty stream key as + * an error. */ + if (key_suffix.empty()) { + if (allow_retry && RetryLogin()) { + return GetChannelInfo(false); + } + throw ErrorInfo("Auth Failure", "Could not get channel data"); + } + + key_ = id + "-" + key_suffix; + + return true; +} catch (ErrorInfo info) { + QString title = QTStr("Auth.ChannelFailure.Title"); + QString text = QTStr("Auth.ChannelFailure.Text") + .arg(service(), info.message.c_str(), info.error.c_str()); + + QMessageBox::warning(OBSBasic::Get(), title, text); + + blog(LOG_WARNING, "%s: %s: %s", + __FUNCTION__, + info.message.c_str(), + info.error.c_str()); + return false; +} + +void MixerAuth::SaveInternal() +{ + OBSBasic *main = OBSBasic::Get(); + config_set_string(main->Config(), service(), "Name", name.c_str()); + config_set_string(main->Config(), service(), "Id", id.c_str()); + if (uiLoaded) { + config_set_string(main->Config(), service(), "DockState", + main->saveState().toBase64().constData()); + } + OAuthStreamKey::SaveInternal(); +} + +static inline std::string get_config_str( + OBSBasic *main, + const char *section, + const char *name) +{ + const char *val = config_get_string(main->Config(), section, name); + return val ? val : ""; +} + +bool MixerAuth::LoadInternal() +{ + if (!cef) + return false; + + OBSBasic *main = OBSBasic::Get(); + name = get_config_str(main, service(), "Name"); + id = get_config_str(main, service(), "Id"); + firstLoad = false; + return OAuthStreamKey::LoadInternal(); +} + +class MixerChat : public OBSDock { +public: + inline MixerChat() : OBSDock() {} + + QScopedPointer widget; +}; + +void MixerAuth::LoadUI() +{ + if (!cef) + return; + if (uiLoaded) + return; + if (!GetChannelInfo()) + return; + + OBSBasic::InitBrowserPanelSafeBlock(); + OBSBasic *main = OBSBasic::Get(); + + std::string url; + url += "https://mixer.com/embed/chat/"; + url += id; + + QSize size = main->frameSize(); + QPoint pos = main->pos(); + + chat.reset(new MixerChat()); + chat->setObjectName("mixerChat"); + chat->resize(300, 600); + chat->setMinimumSize(200, 300); + chat->setWindowTitle(QTStr("Auth.Chat")); + chat->setAllowedAreas(Qt::AllDockWidgetAreas); + + QCefWidget *browser = cef->create_widget(nullptr, url, panel_cookies); + chat->setWidget(browser); + + main->addDockWidget(Qt::RightDockWidgetArea, chat.data()); + chatMenu.reset(main->AddDockWidget(chat.data())); + + /* ----------------------------------- */ + + chat->setFloating(true); + chat->move(pos.x() + size.width() - chat->width() - 50, pos.y() + 50); + + if (firstLoad) { + chat->setVisible(true); + } else { + const char *dockStateStr = config_get_string(main->Config(), + service(), "DockState"); + QByteArray dockState = + QByteArray::fromBase64(QByteArray(dockStateStr)); + main->restoreState(dockState); + } + + uiLoaded = true; +} + +bool MixerAuth::RetryLogin() +{ + if (!cef) + return false; + + OAuthLogin login(OBSBasic::Get(), MIXER_AUTH_URL, false); + cef->add_popup_whitelist_url("about:blank", &login); + + if (login.exec() == QDialog::Rejected) { + return false; + } + + std::shared_ptr auth = std::make_shared(mixerDef); + std::string client_id = MIXER_CLIENTID; + deobfuscate_str(&client_id[0], MIXER_HASH); + + return GetToken(MIXER_TOKEN_URL, client_id, MIXER_SCOPE_VERSION, + QT_TO_UTF8(login.GetCode()), true); +} + +std::shared_ptr MixerAuth::Login(QWidget *parent) +{ + if (!cef) { + return nullptr; + } + + OAuthLogin login(parent, MIXER_AUTH_URL, false); + cef->add_popup_whitelist_url("about:blank", &login); + + if (login.exec() == QDialog::Rejected) { + return nullptr; + } + + std::shared_ptr auth = std::make_shared(mixerDef); + + std::string client_id = MIXER_CLIENTID; + deobfuscate_str(&client_id[0], MIXER_HASH); + + if (!auth->GetToken(MIXER_TOKEN_URL, client_id, MIXER_SCOPE_VERSION, + QT_TO_UTF8(login.GetCode()))) { + return nullptr; + } + + std::string error; + if (auth->GetChannelInfo(false)) { + return auth; + } + + return nullptr; +} + +static std::shared_ptr CreateMixerAuth() +{ + return std::make_shared(mixerDef); +} + +static void DeleteCookies() +{ + if (panel_cookies) { + panel_cookies->DeleteCookies("mixer.com", std::string()); + panel_cookies->DeleteCookies("microsoft.com", std::string()); + } +} + +void RegisterMixerAuth() +{ + OAuth::RegisterOAuth( + mixerDef, + CreateMixerAuth, + MixerAuth::Login, + DeleteCookies); +} diff --git a/UI/auth-mixer.hpp b/UI/auth-mixer.hpp new file mode 100644 index 0000000..29a6e33 --- /dev/null +++ b/UI/auth-mixer.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "auth-oauth.hpp" + +class MixerChat; + +class MixerAuth : public OAuthStreamKey { + Q_OBJECT + + QSharedPointer chat; + QSharedPointer chatMenu; + bool uiLoaded = false; + + std::string name; + std::string id; + + virtual bool RetryLogin() override; + + virtual void SaveInternal() override; + virtual bool LoadInternal() override; + + bool GetChannelInfo(bool allow_retry = true); + + virtual void LoadUI() override; + +public: + MixerAuth(const Def &d); + + static std::shared_ptr Login(QWidget *parent); +}; diff --git a/UI/auth-oauth.cpp b/UI/auth-oauth.cpp new file mode 100644 index 0000000..976bca4 --- /dev/null +++ b/UI/auth-oauth.cpp @@ -0,0 +1,311 @@ +#include "auth-oauth.hpp" + +#include +#include +#include + +#include +#include + +#include "window-basic-main.hpp" +#include "remote-text.hpp" + +#include + +#include + +using namespace json11; + +#include +extern QCef *cef; +extern QCefCookieManager *panel_cookies; + +/* ------------------------------------------------------------------------- */ + +OAuthLogin::OAuthLogin(QWidget *parent, const std::string &url, bool token) + : QDialog (parent), + get_token (token) +{ + if (!cef) { + return; + } + + setWindowTitle("Auth"); + setMinimumSize(400, 400); + resize(700, 700); + + Qt::WindowFlags flags = windowFlags(); + Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; + setWindowFlags(flags & (~helpFlag)); + + OBSBasic::InitBrowserPanelSafeBlock(); + + cefWidget = cef->create_widget(nullptr, url, panel_cookies); + if (!cefWidget) { + fail = true; + return; + } + + connect(cefWidget, SIGNAL(titleChanged(const QString &)), + this, SLOT(setWindowTitle(const QString &))); + connect(cefWidget, SIGNAL(urlChanged(const QString &)), + this, SLOT(urlChanged(const QString &))); + + QPushButton *close = new QPushButton(QTStr("Cancel")); + connect(close, &QAbstractButton::clicked, + this, &QDialog::reject); + + QHBoxLayout *bottomLayout = new QHBoxLayout(); + bottomLayout->addStretch(); + bottomLayout->addWidget(close); + bottomLayout->addStretch(); + + QVBoxLayout *topLayout = new QVBoxLayout(this); + topLayout->addWidget(cefWidget); + topLayout->addLayout(bottomLayout); +} + +OAuthLogin::~OAuthLogin() +{ + delete cefWidget; +} + +int OAuthLogin::exec() +{ + if (cefWidget) { + return QDialog::exec(); + } + + return QDialog::Rejected; +} + +void OAuthLogin::urlChanged(const QString &url) +{ + std::string uri = get_token ? "access_token=" : "code="; + int code_idx = url.indexOf(uri.c_str()); + if (code_idx == -1) + return; + + if (url.left(22) != "https://obsproject.com") + return; + + code_idx += (int)uri.size(); + + int next_idx = url.indexOf("&", code_idx); + if (next_idx != -1) + code = url.mid(code_idx, next_idx - code_idx); + else + code = url.right(url.size() - code_idx); + + accept(); +} + +/* ------------------------------------------------------------------------- */ + +struct OAuthInfo { + Auth::Def def; + OAuth::login_cb login; + OAuth::delete_cookies_cb delete_cookies; +}; + +static std::vector loginCBs; + +void OAuth::RegisterOAuth(const Def &d, create_cb create, login_cb login, + delete_cookies_cb delete_cookies) +{ + OAuthInfo info = {d, login, delete_cookies}; + loginCBs.push_back(info); + RegisterAuth(d, create); +} + +std::shared_ptr OAuth::Login(QWidget *parent, const std::string &service) +{ + for (auto &a : loginCBs) { + if (service.find(a.def.service) != std::string::npos) { + return a.login(parent); + } + } + + return nullptr; +} + +void OAuth::DeleteCookies(const std::string &service) +{ + for (auto &a : loginCBs) { + if (service.find(a.def.service) != std::string::npos) { + a.delete_cookies(); + } + } +} + +void OAuth::SaveInternal() +{ + OBSBasic *main = OBSBasic::Get(); + config_set_string(main->Config(), service(), "RefreshToken", + refresh_token.c_str()); + config_set_string(main->Config(), service(), "Token", token.c_str()); + config_set_uint(main->Config(), service(), "ExpireTime", expire_time); + config_set_int(main->Config(), service(), "ScopeVer", currentScopeVer); +} + +static inline std::string get_config_str( + OBSBasic *main, + const char *section, + const char *name) +{ + const char *val = config_get_string(main->Config(), section, name); + return val ? val : ""; +} + +bool OAuth::LoadInternal() +{ + OBSBasic *main = OBSBasic::Get(); + refresh_token = get_config_str(main, service(), "RefreshToken"); + token = get_config_str(main, service(), "Token"); + expire_time = config_get_uint(main->Config(), service(), "ExpireTime"); + currentScopeVer = (int)config_get_int(main->Config(), service(), + "ScopeVer"); + return implicit + ? !token.empty() + : !refresh_token.empty(); +} + +bool OAuth::TokenExpired() +{ + if (token.empty()) + return true; + if ((uint64_t)time(nullptr) > expire_time - 5) + return true; + return false; +} + +bool OAuth::GetToken(const char *url, const std::string &client_id, + int scope_ver, const std::string &auth_code, bool retry) +try { + std::string output; + std::string error; + std::string desc; + + if (currentScopeVer > 0 && currentScopeVer < scope_ver) { + if (RetryLogin()) { + return true; + } else { + QString title = QTStr("Auth.InvalidScope.Title"); + QString text = QTStr("Auth.InvalidScope.Text") + .arg(service()); + + QMessageBox::warning(OBSBasic::Get(), title, text); + } + } + + if (auth_code.empty() && !TokenExpired()) { + return true; + } + + std::string post_data; + post_data += "action=redirect&client_id="; + post_data += client_id; + + if (!auth_code.empty()) { + post_data += "&grant_type=authorization_code&code="; + post_data += auth_code; + } else { + post_data += "&grant_type=refresh_token&refresh_token="; + post_data += refresh_token; + } + + bool success = false; + + auto func = [&] () { + success = GetRemoteFile( + url, + output, + error, + nullptr, + "application/x-www-form-urlencoded", + post_data.c_str(), + std::vector(), + nullptr, + 5); + }; + + ExecThreadedWithoutBlocking( + func, + QTStr("Auth.Authing.Title"), + QTStr("Auth.Authing.Text").arg(service())); + if (!success || output.empty()) + throw ErrorInfo("Failed to get token from remote", error); + + Json json = Json::parse(output, error); + if (!error.empty()) + throw ErrorInfo("Failed to parse json", error); + + /* -------------------------- */ + /* error handling */ + + error = json["error"].string_value(); + if (!retry && error == "invalid_grant") { + if (RetryLogin()) { + return true; + } + } + if (!error.empty()) + throw ErrorInfo(error, json["error_description"].string_value()); + + /* -------------------------- */ + /* success! */ + + expire_time = (uint64_t)time(nullptr) + json["expires_in"].int_value(); + token = json["access_token"].string_value(); + if (token.empty()) + throw ErrorInfo("Failed to get token from remote", error); + + if (!auth_code.empty()) { + refresh_token = json["refresh_token"].string_value(); + if (refresh_token.empty()) + throw ErrorInfo("Failed to get refresh token from " + "remote", error); + + currentScopeVer = scope_ver; + } + + return true; + +} catch (ErrorInfo info) { + if (!retry) { + QString title = QTStr("Auth.AuthFailure.Title"); + QString text = QTStr("Auth.AuthFailure.Text") + .arg(service(), info.message.c_str(), info.error.c_str()); + + QMessageBox::warning(OBSBasic::Get(), title, text); + } + + blog(LOG_WARNING, "%s: %s: %s", + __FUNCTION__, + info.message.c_str(), + info.error.c_str()); + return false; +} + +void OAuthStreamKey::OnStreamConfig() +{ + if (key_.empty()) + return; + + OBSBasic *main = OBSBasic::Get(); + obs_service_t *service = main->GetService(); + + obs_data_t *settings = obs_service_get_settings(service); + + bool bwtest = obs_data_get_bool(settings, "bwtest"); + + if (bwtest && strcmp(this->service(), "Twitch") == 0) + obs_data_set_string(settings, "key", + (key_ + "?bandwidthtest=true").c_str()); + else + obs_data_set_string(settings, "key", key_.c_str()); + + obs_service_update(service, settings); + + obs_data_release(settings); +} diff --git a/UI/auth-oauth.hpp b/UI/auth-oauth.hpp new file mode 100644 index 0000000..7cf035c --- /dev/null +++ b/UI/auth-oauth.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +#include "auth-base.hpp" + +class QCefWidget; + +class OAuthLogin : public QDialog { + Q_OBJECT + + QCefWidget *cefWidget = nullptr; + QString code; + bool get_token = false; + bool fail = false; + +public: + OAuthLogin(QWidget *parent, const std::string &url, bool token); + ~OAuthLogin(); + + inline QString GetCode() const {return code;} + inline bool LoadFail() const {return fail;} + + virtual int exec() override; + +public slots: + void urlChanged(const QString &url); +}; + +class OAuth : public Auth { + Q_OBJECT + +public: + inline OAuth(const Def &d) : Auth(d) {} + + typedef std::function (QWidget *)> login_cb; + typedef std::function delete_cookies_cb; + + static std::shared_ptr Login(QWidget *parent, + const std::string &service); + static void DeleteCookies(const std::string &service); + + static void RegisterOAuth(const Def &d, create_cb create, + login_cb login, delete_cookies_cb delete_cookies); + +protected: + std::string refresh_token; + std::string token; + bool implicit = false; + uint64_t expire_time = 0; + int currentScopeVer = 0; + + virtual void SaveInternal() override; + virtual bool LoadInternal() override; + + virtual bool RetryLogin()=0; + bool TokenExpired(); + bool GetToken(const char *url, const std::string &client_id, + int scope_ver, + const std::string &auth_code = std::string(), + bool retry = false); +}; + +class OAuthStreamKey : public OAuth { + Q_OBJECT + +protected: + std::string key_; + +public: + inline OAuthStreamKey(const Def &d) : OAuth(d) {} + + inline const std::string &key() const {return key_;} + + virtual void OnStreamConfig() override; +}; diff --git a/UI/auth-restream.cpp b/UI/auth-restream.cpp new file mode 100644 index 0000000..f10e8cb --- /dev/null +++ b/UI/auth-restream.cpp @@ -0,0 +1,283 @@ +#include "auth-restream.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "window-basic-main.hpp" +#include "remote-text.hpp" +#include "ui-config.h" +#include "obf.h" +#include + +using namespace json11; + +extern QCef *cef; +extern QCefCookieManager *panel_cookies; + +/* ------------------------------------------------------------------------- */ + +#define RESTREAM_AUTH_URL "https://obsproject.com/app-auth/restream?action=redirect" +#define RESTREAM_TOKEN_URL "https://obsproject.com/app-auth/restream-token" +#define RESTREAM_STREAMKEY_URL "https://api.restream.io/v2/user/streamKey" +#define RESTREAM_SCOPE_VERSION 1 + + +static Auth::Def restreamDef = { + "Restream", + Auth::Type::OAuth_StreamKey +}; + +/* ------------------------------------------------------------------------- */ + +RestreamAuth::RestreamAuth(const Def &d) + : OAuthStreamKey(d) +{ +} + +bool RestreamAuth::GetChannelInfo() +try { + std::string client_id = RESTREAM_CLIENTID; + deobfuscate_str(&client_id[0], RESTREAM_HASH); + + if (!GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION)) + return false; + if (token.empty()) + return false; + if (!key_.empty()) + return true; + + std::string auth; + auth += "Authorization: Bearer "; + auth += token; + + std::vector headers; + headers.push_back(std::string("Client-ID: ") + client_id); + headers.push_back(std::move(auth)); + + std::string output; + std::string error; + Json json; + bool success; + + auto func = [&] () { + success = GetRemoteFile( + RESTREAM_STREAMKEY_URL, + output, + error, + nullptr, + "application/json", + nullptr, + headers, + nullptr, + 5); + }; + + ExecThreadedWithoutBlocking( + func, + QTStr("Auth.LoadingChannel.Title"), + QTStr("Auth.LoadingChannel.Text").arg(service())); + if (!success || output.empty()) + throw ErrorInfo("Failed to get stream key from remote", error); + + json = Json::parse(output, error); + if (!error.empty()) + throw ErrorInfo("Failed to parse json", error); + + error = json["error"].string_value(); + if (!error.empty()) + throw ErrorInfo(error, json["error_description"].string_value()); + + key_ = json["streamKey"].string_value(); + + return true; +} catch (ErrorInfo info) { + QString title = QTStr("Auth.ChannelFailure.Title"); + QString text = QTStr("Auth.ChannelFailure.Text") + .arg(service(), info.message.c_str(), info.error.c_str()); + + QMessageBox::warning(OBSBasic::Get(), title, text); + + blog(LOG_WARNING, "%s: %s: %s", + __FUNCTION__, + info.message.c_str(), + info.error.c_str()); + return false; +} + +void RestreamAuth::SaveInternal() +{ + OBSBasic *main = OBSBasic::Get(); + config_set_string(main->Config(), service(), "DockState", + main->saveState().toBase64().constData()); + OAuthStreamKey::SaveInternal(); +} + +static inline std::string get_config_str( + OBSBasic *main, + const char *section, + const char *name) +{ + const char *val = config_get_string(main->Config(), section, name); + return val ? val : ""; +} + +bool RestreamAuth::LoadInternal() +{ + firstLoad = false; + return OAuthStreamKey::LoadInternal(); +} + +class RestreamWidget : public QDockWidget { +public: + inline RestreamWidget() : QDockWidget() {} + + QScopedPointer widget; +}; + +void RestreamAuth::LoadUI() +{ + if (uiLoaded) + return; + if (!GetChannelInfo()) + return; + + OBSBasic::InitBrowserPanelSafeBlock(); + OBSBasic *main = OBSBasic::Get(); + + QCefWidget *browser; + std::string url; + std::string script; + + /* ----------------------------------- */ + + url = "https://restream.io/chat-application"; + + QSize size = main->frameSize(); + QPoint pos = main->pos(); + + chat.reset(new RestreamWidget()); + chat->setObjectName("restreamChat"); + chat->resize(420, 600); + chat->setMinimumSize(200, 300); + chat->setWindowTitle(QTStr("Auth.Chat")); + chat->setAllowedAreas(Qt::AllDockWidgetAreas); + + browser = cef->create_widget(nullptr, url, panel_cookies); + chat->setWidget(browser); + + main->addDockWidget(Qt::RightDockWidgetArea, chat.data()); + chatMenu.reset(main->AddDockWidget(chat.data())); + + /* ----------------------------------- */ + + url = "https://restream.io/titles/embed"; + + info.reset(new RestreamWidget()); + info->setObjectName("restreamInfo"); + info->resize(410, 600); + info->setMinimumSize(200, 150); + info->setWindowTitle(QTStr("Auth.StreamInfo")); + info->setAllowedAreas(Qt::AllDockWidgetAreas); + + browser = cef->create_widget(nullptr, url, panel_cookies); + info->setWidget(browser); + + main->addDockWidget(Qt::RightDockWidgetArea, info.data()); + infoMenu.reset(main->AddDockWidget(info.data())); + + /* ----------------------------------- */ + + chat->setFloating(true); + info->setFloating(true); + chat->move(pos.x() + size.width() - chat->width() - 50, pos.y() + 50); + info->move(pos.x() + 40, pos.y() + 50); + + if (firstLoad) { + chat->setVisible(true); + info->setVisible(true); + } + else { + const char *dockStateStr = config_get_string(main->Config(), + service(), "DockState"); + QByteArray dockState = + QByteArray::fromBase64(QByteArray(dockStateStr)); + main->restoreState(dockState); + } + + uiLoaded = true; +} + +bool RestreamAuth::RetryLogin() +{ + OAuthLogin login(OBSBasic::Get(), RESTREAM_AUTH_URL, false); + cef->add_popup_whitelist_url("about:blank", &login); + if (login.exec() == QDialog::Rejected) { + return false; + } + + std::shared_ptr auth = + std::make_shared(restreamDef); + + std::string client_id = RESTREAM_CLIENTID; + deobfuscate_str(&client_id[0], RESTREAM_HASH); + + return GetToken(RESTREAM_TOKEN_URL, client_id, + RESTREAM_SCOPE_VERSION, + QT_TO_UTF8(login.GetCode()), true); +} + +std::shared_ptr RestreamAuth::Login(QWidget *parent) +{ + OAuthLogin login(parent, RESTREAM_AUTH_URL, false); + cef->add_popup_whitelist_url("about:blank", &login); + + if (login.exec() == QDialog::Rejected) { + return nullptr; + } + + std::shared_ptr auth = + std::make_shared(restreamDef); + + std::string client_id = RESTREAM_CLIENTID; + deobfuscate_str(&client_id[0], RESTREAM_HASH); + + if (!auth->GetToken(RESTREAM_TOKEN_URL, client_id, + RESTREAM_SCOPE_VERSION, + QT_TO_UTF8(login.GetCode()))) { + return nullptr; + } + + std::string error; + if (auth->GetChannelInfo()) { + return auth; + } + + return nullptr; +} + +static std::shared_ptr CreateRestreamAuth() +{ + return std::make_shared(restreamDef); +} + +static void DeleteCookies() +{ + if (panel_cookies) { + panel_cookies->DeleteCookies("restream.io", std::string()); + } +} + +void RegisterRestreamAuth() +{ + OAuth::RegisterOAuth( + restreamDef, + CreateRestreamAuth, + RestreamAuth::Login, + DeleteCookies); +} diff --git a/UI/auth-restream.hpp b/UI/auth-restream.hpp new file mode 100644 index 0000000..5e66fb0 --- /dev/null +++ b/UI/auth-restream.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "auth-oauth.hpp" + +class RestreamWidget; + +class RestreamAuth : public OAuthStreamKey { + Q_OBJECT + + QSharedPointer chat; + QSharedPointer info; + QSharedPointer chatMenu; + QSharedPointer infoMenu; + bool uiLoaded = false; + + virtual bool RetryLogin() override; + + virtual void SaveInternal() override; + virtual bool LoadInternal() override; + + bool GetChannelInfo(); + + virtual void LoadUI() override; + +public: + RestreamAuth(const Def &d); + + static std::shared_ptr Login(QWidget *parent); +}; diff --git a/UI/auth-twitch.cpp b/UI/auth-twitch.cpp new file mode 100644 index 0000000..3206c32 --- /dev/null +++ b/UI/auth-twitch.cpp @@ -0,0 +1,483 @@ +#include "auth-twitch.hpp" + +#include +#include +#include + +#include +#include + +#include "window-basic-main.hpp" +#include "remote-text.hpp" +#include "window-dock.hpp" + +#include + +#include "ui-config.h" +#include "obf.h" + +using namespace json11; + +#include +extern QCef *cef; +extern QCefCookieManager *panel_cookies; + +/* ------------------------------------------------------------------------- */ + +#define TWITCH_AUTH_URL \ + "https://obsproject.com/app-auth/twitch?action=redirect" +#define TWITCH_TOKEN_URL \ + "https://obsproject.com/app-auth/twitch-token" +#define ACCEPT_HEADER \ + "Accept: application/vnd.twitchtv.v5+json" + +#define TWITCH_SCOPE_VERSION 1 + +static Auth::Def twitchDef = { + "Twitch", + Auth::Type::OAuth_StreamKey +}; + +/* ------------------------------------------------------------------------- */ + +TwitchAuth::TwitchAuth(const Def &d) + : OAuthStreamKey(d) +{ + if (!cef) + return; + + cef->add_popup_whitelist_url( + "https://twitch.tv/popout/frankerfacez/chat?ffz-settings", + this); + uiLoadTimer.setSingleShot(true); + uiLoadTimer.setInterval(500); + connect(&uiLoadTimer, &QTimer::timeout, + this, &TwitchAuth::TryLoadSecondaryUIPanes); +} + +bool TwitchAuth::GetChannelInfo() +try { + std::string client_id = TWITCH_CLIENTID; + deobfuscate_str(&client_id[0], TWITCH_HASH); + + if (!GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION)) + return false; + if (token.empty()) + return false; + if (!key_.empty()) + return true; + + std::string auth; + auth += "Authorization: OAuth "; + auth += token; + + std::vector headers; + headers.push_back(std::string("Client-ID: ") + client_id); + headers.push_back(ACCEPT_HEADER); + headers.push_back(std::move(auth)); + + std::string output; + std::string error; + long error_code = 0; + + bool success = false; + + auto func = [&] () { + success = GetRemoteFile( + "https://api.twitch.tv/kraken/channel", + output, + error, + &error_code, + "application/json", + nullptr, + headers, + nullptr, + 5); + }; + + ExecThreadedWithoutBlocking( + func, + QTStr("Auth.LoadingChannel.Title"), + QTStr("Auth.LoadingChannel.Text").arg(service())); + if (error_code == 403) { + OBSMessageBox::warning(OBSBasic::Get(), + Str("TwitchAuth.TwoFactorFail.Title"), + Str("TwitchAuth.TwoFactorFail.Text"), + true); + blog(LOG_WARNING, "%s: %s", + __FUNCTION__, + "Got 403 from Twitch, user probably does not " + "have two-factor authentication enabled on " + "their account"); + return false; + } + + if (!success || output.empty()) + throw ErrorInfo("Failed to get text from remote", error); + + Json json = Json::parse(output, error); + if (!error.empty()) + throw ErrorInfo("Failed to parse json", error); + + error = json["error"].string_value(); + if (!error.empty()) { + if (error == "Unauthorized") { + if (RetryLogin()) { + return GetChannelInfo(); + } + throw ErrorInfo(error, + json["message"].string_value()); + } + throw ErrorInfo(error, json["error_description"].string_value()); + } + + name = json["name"].string_value(); + key_ = json["stream_key"].string_value(); + + return true; +} catch (ErrorInfo info) { + QString title = QTStr("Auth.ChannelFailure.Title"); + QString text = QTStr("Auth.ChannelFailure.Text") + .arg(service(), info.message.c_str(), info.error.c_str()); + + QMessageBox::warning(OBSBasic::Get(), title, text); + + blog(LOG_WARNING, "%s: %s: %s", + __FUNCTION__, + info.message.c_str(), + info.error.c_str()); + return false; +} + +void TwitchAuth::SaveInternal() +{ + OBSBasic *main = OBSBasic::Get(); + config_set_string(main->Config(), service(), "Name", name.c_str()); + if (uiLoaded) { + config_set_string(main->Config(), service(), "DockState", + main->saveState().toBase64().constData()); + } + OAuthStreamKey::SaveInternal(); +} + +static inline std::string get_config_str( + OBSBasic *main, + const char *section, + const char *name) +{ + const char *val = config_get_string(main->Config(), section, name); + return val ? val : ""; +} + +bool TwitchAuth::LoadInternal() +{ + if (!cef) + return false; + + OBSBasic *main = OBSBasic::Get(); + name = get_config_str(main, service(), "Name"); + firstLoad = false; + return OAuthStreamKey::LoadInternal(); +} + +class TwitchWidget : public OBSDock { +public: + inline TwitchWidget() : OBSDock() {} + + QScopedPointer widget; + + inline void SetWidget(QCefWidget *widget_) + { + setWidget(widget_); + widget.reset(widget_); + } +}; + +static const char *ffz_script = "\ +var ffz = document.createElement('script');\ +ffz.setAttribute('src','https://cdn.frankerfacez.com/script/script.min.js');\ +document.head.appendChild(ffz);"; + +static const char *bttv_script = "\ +localStorage.setItem('bttv_clickTwitchEmotes', true);\ +localStorage.setItem('bttv_darkenedMode', true);\ +localStorage.setItem('bttv_bttvGIFEmotes', true);\ +var bttv = document.createElement('script');\ +bttv.setAttribute('src','https://cdn.betterttv.net/betterttv.js');\ +document.head.appendChild(bttv);"; + +static const char *referrer_script1 = "\ +Object.defineProperty(document, 'referrer', {get : function() { return '"; +static const char *referrer_script2 = "'; }});"; + +void TwitchAuth::LoadUI() +{ + if (!cef) + return; + if (uiLoaded) + return; + if (!GetChannelInfo()) + return; + + OBSBasic::InitBrowserPanelSafeBlock(); + OBSBasic *main = OBSBasic::Get(); + + QCefWidget *browser; + std::string url; + std::string script; + + std::string moderation_tools_url; + moderation_tools_url = "https://www.twitch.tv/"; + moderation_tools_url += name; + moderation_tools_url += "/dashboard/settings/moderation?no-reload=true"; + + /* ----------------------------------- */ + + url = "https://www.twitch.tv/popout/"; + url += name; + url += "/chat"; + + QSize size = main->frameSize(); + QPoint pos = main->pos(); + + chat.reset(new TwitchWidget()); + chat->setObjectName("twitchChat"); + chat->resize(300, 600); + chat->setMinimumSize(200, 300); + chat->setWindowTitle(QTStr("Auth.Chat")); + chat->setAllowedAreas(Qt::AllDockWidgetAreas); + + browser = cef->create_widget(nullptr, url, panel_cookies); + chat->SetWidget(browser); + cef->add_force_popup_url(moderation_tools_url, chat.data()); + + script = bttv_script; + script += ffz_script; + browser->setStartupScript(script); + + main->addDockWidget(Qt::RightDockWidgetArea, chat.data()); + chatMenu.reset(main->AddDockWidget(chat.data())); + + /* ----------------------------------- */ + + chat->setFloating(true); + chat->move(pos.x() + size.width() - chat->width() - 50, pos.y() + 50); + + if (firstLoad) { + chat->setVisible(true); + } else { + const char *dockStateStr = config_get_string(main->Config(), + service(), "DockState"); + QByteArray dockState = + QByteArray::fromBase64(QByteArray(dockStateStr)); + main->restoreState(dockState); + } + + TryLoadSecondaryUIPanes(); + + uiLoaded = true; +} + +void TwitchAuth::LoadSecondaryUIPanes() +{ + OBSBasic *main = OBSBasic::Get(); + + QCefWidget *browser; + std::string url; + std::string script; + + QSize size = main->frameSize(); + QPoint pos = main->pos(); + + script = "localStorage.setItem('twilight.theme', 1);"; + script += referrer_script1; + script += "https://www.twitch.tv/"; + script += name; + script += "/dashboard/live"; + script += referrer_script2; + script += bttv_script; + script += ffz_script; + + /* ----------------------------------- */ + + url = "https://www.twitch.tv/popout/"; + url += name; + url += "/dashboard/live/stream-info"; + + info.reset(new TwitchWidget()); + info->setObjectName("twitchInfo"); + info->resize(300, 650); + info->setMinimumSize(200, 300); + info->setWindowTitle(QTStr("Auth.StreamInfo")); + info->setAllowedAreas(Qt::AllDockWidgetAreas); + + browser = cef->create_widget(nullptr, url, panel_cookies); + info->SetWidget(browser); + browser->setStartupScript(script); + + main->addDockWidget(Qt::RightDockWidgetArea, info.data()); + infoMenu.reset(main->AddDockWidget(info.data())); + + /* ----------------------------------- */ + + url = "https://www.twitch.tv/popout/"; + url += name; + url += "/dashboard/live/stats"; + + stat.reset(new TwitchWidget()); + stat->setObjectName("twitchStats"); + stat->resize(200, 250); + stat->setMinimumSize(200, 150); + stat->setWindowTitle(QTStr("TwitchAuth.Stats")); + stat->setAllowedAreas(Qt::AllDockWidgetAreas); + + browser = cef->create_widget(nullptr, url, panel_cookies); + stat->SetWidget(browser); + browser->setStartupScript(script); + + main->addDockWidget(Qt::RightDockWidgetArea, stat.data()); + statMenu.reset(main->AddDockWidget(stat.data())); + + /* ----------------------------------- */ + + url = "https://www.twitch.tv/popout/"; + url += name; + url += "/dashboard/live/activity-feed"; + + feed.reset(new TwitchWidget()); + feed->setObjectName("twitchFeed"); + feed->resize(300, 650); + feed->setMinimumSize(200, 300); + feed->setWindowTitle(QTStr("TwitchAuth.Feed")); + feed->setAllowedAreas(Qt::AllDockWidgetAreas); + + browser = cef->create_widget(nullptr, url, panel_cookies); + feed->SetWidget(browser); + browser->setStartupScript(script); + + main->addDockWidget(Qt::RightDockWidgetArea, feed.data()); + feedMenu.reset(main->AddDockWidget(feed.data())); + + /* ----------------------------------- */ + + info->setFloating(true); + stat->setFloating(true); + feed->setFloating(true); + + QSize statSize = stat->frameSize(); + + info->move(pos.x() + 50, pos.y() + 50); + stat->move(pos.x() + size.width() / 2 - statSize.width() / 2, + pos.y() + size.height() / 2 - statSize.height() / 2); + feed->move(pos.x() + 100, pos.y() + 100); + + if (firstLoad) { + info->setVisible(true); + stat->setVisible(false); + feed->setVisible(false); + } else { + uint32_t lastVersion = config_get_int(App()->GlobalConfig(), "General", + "LastVersion"); + + if (lastVersion <= MAKE_SEMANTIC_VERSION(23, 0, 2)) { + feed->setVisible(false); + } + + const char *dockStateStr = config_get_string(main->Config(), + service(), "DockState"); + QByteArray dockState = + QByteArray::fromBase64(QByteArray(dockStateStr)); + main->restoreState(dockState); + } +} + +/* Twitch.tv has an OAuth for itself. If we try to load multiple panel pages + * at once before it's OAuth'ed itself, they will all try to perform the auth + * process at the same time, get their own request codes, and only the last + * code will be valid -- so one or more panels are guaranteed to fail. + * + * To solve this, we want to load just one panel first (the chat), and then all + * subsequent panels should only be loaded once we know that Twitch has auth'ed + * itself (if the cookie "auth-token" exists for twitch.tv). + * + * This is annoying to deal with. */ +void TwitchAuth::TryLoadSecondaryUIPanes() +{ + QPointer this_ = this; + + auto cb = [this_] (bool found) + { + if (!this_) { + return; + } + + if (!found) { + QMetaObject::invokeMethod(&this_->uiLoadTimer, + "start"); + } else { + QMetaObject::invokeMethod(this_, "LoadSecondaryUIPanes"); + } + }; + + panel_cookies->CheckForCookie("https://www.twitch.tv", "auth-token", cb); +} + +bool TwitchAuth::RetryLogin() +{ + OAuthLogin login(OBSBasic::Get(), TWITCH_AUTH_URL, false); + if (login.exec() == QDialog::Rejected) { + return false; + } + + std::shared_ptr auth = std::make_shared(twitchDef); + std::string client_id = TWITCH_CLIENTID; + deobfuscate_str(&client_id[0], TWITCH_HASH); + + return GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, + QT_TO_UTF8(login.GetCode()), true); +} + +std::shared_ptr TwitchAuth::Login(QWidget *parent) +{ + OAuthLogin login(parent, TWITCH_AUTH_URL, false); + if (login.exec() == QDialog::Rejected) { + return nullptr; + } + + std::shared_ptr auth = std::make_shared(twitchDef); + + std::string client_id = TWITCH_CLIENTID; + deobfuscate_str(&client_id[0], TWITCH_HASH); + + if (!auth->GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, + QT_TO_UTF8(login.GetCode()))) { + return nullptr; + } + + std::string error; + if (auth->GetChannelInfo()) { + return auth; + } + + return nullptr; +} + +static std::shared_ptr CreateTwitchAuth() +{ + return std::make_shared(twitchDef); +} + +static void DeleteCookies() +{ + if (panel_cookies) + panel_cookies->DeleteCookies("twitch.tv", std::string()); +} + +void RegisterTwitchAuth() +{ + OAuth::RegisterOAuth( + twitchDef, + CreateTwitchAuth, + TwitchAuth::Login, + DeleteCookies); +} diff --git a/UI/auth-twitch.hpp b/UI/auth-twitch.hpp new file mode 100644 index 0000000..76698d1 --- /dev/null +++ b/UI/auth-twitch.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +#include "auth-oauth.hpp" + +class TwitchWidget; + +class TwitchAuth : public OAuthStreamKey { + Q_OBJECT + + friend class TwitchLogin; + + QSharedPointer chat; + QSharedPointer info; + QSharedPointer stat; + QSharedPointer feed; + QSharedPointer chatMenu; + QSharedPointer infoMenu; + QSharedPointer statMenu; + QSharedPointer feedMenu; + bool uiLoaded = false; + + std::string name; + + virtual bool RetryLogin() override; + + virtual void SaveInternal() override; + virtual bool LoadInternal() override; + + bool GetChannelInfo(); + + virtual void LoadUI() override; + +public: + TwitchAuth(const Def &d); + + static std::shared_ptr Login(QWidget *parent); + + QTimer uiLoadTimer; + +public slots: + void TryLoadSecondaryUIPanes(); + void LoadSecondaryUIPanes(); +}; diff --git a/UI/balance-slider.hpp b/UI/balance-slider.hpp new file mode 100644 index 0000000..06be1b5 --- /dev/null +++ b/UI/balance-slider.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class BalanceSlider : public QSlider { + Q_OBJECT + +public: + inline BalanceSlider(QWidget *parent = 0) : QSlider(parent) {} + +signals: + void doubleClicked(); + +protected: + void mouseDoubleClickEvent(QMouseEvent *event) + { + emit doubleClicked(); + event->accept(); + } +}; diff --git a/UI/clickable-label.hpp b/UI/clickable-label.hpp new file mode 100644 index 0000000..3c76a8b --- /dev/null +++ b/UI/clickable-label.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class ClickableLabel : public QLabel { + Q_OBJECT + +public: + inline ClickableLabel(QWidget *parent = 0) : QLabel(parent) {} + +signals: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *event) + { + emit clicked(); + event->accept(); + } +}; diff --git a/UI/combobox-ignorewheel.cpp b/UI/combobox-ignorewheel.cpp new file mode 100644 index 0000000..c7c24eb --- /dev/null +++ b/UI/combobox-ignorewheel.cpp @@ -0,0 +1,14 @@ +#include "combobox-ignorewheel.hpp" + +ComboBoxIgnoreScroll::ComboBoxIgnoreScroll(QWidget *parent) : QComboBox(parent) +{ + setFocusPolicy(Qt::StrongFocus); +} + +void ComboBoxIgnoreScroll::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QComboBox::wheelEvent(event); +} diff --git a/UI/combobox-ignorewheel.hpp b/UI/combobox-ignorewheel.hpp new file mode 100644 index 0000000..479e1bb --- /dev/null +++ b/UI/combobox-ignorewheel.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +class ComboBoxIgnoreScroll : public QComboBox { + Q_OBJECT + +public: + ComboBoxIgnoreScroll(QWidget *parent = nullptr); + +protected: + virtual void wheelEvent(QWheelEvent *event) override; +}; diff --git a/UI/data/images/overflow.png b/UI/data/images/overflow.png new file mode 100644 index 0000000000000000000000000000000000000000..5bddc3384e1c342adbaa80d47a609a93608c5343 GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!phSslL`iUdT1k0gQ7S`0VrE{6US4X6f{C7? zo@t7E1jw4to-U3d5$;FNF|KP>5O5B>+x7dt@-0KHDU97*|DLG$Pr7e?`P+5}j)zl| zpEt%oKK+DuTY)?4XPfu?_Xdjguj<>8`(Z_wduZXEvPU4kMPBsB_9pkxxI5o|ENpWR zUAiN;s$Whn^y(u8*#nDL?Y6ihcVMxWD&JM+ijbwp7FICyuZnu~04TQVnneoxhZV0p zWhMcozMAkoXS7*WCCqN!;2xU)c*EcQ_I1y@Epnvm7Cbv_@uYE9|Kka4Syo3Be`>rE zX@8@5*^uwu3krCYI*jAj4;002ovPDHLk FV1i+YZ8rb_ diff --git a/UI/data/themes/Acri/down_arrow.png b/UI/data/themes/Acri/down_arrow.png deleted file mode 100644 index 39d31da89d6edfef47f642073fda4202e2b08b9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV7%t(;uuoF`1U$uJ5#j8@rRjN zUlYZ$7=O1Os`N6rlk1|2)ZhF+l{J3ST>`qF%qDNFKd583`{CO5?}GpTT>D^9*&IAMeO6Kx>=SXD%`|P5g zu%8@i-;ccy>iDtnf?!Ae+R*9ddsY9{RJ=Z2zjcZo(?@5M?zYd-?ib>IK7N$I;xGKa z;A#KYZ|nYV?-9)Deem*8{^lQ-1?x5n_AL0q=BBVuup^)4k#yq^C^IjUB`<>|FP$YX zjU_LYB`<{~?{W98PW?Ax`*uiMG`xQ&uy1p7$9scE))Ri{muY;I^{HtWzR!5H+~cEc zNKL!w|C^X*A{h^2PVIO2Q{}!v{$BN-y+2C(9%#?|A=>v~#($lIdR+PlDZ@3IP{*Z?v2 XZxz<#oD^-J1mbwQ`njxgN@xNA6+!JL diff --git a/UI/data/themes/Acri/minus.png b/UI/data/themes/Acri/minus.png deleted file mode 100644 index 26e35b4e40d74afb525d36578380cf1f4bd93cae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxc&!&(3-C~KoQ20AirP+ zhi5m^fE+PT7srqa#$<-3z=Z$uJpU#fVR$9Y$}rns+dj8Ezze8?!PC{xWt~$(699=Z BAl(1} diff --git a/UI/data/themes/Acri/mute.png b/UI/data/themes/Acri/mute.png deleted file mode 100644 index 682f91edfac637fd742aecaab27751f3e2bcf290..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^F3W0Ln;`TUS?!taujL*cr#Qx zV)bd4MoahDMXO7i99DEN35XeTTOIDw6a4c#X~Us+cFp$>d`Ns|tNwoSm)LU~-Pno~ z8pRG>lDhGT*|kn%gR}JBg?)Jn?Y}4g=i~h_<6$fFvK0Z!J-ilI1lDCJywPnGV(dCM zBZ5g(N@Ldo3F#9b8VZ~n3O3n25@hwd^U>$O+i!)N?v+zF`A9!Dx>?J(SLv~l%nbWa QK&LWzy85}Sb4q9e0IU#H@c;k- diff --git a/UI/data/themes/Acri/plus.png b/UI/data/themes/Acri/plus.png deleted file mode 100644 index 69325d1d810f6a5bdac715a356a2d6ac9e55287b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxc&!&(3-C~KoQ20AirP+ zhi5m^fE;O07srqa#^eKBKlpoe+WyuX{BRaZW?>UiV_;ZyIP9gTfaMpUG6qjqKbLh* G2~7Y9L*)`>6r7gQLa|^RFhdevXTXrqZ-s0k{?3QQqfAxH4-&?=B7d_{&zrVjg z#oPDZ4#S;~QHf7f`wGa#wK&heagStA0d07LrQS7Lz%rsRsNJRsXu^RUL*)>#qrMBV zxKLkiS^mOPnSczusZT+x_B|}UlmcW%vnmwn@oFFewVI710YA76HXt#WfH}LjP;B0U zzaL@_KI}fkHtl=OOWTk`4*~n?I}nE(^_7;Tvj)661x#WQkr>l%58*BWl8q}jKjlLF n;1OxT{FzGeiCQxY`S1J>z?^g7k8|*Z00000NkvXXu0mjfWa58k diff --git a/UI/data/themes/Acri/up_arrow.png b/UI/data/themes/Acri/up_arrow.png deleted file mode 100644 index 89a5d470791916214b28709585ad0a1cd332bf13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU_9XI;uuoF`1Vd?nsT5B+k=^R zV;PtdQZt8ZHf4Bb2AK%_Q=i&QV-`@&NJMjHaMZL^-mP-A~V-|Ydo9=zsX(+N! zh$Eh}Wj&fH0!YHALzuinm`+EZlR0GG|KY4^&F9EIOWmV;rX}Wi8|;7h_TYtYi4oO? zOa4lno5zzH-?q`*`Nn*hbC3;ssD04+?|&OriPrTjk9GO)O@CFhGe%5kpHNL&sovFp z)BNlA{M$P(d1YM7{xw1-Gi8-eo86vorx6!^{d3H!@Q3<~S$xAL-p#U_T?zE~e$^SW zb-x$f7gn|4+<#kW&aDr(c~<{awV0Bn{O6t52F&c3-< z&g7WnE{ECfv#;)z`;z`bXkstF8gh^`Fxaf(&z$?2`{P2VCm;z=S3j3^P6<7gR`t)iS1tQ{Vq=8a+!t@=-S1EK zoA&0>|GTrRJ&#*50!;;hV});)U*78Z=hePfQjSf}D`U@z@2r1yOQFuP_MJV?;SbrB zb!jZUN7ldNe>{76(KWY%bwb;A)zANMyC|+_!NeawXZ}xa+TWMRl6xdxy`ZUGup^)4 zk#yq^w+l&s6pwRA_3U5$-t9uzpWJ!-pFS~F&d~frkUCaG&FdITIyJ3!ozu_QBnQojD;tX0C6Ia)j*f#@cz; zpZ+PC^|4ps?Yh^?jvsly`bYh|h#F;&%_eW|TQ}|gc=lb&BWcIGuk!5wZB)L|bJT#R zGWnypUDMqwT4wLN;%hIs+BQ`P)-P1{n0@04Z}K+5KXK=Je{@S8^qnQsXMdO@?`ZrV zj=c1a`VaS6=Ig)ye#xVIi|6VeD+JfpFD z<7KV4*GexR23oY2|6dBrav6Kx!<0rUG+9iyBPwb9IR^AVv+{~}_n)==@a;*^;M=2C z$E~c<_N(Lmv->RnWj@}PZTz9zhnC*FVdQ&MBb@00FSIrT_o{ diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index 934706a..693a556 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -86,7 +86,7 @@ QWidget::disabled { color: 2px solid rgb(200,199,200); /* lighter */ } -QAbstractItemView { +QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { background-color: rgb(31,30,31); /* veryDark */ } @@ -100,21 +100,27 @@ QMenuBar::item { background-color: rgb(58,57,58); /* dark */ } -QListWidget::item:selected:!active { +QListWidget::item:selected:!active, +SourceTree::item:selected:!active { color: rgb(255, 255, 255); background-color: rgb(48,47,48); } +QListWidget QLineEdit, +SourceTree QLineEdit { + padding-top: 0px; + padding-bottom: 0px; + padding-right: 0; + padding-left: 2px; + border: none; + border-radius: none; +} + /* Dock Widget */ QDockWidget::title { text-align: center; - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(86,85,86), - stop: 0.1 rgb(82,81,82), - stop: 0.5 rgb(78,77,78), - stop: 0.9 rgb(74,73,74), - stop: 1 rgb(70,69,70)); + background-color: rgb(70,69,70); } QDockWidget::close-button, QDockWidget::float-button { @@ -148,28 +154,24 @@ QGroupBox { } QScrollBar:vertical { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(54, 53, 54), - stop: 1 rgb(58,57,58)); /* dark */ + background-color: rgb(58,57,58); width: 14px; + margin: 0px; } QScrollBar::handle:vertical { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0 rgb(122,121,122), /* light */ - stop: 0.25 rgb(100, 99, 100), - stop: 1 rgb(88,87,88)); /* kindaDark */ + background-color: rgb(76,76,76); min-height: 20px; margin: 2px; border-radius: 5px; border-width: 1px; - border: 1px solid rgb(31,30,31); /* veryDark */ + border: 1px solid rgb(76,76,76); } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { border: none; background: none; + height: 0px; } QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { @@ -179,28 +181,24 @@ QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add- } QScrollBar:horizontal { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(54, 53, 54), - stop: 1 rgb(58,57,58)); /* dark */ + background-color: rgb(58,57,58); /* dark */ height: 14px; + margin: 0px; } QScrollBar::handle:horizontal { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(122,121,122), /* light */ - stop: 0.25 rgb(100, 99, 100), - stop: 1 rgb(88,87,88)); /* kindaDark */ + background-color: rgb(76,76,76); min-width: 20px; margin: 2px; border-radius: 5px; border-width: 1px; - border: 1px solid rgb(31,30,31); /* veryDark */ + border: 1px solid rgb(76,76,76); } QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { border: none; background: none; + width: 0px; } QScrollBar::left-arrow:horizontal, QScrollBar::right-arrow:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { @@ -228,31 +226,31 @@ QToolButton:pressed { } * [themeID="addIconSmall"] { - qproperty-icon: url(./Dark/plus.png); + qproperty-icon: url(./Dark/plus.svg); } * [themeID="removeIconSmall"] { - qproperty-icon: url(./Dark/minus.png); + qproperty-icon: url(./Dark/minus.svg); } * [themeID="propertiesIconSmall"] { - qproperty-icon: url(./Dark/cogwheel.png); + qproperty-icon: url(./Dark/settings/general.svg); } * [themeID="configIconSmall"] { - qproperty-icon: url(./Dark/cogwheel.png); + qproperty-icon: url(./Dark/settings/general.svg); } * [themeID="refreshIconSmall"] { - qproperty-icon: url(./Dark/refresh.png); + qproperty-icon: url(./Dark/refresh.svg); } * [themeID="upArrowIconSmall"] { - qproperty-icon: url(./Dark/up_arrow.png); + qproperty-icon: url(./Dark/up.svg); } * [themeID="downArrowIconSmall"] { - qproperty-icon: url(./Dark/down_arrow.png); + qproperty-icon: url(./Dark/down.svg); } @@ -263,11 +261,11 @@ QTabWidget::pane { /* The tab widget frame */ } QTabWidget::tab-bar { - alignment: center; + alignment: left; } QTabBar::tab { - background-color: rgb(88,87,88); /* kindaDark */ + background-color: rgb(76,76,76); border: none; padding: 5px; min-width: 50px; @@ -276,16 +274,16 @@ QTabBar::tab { QTabBar::tab:top { border-bottom: 1px transparent; - border-top-left-radius: 5px; - border-top-right-radius: 5px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; } QTabBar::tab:bottom { padding-top: 1px; margin-bottom: 4px; - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; height: 14px; } @@ -305,20 +303,19 @@ QTabBar::tab:pressed { /* ComboBox */ QComboBox { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(86,85,86), - stop: 0.1 rgb(82,81,82), - stop: 0.5 rgb(78,77,78), - stop: 0.9 rgb(74,73,74), - stop: 1 rgb(70,69,70)); + background-color: rgb(76,76,76); border-style: solid; border: 1px; border-radius: 3px; - border-color: rgb(31,30,31); /* veryDark */ + border-color: rgb(76,76,76); /* veryDark */ padding: 2px; padding-left: 10px; } +QComboBox:hover { + background-color: rgb(88,87,88); +} + QComboBox::drop-down { border:none; border-left: 1px solid rgba(31,30,31,155); /* veryDark */ @@ -327,7 +324,7 @@ QComboBox::drop-down { QComboBox::down-arrow { qproperty-alignment: AlignTop; - image: url(./Dark/updown.png); + image: url(./Dark/updown.svg); width: 100%; } @@ -347,7 +344,7 @@ QComboBox::drop-down:editable { QComboBox::down-arrow:editable { qproperty-alignment: AlignTop; - image: url(./Dark/down_arrow.png); + image: url(./Dark/down.svg); width: 8%; } @@ -357,25 +354,25 @@ QComboBox::down-arrow:editable { QLineEdit, QTextEdit, QPlainTextEdit { background-color: rgb(31,30,31); /* veryDark */ border: none; - padding-left: 2px; + border-radius: 3px; + padding: 2px 2px 3px 7px; } - /* Spinbox and doubleSpinbox */ QSpinBox, QDoubleSpinBox { background-color: rgb(31,30,31); /* veryDark */ border: none; - padding-left: 2px; - padding-right: 15px; - margin-right: 10px; + border-radius: 3px; + margin: 0px 3px 0px 0px; + padding: 2px 2px 3px 7px; } QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: margin; subcontrol-position: top right; /* position at the top right corner */ - background-color: rgb(88,87,88); /* kindaDark */ + background-color: rgb(76,76,76); border: 1px solid rgb(31,30,31); /* veryDark */ border-radius: 3px; border-width: 0; @@ -387,7 +384,7 @@ QSpinBox::up-button, QDoubleSpinBox::up-button { QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: margin; subcontrol-position: bottom right; /* position at the top right corner */ - background-color: rgb(88,87,88); /* kindaDark */ + background-color: rgb(76,76,76); border: 1px solid rgb(31,30,31); /* veryDark */ border-radius: 3px; border-width: 0; @@ -413,12 +410,12 @@ QDoubleSpinBox::up-button:disabled, QDoubleSpinBox::up-button:off, QDoubleSpinBo } QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { - image: url(./Dark/up_arrow.png); + image: url(./Dark/up.svg); width: 100%; } QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { - image: url(./Dark/down_arrow.png); + image: url(./Dark/down.svg); width: 100%; } @@ -427,7 +424,7 @@ QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { QPushButton { color: rgb(225,224,225); /* veryLight */ - background-color: rgb(88,87,88); /* kindaDark */ + background-color: rgb(76,76,76); border: none; border-radius: 3px; padding: 4px; @@ -452,7 +449,7 @@ QPushButton:checked { } QPushButton::menu-indicator { - image: url(./Dark/down_arrow.png); + image: url(./Dark/down.svg); subcontrol-position: right; subcontrol-origin: padding; width: 25px; @@ -461,20 +458,15 @@ QPushButton::menu-indicator { /* Sliders */ QSlider::groove:horizontal { - background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0, - stop: 0 rgb(50, 49, 50), /* dark */ - stop: 0.75 rgb(88,87,88)); /* kindaDark */ + background-color: rgb(76,76,76); height: 4px; border: none; border-radius: 2px; } QSlider::handle:horizontal { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(240,239,240), /* lighter */ - stop: 0.25 rgb(200,199,200), - stop: 1 rgb(162,161,162)); /* light */ - border: 1px solid rgb(58,57,58); /* dark */ + background-color: #d2d2d2; + border: 1px solid rgb(58,57,58); border-radius: 3px; height: 10px; width: 18px; @@ -482,10 +474,7 @@ QSlider::handle:horizontal { } QSlider::handle:horizontal:pressed { - background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0, - stop: 0 rgb(240,239,240), /* lighter */ - stop: 0.25 rgb(200,199,200), - stop: 1 rgb(162,161,162)); /* light */ + background-color: #d2d2d2; } QSlider::sub-page:horizontal { @@ -494,27 +483,20 @@ QSlider::sub-page:horizontal { } QSlider::sub-page:horizontal:disabled { - background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(50, 49, 50)); /* dark */ + background-color: rgb(50, 49, 50); /* dark */ border-radius: 2px; } QSlider::groove:vertical { - background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, - stop: 0 rgb(50, 49, 50), /* dark */ - stop: 0.75 rgb(88,87,88)); /* kindaDark */ + background-color: rgb(76,76,76); width: 4px; border: none; border-radius: 2px; } QSlider::handle:vertical { - background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, - stop: 0 rgb(240,239,240), /* lighter */ - stop: 0.25 rgb(200,199,200), - stop: 1 rgb(162,161,162)); /* light */ - border: 1px solid rgb(58,57,58); /* dark */ + background-color: #d2d2d2; + border: 1px solid rgb(58,57,58); border-radius: 3px; width: 10px; height: 18px; @@ -522,10 +504,7 @@ QSlider::handle:vertical { } QSlider::handle:vertical:pressed { - background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, - stop: 0 rgb(240,239,240), /* lighter */ - stop: 0.25 rgb(200,199,200), - stop: 1 rgb(162,161,162)); /* light */ + background-color: #d2d2d2; } QSlider::add-page:vertical { @@ -534,9 +513,7 @@ QSlider::add-page:vertical { } QSlider::add-page:vertical:disabled { - background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(50, 49, 50)); /* dark */ + background-color: rgb(50, 49, 50); /* dark */ border-radius: 2px; } @@ -569,6 +546,18 @@ QStatusBar::item { border: none; } +/* Table View */ + +QTableView { + gridline-color: rgb(88,87,88); /* kindaDark */ +} + +QHeaderView::section { + background-color: rgb(58,57,58); /* dark */ + color: rgb(225,224,225); /* veryLight */ + border: 1px solid rgb(31,30,31); /* veryDark */; + border-radius: 5px; +} /* Mute CheckBox */ @@ -577,18 +566,17 @@ MuteCheckBox { } MuteCheckBox::indicator:checked { - image: url(./Dark/mute.png); + image: url(./Dark/mute.svg); } MuteCheckBox::indicator:unchecked { - image: url(./Dark/unmute.png); + image: url(./Dark/settings/audio.svg); } OBSHotkeyLabel[hotkeyPairHover=true] { color: red; } - /* Group Collapse Checkbox */ SourceTreeSubItemCheckBox { @@ -602,11 +590,11 @@ SourceTreeSubItemCheckBox::indicator { } SourceTreeSubItemCheckBox::indicator:checked { - image: url(./Dark/expand.png); + image: url(./Dark/expand.svg); } SourceTreeSubItemCheckBox::indicator:unchecked { - image: url(./Dark/collapse.png); + image: url(./Dark/down.svg); } @@ -636,3 +624,92 @@ QLabel#errorLabel { color: rgb(0, 192, 0); font-weight: bold; } + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: 36px; + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: 16px; + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: rgb(31, 30, 31); /* veryDark */ +} + +/* Preview background color */ + +OBSQTDisplay { + qproperty-displayBackgroundColor: rgb(76, 76, 76); +} + +/* Preview/Program labels */ + +* [themeID="previewProgramLabels"] { + font-size: 18px; + font-weight: bold; + color: rgb(122,121,122); +} + +/* Settings Icons */ + +OBSBasicSettings { + qproperty-generalIcon: url(./Dark/settings/general.svg); + qproperty-streamIcon: url(./Dark/settings/stream.svg); + qproperty-outputIcon: url(./Dark/settings/output.svg); + qproperty-audioIcon: url(./Dark/settings/audio.svg); + qproperty-videoIcon: url(./Dark/settings/video.svg); + qproperty-hotkeysIcon: url(./Dark/settings/hotkeys.svg); + qproperty-advancedIcon: url(./Dark/settings/advanced.svg); +} + +OBSBasicSettings QListWidget::item { + padding-top: 5px; + padding-bottom: 5px; +} + +/* Locked CheckBox */ + +LockedCheckBox { + outline: none; + background: transparent; +} + +LockedCheckBox::indicator:checked { + image: url(./Dark/locked.svg); +} + +LockedCheckBox::indicator:unchecked { + image: url(:res/images/unlocked.svg); +} + +/* Visibilty CheckBox */ + +VisibilityCheckBox { + outline: none; + background: transparent; +} + +VisibilityCheckBox::indicator:checked { + image: url(./Dark/visible.svg); +} + +VisibilityCheckBox::indicator:unchecked { + image: url(:res/images/invisible.svg); +} + +* [themeID="trashIcon"] { + qproperty-icon: url(./Dark/trash.svg); +} + +* [themeID="revertIcon"] { + qproperty-icon: url(./Dark/revert.svg); +} diff --git a/UI/data/themes/Dark/cogwheel.png b/UI/data/themes/Dark/cogwheel.png deleted file mode 100644 index 62ecce9448678450460098f070c32f1a453dcc57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6)_J-(hG>Z99yH`?F%WURcs)Bg zRlH#JwUc^5kvrL^t2i;WS^qrJz%y^LxfoYxv&Zx!P0tUd z%+ZpOEZ$^vS|Gz{qQ|n2uWtOvwhbe@%x?gI606##%l_`>f-4T3L3f}I`h0lYuGqO-b?j~s}0@{R=n#AW{s0YK>!MLsAR2X%1} z{Us~|G?6k9JDtjhz!6ux;_Oh@uZJSz_)7r5?ndafxX=(~bVOXZvLn>REkrL=6abJE zf!d$Ha%+M?h35EwlJ8k_XPT7QtG~Bd9lBMXYX}s6o%K*7+iAH;IXg2-=AKKHy!gpu z%O%K(n@10weGhys{tBS-QAg&EedC>|18&L(>_qy5zq7P#Bl8vb6p)Kr2%{ZX;(nZZw$ga)XO z-!3&MCmzwVmZsyR0HfR~NYPsqhALgOkmsQ#V6~t8& z8rV-3s%e3z7Y{Mtzd4(z)Qj&`_m|}*iyv?vmir(+I~b`BZum%xYMzRk(SX_|y8|yf zo~>WJTALYbcm%_Bro_t$3_szFej3|oH1j;o3S4XdM$0x;#GHsJdLl$9orF;+LO5vU5{U~g{W8hFcd+RZQ7pIZ}DTqs^I%6$X z9xXcWcah(n`8gYma2{Cph928{UYllt3OGhyjxZvg?U_p2dyb=e^%u>{rApcQQi>3) z0X_NR(?;yOxK7OochLFVGT76R(8!$10R$^Jr?FN~WARNv%U!97TrB_-$+>Ri`(GuG zoT@E2=rnQIHvF5Sy=V;4pl8U$D6vNa)7nzz`FQ+Id`hp6SgIwvW7uV!cojIYM%o|{ zB5gs1WMK4tG`GKPfJ4CP-fR4?j@B^mG@AfGAnc4&szVX18p4ZU`RY@t-h*CnvR^17 zugB}TcAy?AL?9DA<6xXOcf%;rmVzkndhdsL88g7M&v|O0`!`L`Ch6>Y$bdoC7-8!+E4)P%a&>-dVS?5aTE9~R(+W0KL$vv&~)knC_{D_zC zcfeT=L6M+qS#k>~mGs(MHVnhZ$ep&)whxphn``F>=70#k!)2>=e$A zmC|vv9SD^#l58#Hg+dJW&^&u+m|k+r9Vu9)U&OG4@=2ZzXeQZ+csgL&qY7tZDY+9{ zFj85oyf_5;&fSSED`T?s^9&!T?Yc{H3{LXWE}*=fEg;;f@fkM2qNsc(!3nGo9fMGv|z?79tH5R-Vj+hrkYfeo7W9;Hc?}4)vK$9`7})mYdB` zd|;%#@!^gH*|OfSPsqMgDR43L#L`qLWIK2M8lu!_8+f9>Loc!&Em6M>hHr-WRU&zN zW>p#=N7Rx#=XnL)7l}f%!VJkMg5lW4K~kM?Ji9mf=yjJr?Pq?=^2jaOoQ021a77E49TJSglFxaOQ3E`ZFE4qqi6C zwT){oGHipxo=7i7nXfv{Yn4~s#}qbyMnC%NN(TYS(}JQx%UP}32O(AaU=tCguTvj! zaSlPc*4W@tPUNiOoSy~Eqh&9AC*G_rB74&d61L3E@ z$I3N>(9>cF!B11W4VmzQ*a>{_jgq@DXwhVy4Vy3WSeH%U>xNy^cADq>u@9PR?t{}1 zFQ<&{x?x$HM`m72M^oCt2>ah~(|$M3$lMh5 z_%2&hx6^<|bEn`CCR}Dq_`^R%JwZ$=cGP%3BQN??32zNXdDCCI;v%{9*r`^{>|96duX#XP4l z!Uh7vhX_>c2b`-HU*DIVs^U=Te2<~tw=T_!X+*P5qQqA z**DrxCn3M#;+?XW)Nn#I)^^fC=FzMOk}G#ge?#$FNP=ZekC?gE-dlokPM>#gWbR`Y z-J~b;?Ckvl_BggBQbO{Z19!?ycyv{}a+0TP)PgV;$qNaPB%BX3Z=hrdQUcbd`s(e^W4G&PTI(y2ymJMladhbevTwSsCvcCo6^v{}xC>CZ za^j?5#E4Qi8pV%&uRHt|K0y#UKzf>rsgUp+oqdWL2iP+_XfprQEaaNp0Fs zw%E72+()IS6Xsm-2ih!oH;Os!f%7pKlHP}KiHReqV_!SDRw|3@RU*%{-xd>TgVYQt zYQ^9F!e*GXSp!A3q|XnSh97DRE|pSp zZxZz*{nzO!xLzcLWg|9{ct*4aVRlL?VJD%=nD=)v=d)kXiV9^d^n7BVO7Gn~sLx31 zk~ysavc2|$of%C=)L8`d6^1)%=rZ0cKKJ$5MPisn3bQUtbrqJ~L`|-hTi>_gXUuVI z_ldTY+DX#uR3zpoQ79z}+aw$_0anzbjbq_iD~dYppart&?(soCo9`tD*@nXN6I~Nm z`tOVsq!2oP`UnK^^(Ej4%gf`i(9ZcJewsKnol)kcR{&PJnmfRUEzZ=96w# z+-Tv2tBi&W=gAM%Rs3}CV?9DiNFv%KU;GNwyhhw)?%>Agj=IoS#C##WH%z`cyo>yZ z9J<6%SAFmdw0!jl_aRJTWV18FZ=G&(NnO=(y}EizQei#+i;{-_VTk*}`)wDZiZhi@ zYj*D2yN!U~d>*Y--3|+oyu`mpA@2&`%M(L|&vJZhX@qYXWSyXIX4gZ~)O56xM%o~^a zYH@sZ^sr_phyecp$H^|ZdjcPoUuP_jaGd?uF{SOPx!+dp^bcd3E%Vu9tM>8c27F!1 zAH*pab}VMzn(HIj=>eoPj)QT7U#f|VD;oa4A-;28A##s1%)@jSk1}dfU=_vL-SxKR zMVQm_*w3Yyc4w|1<;}Rm#|mgaz91@4deMQmuk9b#Tf_W^Tm~M1z3#tn58_14b?c>wl z{p}z9WlH;FvSWXBoqs$fT7(Q8z#meu#u$#l5vGrEd*N0Tbf;zds?ooxf z`_)wB@1_QlY5MAH+`-JdR%Rg&n)b?J+zmSr{WP@bL>wE-RKQvb4+5Bo^i-w>hM(pQ`K{YKg9g7KYN<>)?B?YM ziQ;46@R$yT8WM=$d7sW{0-e(CP5dX`)D14})Nq_+{DEoncH`$Shk=P4l8+T7nVipf+)9tjF9feQHX@G_DJb zL|0H}{4xKovjPIB2RCh8rw^OThU4Mut{!jqU+!X0Xo--Pr^m6IgPb(&1DgT-Y~hzR zo89JesfX05(>#AbLer_}dxTz%vQmd%_<~JKs6yjB>fA`&zM-re#|Tt!+O$$KZervc zg;3}rF-VA;6mo!ZlY$+{_2c(sm2`toR|$ZM)@&PtCaje0mwo0hFF*)FiC-tZC7FaH u+*N=vTd5-QY5GevJflJA|Lng+{*Nah(QB6#A diff --git a/UI/data/themes/Dark/down_arrow.png b/UI/data/themes/Dark/down_arrow.png deleted file mode 100644 index 39d31da89d6edfef47f642073fda4202e2b08b9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV7%t(;uuoF`1U$uJ5#j8@rRjN zUlYZ$7=O1Os`N6rlk1|2)ZhF+l{J3ST>`qF%qDNFKd583`{CO5?}GpTT>D^9*&IAMeO6Kx>=SXD%`|P5g zu%8@i-;ccy>iDtnf?!Ae+R*9ddsY9{RJ=Z2zjcZo(?@5M?zYd-?ib>IK7N$I;xGKa z;A#KYZ|nYV?-9)Deem*8{^lQ-1?x5n_AL0q=BBVuup^)4k#yq^C^IjUB`<>|FP$YX zjU_LYB`<{~?{W98PW?Ax`*uiMG`xQ&uy1p7$9scE))Ri{muY;I^{HtWzR!5H+~cEc zNKL!w|C^X*A{h^2PVIO2Q{}!v{$BN-y+2C(9%#?|A=>v~#($lIdR+PlDZ@3IP{*Z?v2 XZxz<#oD^-J1mbwQ`njxgN@xNA6+!JL diff --git a/UI/data/themes/Dark/expand.png b/UI/data/themes/Dark/expand.png deleted file mode 100644 index dd5d2b5ecc54f6622af84ad2cc18a0b9b7d45d80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3545 zcma)9jX#s?|G)RZHqQ}BW71Gj)DTB(N5aU%DK(tqp<+0BZfy>-Ez)x|5u#0JLUkg1 zDa4kOag0eF4`~^C;1`-a4P)P1zklF+-LLy~UDxOHxjygr=ej=kbzS!rKOc8>6$2Fj z0Mv;du15g?0yZH4QW>m%OR6A)mGUVscUM5M@o-+0=YkScf=A$408rK0cp*Shu|6n7 zP>9}#5o1c)aJ%i1Z+A6G-p=?Ps3V% zA&s4@D)c_44h);i=tX%e7&lL-h)fK-%C_-~*OoOGzKF=JHL5~*KnY?#K7#D)GcnUz zxWob&E84s-!YRWu{m!_%4=@ZO=XUYqVVj@czj+I}$K-zVTq(g)5d>((#~wCx_GWVI zZtEu22~($h7T^TQ0sc|lhs1x92EDLE9gHQv*`BG+0z80E^n6V0GiXOHAxLAYn+145 z-zN~SbPo1Tlo}ED3q&)wo~b1t%$ zeSHlDXb{78hh*Ey{CXD#J9aaT)&QOriYuUoz8iDp5b>UJ67K6$G$3d3jx_IWH=f$! z34Ii3T8(QlH^?BoSp#ri|1L~7S21TUcpGF`qJZ-7UR>??#XGP{^8}km@Bj@U`b8c#A_X(_#wj*Rs2^t zO=8^yW6(_sdJHnkSOmNm^uRADpydkZPV6QPizv03kH;Wsxr76bmi}!sglsW{X9b~* zohwVw*1tZjkplXXN8WWbUf7YyIbv%q z^4r8IK^dRlKDmE~fJrrDzP%E9>Z5({3^w6vsR_X`mvC96iQbhKQ*djXz<0IIjoq|A z2_1D(yI_AUZb}1~J_3l7_c-}yg6dLi&QG(d*U^Zecbb<6#y7OyaIotEJ$?fnS%LI3 z-ehU*508r0)vDUCDuN$1U$^{!SevA@n;>4Q&d;$s1Y37QH&-b8oTzcM7SzHBg|5~9 zoqo3*s0iRo8KJpy%HGM67^mA9zSeT_{xx7wKD&li@ z3@zw^6b0!vXWAq+=a-tyk*T;mKynvJu0dS>9ZZq2V?zdwm4ivP#*^2%RUS1j75VvC zC(CHt%xZr5*|nW_tB;pxy(F0iU*^YO{2=h_d%^PYD7w+LIWC5ANOyxuon!UwZXM1( zBp8NG$m98r+1qfv?x`JE2+v`Yzm0!5U%2@9(yRSj5{^Ix7DI*s9$(-I=WM)sT}=XV zDC~cxHMj%8x8^xqiz$tmo8OT-RZth?(xa&vLT?mJ;$;wf{6Tcvdns@UyMgvrzuDuo zmed>ipX+Gfs#Lx(Zu>^c8)FQz{nWd1W1X%_RKJHJ$BOQ z-=~gJMlzKV4A{SqaO2~*xjZBFE$e5RLajUuqQE#TuaufFPMhXrT@{j&Vr22f`tY<| zp1sL5gx5hiaq0)JH6s0EKc=roOX}~r-tlw$p?o27?L%dqPy!B&&WxWsGH+gu;D4(&u~)NlE9 zW_zR3I1la-NPVH$^SBexkjTY1kkSLDVzX#17yTRnP++Zfa(M@eLmRE4gZ^y$IYY~Z zZ3;+Qu>p7HV~KM;RwX`WJr(aWPiq5ygE4p4BZ)voB1(FJTcsPM(fZ~=**+9tt3)UD z%_XjVdu|x@be@h|jR$!q58UB8+@J!=--jU`u=N7BZeIUEllPz47!`yf=Add0SxHS* z-h^(yJIdF3S?!IdYXhFjbaQ1z4Yz5$WH2>^9bLD!L?c$!P2<8)j6R}-q9->kil4Nl z>$xOn_`(x{mPqsV(%du?hOcD6PV3_lyUNSCPYD@(Hgwu%2cJPzRwRwq;7v>JbjOf} zGuSMI5nmJ__55X#cSs=g(bHI`v^DLVEY*4$(XvlJYJEmT%7E-qOKU-Ij*6DfEl3Dn z8T6k*L|crON?1Kt3&pNO^o0jV>q%_2@uc&=uczsLeYytej5hIz@FwZ|VT>xd{;sbQ zxaqfD9FVtJ${a=!_D`0xNlDGLTUi*1j=XZX)sNUf-*rZ|Nj|_dGp;v~i?v?zggsu3 zUY`uoCskeZO`WjO$5}4O_Hs(>5%|k|m`K>8r;+|j63Uvd=GsCrAWwX5iPSu)wQ(5VaY6A}_F-ZXrmjp+xu*ypjZ*~a$(zPg?}Iepild|DEB&>Sk$I)j4heXS| z96s-x|Nh1%wi5j-A{lDL5AB+m`H%OEoU+O^^IA_1e!?B5+oXPA)L;@kpk8^D)i1)B zd=^HElE+ay%I9Z4v{gl_ zbcH)Fh=ZVh-g z6)NTqi@@o$mX)_(X!UQP6B;m5U*HXy`h+$9VilRrvy!C-wpGnf#DPwaNoZq)Y#p^Z z;GK?a`x)6D|Gw$S0t85+@5G4bXbt_wNh3lbR(fjGZ6pV5GMng&H=IE8pv`hywK@Hy z<}Fgy6)nfLV`S<>)=4AEDx_TED_)Oqqrs*>o;B90S*;?%zuqrWLN&#a&3i$KX?RM<}zt3NPg zkI-UoC}3c@ywN=X;bx7P{Mp2$D$xzZ!HvVM^_>|`x7Rj4)|QI7@X@RhWl+1HC?Vw) zPe)P?NYHZH-62Qav}lAl{WqP6(T`mUchl+ox?d!F9={UJGq{37$n&_nD%!ysMcu>#uK$O)Pp518?XKf0&NvVIKrsqMp0alZL-y2Q*q_%(`~6X2I%ikxL&M~ yy!j;LC_8%VCKr9F4!oYOjm(vS|5yK0c{` diff --git a/UI/data/themes/Dark/locked.svg b/UI/data/themes/Dark/locked.svg new file mode 100644 index 0000000..58239eb --- /dev/null +++ b/UI/data/themes/Dark/locked.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/UI/data/themes/Dark/minus.png b/UI/data/themes/Dark/minus.png deleted file mode 100644 index 26e35b4e40d74afb525d36578380cf1f4bd93cae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxc&!&(3-C~KoQ20AirP+ zhi5m^fE+PT7srqa#$<-3z=Z$uJpU#fVR$9Y$}rns+dj8Ezze8?!PC{xWt~$(699=Z BAl(1} diff --git a/UI/data/themes/Dark/minus.svg b/UI/data/themes/Dark/minus.svg new file mode 100644 index 0000000..8fdc037 --- /dev/null +++ b/UI/data/themes/Dark/minus.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/mute.png b/UI/data/themes/Dark/mute.png deleted file mode 100644 index 682f91edfac637fd742aecaab27751f3e2bcf290..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^F3W0Ln;`TUS?!taujL*cr#Qx zV)bd4MoahDMXO7i99DEN35XeTTOIDw6a4c#X~Us+cFp$>d`Ns|tNwoSm)LU~-Pno~ z8pRG>lDhGT*|kn%gR}JBg?)Jn?Y}4g=i~h_<6$fFvK0Z!J-ilI1lDCJywPnGV(dCM zBZ5g(N@Ldo3F#9b8VZ~n3O3n25@hwd^U>$O+i!)N?v+zF`A9!Dx>?J(SLv~l%nbWa QK&LWzy85}Sb4q9e0IU#H@c;k- diff --git a/UI/data/themes/Dark/mute.svg b/UI/data/themes/Dark/mute.svg new file mode 100644 index 0000000..d6c9c5d --- /dev/null +++ b/UI/data/themes/Dark/mute.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/no_sources.svg b/UI/data/themes/Dark/no_sources.svg new file mode 100644 index 0000000..be6ad55 --- /dev/null +++ b/UI/data/themes/Dark/no_sources.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/UI/data/themes/Dark/plus.png b/UI/data/themes/Dark/plus.png deleted file mode 100644 index 69325d1d810f6a5bdac715a356a2d6ac9e55287b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxc&!&(3-C~KoQ20AirP+ zhi5m^fE;O07srqa#^eKBKlpoe+WyuX{BRaZW?>UiV_;ZyIP9gTfaMpUG6qjqKbLh* G2~7Y diff --git a/UI/data/themes/Dark/refresh.png b/UI/data/themes/Dark/refresh.png deleted file mode 100644 index 7a6f6e6dc4b8846b6a8c529284b3d0bf7e8541a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3275 zcmV;+3^enJP)t1R z{$mv49~i}djO7n7Di8~z!9;~BP(=|!Yl#K|wFaSJp#qiu*mifj?e51v?p(ZFUbi#v z&3*5__vV|t?AzUU@0*$Py=TsxIdhH_5fKp)5fKp)5fKp)3_vH)Y2U{IDIq!vzW+Q) zpOqwIOe8@YYJkNgSfBY@z1z@`YnugKMUTJzxfzf#=00Ogp~BAJKm8q;C~3N+X_6*M znrQoPB%P2nEa`}(gOc7d#vH5E)=J+I32Ma3e4u=#83Td;$O*0ydmP*xC0Jfi=L7fqj%8 zZvuA%>wvztCc-HQ0Pr?&^*D3;HNZCexn{#b;BnyEwzU-;2mmk$tjb-t1b7lSO7mk7 z*bXdhI}$h&AjPFXA8;qFi+ilG_zn>oO$Jjm0VHkrxa2>Q&TR4LVnEU+NxwD53^m%9 z&{49%IkOEE<77z>Ncs^lH+5h&N)xohx^-C6tCIFfdds?ck4PG_F5q+=daa9is&yI9 zl61DDzJl7dLDE9tmefVw2u;w61Q?U_M@dgf`kSQJB^|OtKWYgvVvHHhJD4POTbFU4 zCBtc!2p38ExTK{@_7FnhJV|!~3yd*0HHrjfF#>DB3E)BCN?-;s8R!n!j|spe;Qhem zz!vKk4uvth-0fq@ToU{!EftpFYY4g~1mKY)2HL4v>p7y=#wt|*IVtpX920}lX)YwYWDz=Ajhyc zFE&A+yMa#w4+kK@yr$Ly1xx{N16P%_uX!6`2CxwrsBt=&*|a1m$ObqLtSsxGWQhQD z0CVi7P%VaGbFK~wUIkoQc0(ycgkE4XFj8e-w>70vSO-7As%9a9eSb5sze+#B?M+95 zIuKxRym*3=l3*>cr;5G~G%~!t%$~rvvynw59*gXhumpI%ioWhnH;^I$-s>D%Q}%($ zkzjt6B=}i2<}ecA-GQbnnrV)-nxLBLU~L(KNyy3bV$xmE2ud=MXR#_&zW{YJx%t&}}1&d=8|}uU8|x>@jG7 zmw;P=37L5;N-_)ymV3q+eywn=Yy~_8*pd0-5gWO(I%Z%D!Nf0_H+p@Nx$;A?26z7 z$O!Rrk9*$}MOYg$zWsYWF1y^v4ltgK9G3g>=rfHmZ$vQ$u?40}dQHjk^;6-PF{UT@ z6aZYHB-N@5o{%&c)qgN8Ff8eD$4e$ySp~NN7J7X4Nz242!7wTQDtfT+-iNKW=3eh5#8gKG%@|$0hBJPk(UiQxX8;LICHp104?ROW)3RZ0M^> za`Hrk#bHVRcKqz@oa|~|22NCxJjn&GD@n){5f%eh3%H=i$}E@-Fx_JX@RpKnToGY0 zXvfwCT~=nn2rx}al?@jhQ2nET+X2H$avZu6U}{JLyw8IG19sUG5f&pJQ){OjBa2-L z(Btvh;d~Oth#*VBQ4h`9;cBzF3e_jn=tLA(VelE-u8;(XXi;=Dig^)LA-E=Xp4XKm zVRl`Ec1Br+g&k8@U(A@Rz2HiKBT5P?x?qyrwk;$4Fj2{|a2Jfat{7YiaL{8KnC8LM z2zT_juI;U?4u&Maeh&gnw|;|&u;`OC!-D|(gAu?Ob4>2eomp}}-W=hM$#!g=Y&I*i z+6j=^QrzSC&r%C#t<7fBeWLG@_GFytf>sCLRLkEg^yU3wLgZ2 z1tGy1N{VW_4Cb&grZ=bt$QTo@k*VF17DwR+!zCUM>yxeqpwBWeefzLSqp$I4NW{1g z-se0nd&t!QtfoP8JPs)yt84bE)+NDIn+Mqy=la~=YdhE&^P-18pj*=Vs0C`qm#myz z2Jv6Un3oD6Kt_o7d))u)g{hSzPpU-$zD^m|tKTp49pn+;$Sb6sbPV~mIM z3vZB3M6}Z*Y4f8Mv`9pC<39_y?<7Qcx?V}5x@<}^R~!JAM*`IR;AMaRq)ovylo{u* z)Gc|ZEqC&!2MHcXt9eHf1X&ge=&{ACci6;S7uB;Ny*53mPlb!skpP7d;X}YBz@&ON zVx3J??24^1Ygm0qQLiuTtD+?8Mw=2n4r)o-VXAGAEpV_=(q<(w04_Kp=^|syi=``Y zaPq!RP_hBmC^ifkW5!BFfZPCWm$b;^l0QpYX^a_a1`+@tkaV4-nQ{+}4}~5{w;E&i zl-WtKw2ER!cQlJ{z}7~%*3%y!5*Igvn!LxeQzO95mDac=wzgL{hyaVqEco>26weA5 z0RIKnmR$?vNU*Hc2vF1E&`e>*Ly|UmUVgfy?*U87YzL%mv-Zk1NtZRi7HEnkQ?D&- z?2G5qawlc4IFiwLYiz;l_MQSN49r~5{PtNAENKQ3Wcs|pPGgY(xp{IuFi=HD_oOyK zneBpHU#|v^wmktV49x8wwQlm&Ms99060C{@s5~&+s_gIxa7S7PuSrO7X(YgV?SU>} zR}CHilMO=cD6?&l>F-KQ-u5OyZct|0l5*8zz-}PT$lkleTHz{(u&vf7K#m0Sfahu) zgss*S*WCpDz8)BCdjjMJWkCQE9JY}~%TG2JH_{HsXn^16LbckL069ZxUX9a1HpcK# z;Ic*#q0$rte8vW49*#D^_#~KFqhXkhHa-FT)7I7NE1C!ugnl}36Y!d6bA<9r`3Y<_@5#U!z*^upf!p6O0+@P}AhWr0yQD8Dd_JKOB?XqA zQc`ARpORt=14>e^4@(-!=QFl>__~!uH}%?}6M>}UxBR3O26 zNw-KkufVSyQc`JSpOSq02bF}T4JpZ|hms7Oy;kVclw{+Yr6ea$qZ3`U76B@dV6LQZ zNZO!?x)Y!w$)YoH1Tx0#lJt2=>#Uk?b?hxuV;Yt%JSBnE21Am5p(MrJy)7*KA4>X& zq-9aDjlW!63TzMTQEL>A*nN$Kx&E$l?BAlcB!T^Xqdk0bun7gf-#V<;jYIUS9rg|u zwYxUxv&YbW>UkiwQP>OI30#pqt()sp_Wg!agqJOf(}LcKIh=2grtd7F;GeJ?LH*g9 zeJArw=K8i~oO1{5OMsIMT$c?)?+0!JUT&b!cLU!7J_?-XTPPz(f)$Yf)!PblfE%rg zdDuQpqk-JDW10I6+TFezfZ5}cP- zn-W4+;KSBMd_YNx+Y#UI2-uJHN={u*E1CdpH(k{0+5krH20H~TF7i8DzQkKCPiwu8 ztfk&jV~mV3TkJA&Fh0g@s diff --git a/UI/data/themes/Dark/revert.svg b/UI/data/themes/Dark/revert.svg new file mode 100644 index 0000000..38d0170 --- /dev/null +++ b/UI/data/themes/Dark/revert.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/settings/advanced.svg b/UI/data/themes/Dark/settings/advanced.svg new file mode 100644 index 0000000..9ee4de7 --- /dev/null +++ b/UI/data/themes/Dark/settings/advanced.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/settings/audio.svg b/UI/data/themes/Dark/settings/audio.svg new file mode 100644 index 0000000..5a5a78a --- /dev/null +++ b/UI/data/themes/Dark/settings/audio.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/settings/general.svg b/UI/data/themes/Dark/settings/general.svg new file mode 100644 index 0000000..a2b0c42 --- /dev/null +++ b/UI/data/themes/Dark/settings/general.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/settings/hotkeys.svg b/UI/data/themes/Dark/settings/hotkeys.svg new file mode 100644 index 0000000..6fdd695 --- /dev/null +++ b/UI/data/themes/Dark/settings/hotkeys.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/settings/output.svg b/UI/data/themes/Dark/settings/output.svg new file mode 100644 index 0000000..9d36210 --- /dev/null +++ b/UI/data/themes/Dark/settings/output.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/UI/data/themes/Dark/settings/stream.svg b/UI/data/themes/Dark/settings/stream.svg new file mode 100644 index 0000000..982fd1a --- /dev/null +++ b/UI/data/themes/Dark/settings/stream.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/settings/video.svg b/UI/data/themes/Dark/settings/video.svg new file mode 100644 index 0000000..2aca1f3 --- /dev/null +++ b/UI/data/themes/Dark/settings/video.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/trash.svg b/UI/data/themes/Dark/trash.svg new file mode 100644 index 0000000..2748485 --- /dev/null +++ b/UI/data/themes/Dark/trash.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/unmute.png b/UI/data/themes/Dark/unmute.png deleted file mode 100644 index ae73cd2ffa17264fbaaedcc98368abd082018b41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 268 zcmV+n0rUQeP)FNKdm&1CCY!&-%D1dxVB;^atfx2H-#wel11)`|!l`qU_Hik~maeWl zS*5}ic1^j*diap9R9MiLs@X<6C;;IYO)Iz<4 diff --git a/UI/data/themes/Dark/up_arrow.png b/UI/data/themes/Dark/up_arrow.png deleted file mode 100644 index 89a5d470791916214b28709585ad0a1cd332bf13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU_9XI;uuoF`1Vd?nsT5B+k=^R zV;PtdQZt8ZHf4Bb2AK%_Q=i&QV-`@&NJMjHaMZL^-mP-A~V-|Ydo9=zsX(+N! zh$Eh}Wj&fH0!YHALzuinm`+EZlR0GG|KY4^&F9EIOWmV;rX}Wi8|;7h_TYtYi4oO? zOa4lno5zzH-?q`*`Nn*hbC3;ssD04+?|&OriPrTjk9GO)O@CFhGe%5kpHNL&sovFp z)BNlA{M$P(d1YM7{xw1-Gi8-eo86vorx6!^{d3H!@Q3<~S$xAL-p#U_T?zE~e$^SW zb-x$f7gn|4+<#kW&aDr(c~<{awV0Bn{O6t52F&c3-< z&g7WnE{ECfv#;)z`;z`bXkstF8gh^`Fxaf(&z$?2`{P2VCm;z=S3j3^P6<7gR`t)iS1tQ{Vq=8a+!t@=-S1EK zoA&0>|GTrRJ&#*50!;;hV});)U*78Z=hePfQjSf}D`U@z@2r1yOQFuP_MJV?;SbrB zb!jZUN7ldNe>{76(KWY%bwb;A)zANMyC|+_!NeawXZ}xa+TWMRl6xdxy`ZUGup^)4 zk#yq^w+l&s6pwRA_3U5$-t9uzpWJ!-pFS~F&d~frkUCaG&FdITIyJ3!ozu_QBnQojD;tX0C6Ia)j*f#@cz; zpZ+PC^|4ps?Yh^?jvsly`bYh|h#F;&%_eW|TQ}|gc=lb&BWcIGuk!5wZB)L|bJT#R zGWnypUDMqwT4wLN;%hIs+BQ`P)-P1{n0@04Z}K+5KXK=Je{@S8^qnQsXMdO@?`ZrV zj=c1a`VaS6=Ig)ye#xVIi|6VeD+JfpFD z<7KV4*GexR23oY2|6dBrav6Kx!<0rUG+9iyBPwb9IR^AVv+{~}_n)==@a;*^;M=2C z$E~c<_N(Lmv->RnWj@}PZTz9zhnC*FVdQ&MBb@00FSIrT_o{ diff --git a/UI/data/themes/Dark/updown.svg b/UI/data/themes/Dark/updown.svg new file mode 100644 index 0000000..a91e740 --- /dev/null +++ b/UI/data/themes/Dark/updown.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Dark/visible.svg b/UI/data/themes/Dark/visible.svg new file mode 100644 index 0000000..ffe51ec --- /dev/null +++ b/UI/data/themes/Dark/visible.svg @@ -0,0 +1 @@ + diff --git a/UI/data/themes/Default.qss b/UI/data/themes/Default.qss deleted file mode 100644 index 37f4b20..0000000 --- a/UI/data/themes/Default.qss +++ /dev/null @@ -1,117 +0,0 @@ -/* Intentionnally left blank */ -/* Themes are created using Qt CSS, you can visit */ -/* http://doc.qt.io/qt-5/stylesheet-reference.html and */ -/* http://doc.qt.io/qt-5/stylesheet-examples.html for examples. */ - -/* OBS will use the theme filename for the settings. */ -/* You can ship images using relative paths in qss. */ -/* Dark Theme is a good place to start if you need */ -/* a template. */ - - -/* We need to set back the icons, or the preview wont stick. */ - -* [themeID="addIconSmall"] { - qproperty-icon: url(:/res/images/add.png); -} - -* [themeID="removeIconSmall"] { - qproperty-icon: url(:/res/images/list_remove.png); -} - -* [themeID="propertiesIconSmall"] { - qproperty-icon: url(:/res/images/properties.png); -} - -* [themeID="configIconSmall"] { - qproperty-icon: url(:/res/images/configuration21_16.png); -} - -* [themeID="upArrowIconSmall"] { - qproperty-icon: url(:/res/images/up.png); -} - -* [themeID="refreshIconSmall"] { - qproperty-icon: url(:/res/images/refresh.png); -} - -* [themeID="downArrowIconSmall"] { - qproperty-icon: url(:/res/images/down.png); -} - -MuteCheckBox { - outline: none; -} - -MuteCheckBox::indicator:checked { - image: url(:/res/images/mute.png); -} - -MuteCheckBox::indicator:unchecked { - image: url(:/res/images/unmute.png); -} - -SourceTreeSubItemCheckBox { - background: transparent; - outline: none; -} - -SourceTreeSubItemCheckBox::indicator { - width: 10px; - height: 10px; -} - -SourceTreeSubItemCheckBox::indicator:checked { - image: url(:/res/images/expand.png); -} - -SourceTreeSubItemCheckBox::indicator:unchecked { - image: url(:/res/images/collapse.png); -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: red; -} - - -/* Volume Control */ - -VolumeMeter { - qproperty-backgroundNominalColor: rgb(15, 100, 15); - qproperty-backgroundWarningColor: rgb(100, 100, 15); - qproperty-backgroundErrorColor: rgb(100, 15, 15); - qproperty-foregroundNominalColor: rgb(50, 200, 50); - qproperty-foregroundWarningColor: rgb(255, 200, 50); - qproperty-foregroundErrorColor: rgb(200, 50, 50); - qproperty-magnitudeColor: rgb(0, 0, 0); - qproperty-majorTickColor: rgb(0, 0, 0); - qproperty-minorTickColor: rgb(50, 50, 50); -} - - -/* Label warning/error */ - -QLabel#warningLabel { - color: rgb(192, 128, 0); - font-weight: bold; -} - -QLabel#errorLabel { - color: rgb(192, 0, 0); - font-weight: bold; -} - -* [themeID="warning"] { - color: rgb(192, 128, 0); - font-weight: bold; -} - -* [themeID="error"] { - color: rgb(192, 0, 0); - font-weight: bold; -} - -* [themeID="good"] { - color: rgb(0, 128, 0); - font-weight: bold; -} diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index e5a8362..88bde15 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -152,6 +152,15 @@ QSizeGrip { height: 12px; } +QListWidget QLineEdit { + padding-top: 0; + padding-bottom: 0; + padding-right: 0; + padding-left: 2px; + border: none; + border-radius: none; +} + /***********************/ /* --- List widget --- */ /***********************/ @@ -222,9 +231,9 @@ QGroupBox { QGroupBox::title { color: rgb(240, 98, 146); /* Pink (Secondary) */ left: 20px; + top: -7px; padding-left: 5px; padding-right: 4px; - padding-top: -14px; } /*****************/ @@ -471,31 +480,31 @@ QToolButton:pressed { } * [themeID="addIconSmall"] { - qproperty-icon: url(./Dark/plus.png); + qproperty-icon: url(./Dark/plus.svg); } * [themeID="removeIconSmall"] { - qproperty-icon: url(./Dark/minus.png); + qproperty-icon: url(./Dark/minus.svg); } * [themeID="propertiesIconSmall"] { - qproperty-icon: url(./Dark/cogwheel.png); + qproperty-icon: url(./Dark/settings/general.svg); } * [themeID="configIconSmall"] { - qproperty-icon: url(./Dark/cogwheel.png); + qproperty-icon: url(./Dark/settings/general.svg); } * [themeID="refreshIconSmall"] { - qproperty-icon: url(./Dark/refresh.png); + qproperty-icon: url(./Dark/refresh.svg); } * [themeID="upArrowIconSmall"] { - qproperty-icon: url(./Dark/up_arrow.png); + qproperty-icon: url(./Dark/up.svg); } * [themeID="downArrowIconSmall"] { - qproperty-icon: url(./Dark/down_arrow.png); + qproperty-icon: url(./Dark/down.svg); } /***********************/ @@ -729,36 +738,36 @@ MuteCheckBox { } MuteCheckBox::indicator:checked { - image: url(./Dark/mute.png); + image: url(./Dark/mute.svg); } MuteCheckBox::indicator:unchecked { - image: url(./Dark/unmute.png); + image: url(./Dark/settings/audio.svg); } MuteCheckBox::indicator:unchecked:hover { background-color: rgba(240, 98, 146, 0.5); /* Pink (Secondary) */ - image: url(./Dark/unmute.png); + image: url(./Dark/settings/audio.svg); } MuteCheckBox::indicator:unchecked:focus { - image: url(./Dark/unmute.png); + image: url(./Dark/settings/audio.svg); } MuteCheckBox::indicator:checked:hover { background-color: rgba(240, 98, 146, 0.5); /* Pink (Secondary) */ - image: url(./Dark/mute.png); + image: url(./Dark/mute.svg); } MuteCheckBox::indicator:checked:focus { - image: url(./Dark/mute.png); + image: url(./Dark/mute.svg); } MuteCheckBox::indicator:checked:disabled { - image: url(./Dark/mute.png); + image: url(./Dark/mute.svg); } MuteCheckBox::indicator:unchecked:disabled { - image: url(./Dark/unmute.png); + image: url(./Dark/settings/audio.svg); } /****************************/ @@ -777,12 +786,12 @@ SourceTreeSubItemCheckBox::indicator { SourceTreeSubItemCheckBox::indicator:checked, SourceTreeSubItemCheckBox::indicator:checked:hover { - image: url(./Dark/expand.png); + image: url(./Dark/expand.svg); } SourceTreeSubItemCheckBox::indicator:unchecked, SourceTreeSubItemCheckBox::indicator:unchecked:hover { - image: url(./Dark/collapse.png); + image: url(./Dark/down.svg); } /*************************/ @@ -960,6 +969,8 @@ QPushButton:checked#exitButton { QPushButton[themeID="addIconSmall"], QPushButton[themeID="removeIconSmall"], QPushButton[themeID="configIconSmall"], +QPushButton[themeID="trashIcon"], +QPushButton[themeID="revertIcon"], QPushButton#transitionRemove, QPushButton#moveAsyncFilterUp, QPushButton#moveAsyncFilterDown, @@ -973,6 +984,8 @@ QPushButton#moveEffectFilterUp { QPushButton:hover[themeID="addIconSmall"], QPushButton:hover[themeID="removeIconSmall"], QPushButton:hover[themeID="configIconSmall"], +QPushButton:hover[themeID="trashIcon"], +QPushButton:hover[themeID="revertIcon"], QPushButton:hover#transitionRemove, QPushButton:hover#moveAsyncFilterUp, QPushButton:hover#moveAsyncFilterDown, @@ -986,39 +999,6 @@ QPushButton:hover#moveEffectFilterUp { outline: none; } -/******************************/ -/* --- Hotkey Buttons --- */ -/******************************/ -/* Fix for the hotkey buttons */ -/* looking terrible with my */ -/* color choices. */ -/******************************/ - -QPushButton[themeID="hotkeyButtons"] { - background-color: rgb(58, 64, 69); /* Light Blue-gray */ - color: rgb(239, 240, 241); /* White */ - border-radius: 2px; - border: none; - margin: 4px; - padding-top: 6px; - padding-bottom: 6px; -} - -QPushButton:hover[themeID="hotkeyButtons"] { - background-color: rgba(240, 98, 146, 0.5); /* Pink (Secondary) */ - border: 1px solid rgba(240, 98, 146, 0.5); /* Pink (Secondary) */ -} - -QPushButton:pressed[themeID="hotkeyButtons"] { - background-color: rgb(240, 98, 146); /* Pink (Secondary) */ - border: 1px solid rgb(240, 98, 146); /* Pink (Secondary) */ -} - -QPushButton:disabled[themeID="hotkeyButtons"] { - background-color: rgb(58, 64, 69); /* Light Blue-gray */ - color: rgb(162, 161, 162); /* Lighter Gray */ -} - /******************/ /* --- Labels --- */ /******************/ @@ -1165,6 +1145,21 @@ QSlider::handle:hover { QSlider::handle:disabled { background-color: rgb(122, 121, 122); } +/**********************/ +/* --- Table View --- */ +/**********************/ + +QTableView { + gridline-color: rgb(118, 121, 124); /* Light Gray */ +} + +QHeaderView::section { + background-color: rgb(35, 38, 41); /* Dark Gray */ + color: rgb(239, 240, 241); /* "White" */ + border: 1px solid rgb(118, 121, 124); /* Light Gray */ + border-radius: 2px; + padding: 4px; +} /****************/ /* --- Misc --- */ @@ -1191,7 +1186,6 @@ QFrame[frameShape="0"] { border: 1px transparent; } - /* Misc style tweaks for dark themes */ * [themeID="error"] { color: rgb(255, 89, 76); /* Red Error */ @@ -1214,3 +1208,118 @@ QToolTip { background-color: rgb(49, 54, 59); /* Blue-gray */ color: rgb(240, 98, 146); /* Pink (Secondary) */ } + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: 36px; + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: 16px; + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: rgb(35, 38, 41); /* Dark Gray */ +} + +/* Preview background color */ + +OBSQTDisplay { + qproperty-displayBackgroundColor: rgb(35, 38, 41); +} + +/* Preview/Program labels */ + +* [themeID="previewProgramLabels"] { + font-size: 18px; + font-weight: bold; + color: rgb(122,121,122); +} + +/* Settings Icons */ + +OBSBasicSettings { + qproperty-generalIcon: url(./Dark/settings/general.svg); + qproperty-streamIcon: url(./Dark/settings/stream.svg); + qproperty-outputIcon: url(./Dark/settings/output.svg); + qproperty-audioIcon: url(./Dark/settings/audio.svg); + qproperty-videoIcon: url(./Dark/settings/video.svg); + qproperty-hotkeysIcon: url(./Dark/settings/hotkeys.svg); + qproperty-advancedIcon: url(./Dark/settings/advanced.svg); +} + +OBSBasicSettings QListWidget::item { + padding-top: 5px; + padding-bottom: 5px; +} + +/* Locked CheckBox */ + +LockedCheckBox { + outline: none; + background: transparent; +} + +LockedCheckBox::indicator { + width: 16px; + height: 16px; +} + +LockedCheckBox::indicator:checked { + image: url(./Dark/locked.svg); +} + +LockedCheckBox::indicator:unchecked { + image: url(:res/images/unlocked.svg); +} + +LockedCheckBox::indicator:checked:hover { + image: url(./Dark/locked.svg); +} + +LockedCheckBox::indicator:unchecked:hover { + image: url(:res/images/unlocked.svg); +} + +/* Visibilty CheckBox */ + +VisibilityCheckBox { + outline: none; + background: transparent; +} + +VisibilityCheckBox::indicator { + width: 16px; + height: 16px; +} + +VisibilityCheckBox::indicator:checked { + image: url(./Dark/visible.svg); +} + +VisibilityCheckBox::indicator:unchecked { + image: url(:res/images/invisible.svg); +} + +VisibilityCheckBox::indicator:checked:hover { + image: url(./Dark/visible.svg); +} + +VisibilityCheckBox::indicator:unchecked:hover { + image: url(:res/images/invisible.svg); +} + +* [themeID="trashIcon"] { + qproperty-icon: url(./Dark/trash.svg); +} + +* [themeID="revertIcon"] { + qproperty-icon: url(./Dark/revert.svg); +} diff --git a/UI/data/themes/System.qss b/UI/data/themes/System.qss new file mode 100644 index 0000000..c68e0c4 --- /dev/null +++ b/UI/data/themes/System.qss @@ -0,0 +1,206 @@ +/* Intentionnally left blank */ +/* Themes are created using Qt CSS, you can visit */ +/* http://doc.qt.io/qt-5/stylesheet-reference.html and */ +/* http://doc.qt.io/qt-5/stylesheet-examples.html for examples. */ + +/* OBS will use the theme filename for the settings. */ +/* You can ship images using relative paths in qss. */ +/* Dark Theme is a good place to start if you need */ +/* a template. */ + + +/* We need to set back the icons, or the preview wont stick. */ + +* [themeID="addIconSmall"] { + qproperty-icon: url(:/res/images/plus.svg); +} + +* [themeID="removeIconSmall"] { + qproperty-icon: url(:/res/images/minus.svg); +} + +* [themeID="propertiesIconSmall"] { + qproperty-icon: url(:/settings/images/settings/general.svg); +} + +* [themeID="configIconSmall"] { + qproperty-icon: url(:/settings/images/settings/general.svg); +} + +* [themeID="upArrowIconSmall"] { + qproperty-icon: url(:/res/images/up.svg); +} + +* [themeID="refreshIconSmall"] { + qproperty-icon: url(:/res/images/refresh.svg); +} + +* [themeID="downArrowIconSmall"] { + qproperty-icon: url(:/res/images/down.svg); +} + +MuteCheckBox { + outline: none; +} + +MuteCheckBox::indicator:checked { + image: url(:/res/images/mute.svg); +} + +MuteCheckBox::indicator:unchecked { + image: url(:/settings/images/settings/audio.svg); +} + +SourceTreeSubItemCheckBox { + background: transparent; + outline: none; +} + +SourceTreeSubItemCheckBox::indicator { + width: 10px; + height: 10px; +} + +SourceTreeSubItemCheckBox::indicator:checked { + image: url(:/res/images/expand.svg); +} + +SourceTreeSubItemCheckBox::indicator:unchecked { + image: url(:/res/images/down.svg); +} + +OBSHotkeyLabel[hotkeyPairHover=true] { + color: red; +} + + +/* Volume Control */ + +VolumeMeter { + qproperty-backgroundNominalColor: rgb(15, 100, 15); + qproperty-backgroundWarningColor: rgb(100, 100, 15); + qproperty-backgroundErrorColor: rgb(100, 15, 15); + qproperty-foregroundNominalColor: rgb(50, 200, 50); + qproperty-foregroundWarningColor: rgb(255, 200, 50); + qproperty-foregroundErrorColor: rgb(200, 50, 50); + qproperty-magnitudeColor: rgb(0, 0, 0); + qproperty-majorTickColor: rgb(0, 0, 0); + qproperty-minorTickColor: rgb(50, 50, 50); +} + + +/* Label warning/error */ + +QLabel#warningLabel { + color: rgb(192, 128, 0); + font-weight: bold; +} + +QLabel#errorLabel { + color: rgb(192, 0, 0); + font-weight: bold; +} + +* [themeID="warning"] { + color: rgb(192, 128, 0); + font-weight: bold; +} + +* [themeID="error"] { + color: rgb(192, 0, 0); + font-weight: bold; +} + +* [themeID="good"] { + color: rgb(0, 128, 0); + font-weight: bold; +} + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: 36px; + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: 16px; + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: #DCD9D7; +} + +/* Preview background color */ + +OBSQTDisplay { + qproperty-displayBackgroundColor: rgb(76, 76, 76); +} + +/* Preview/Program labels */ + +* [themeID="previewProgramLabels"] { + font-size: 18px; + font-weight: bold; + color: rgb(122,121,122); +} + +/* Settings Icons */ + +OBSBasicSettings { + qproperty-generalIcon: url(:settings/images/settings/general.svg); + qproperty-streamIcon: url(:settings/images/settings/stream.svg); + qproperty-outputIcon: url(:settings/images/settings/output.svg); + qproperty-audioIcon: url(:settings/images/settings/audio.svg); + qproperty-videoIcon: url(:settings/images/settings/video.svg); + qproperty-hotkeysIcon: url(:settings/images/settings/hotkeys.svg); + qproperty-advancedIcon: url(:settings/images/settings/advanced.svg); +} + +OBSBasicSettings QListWidget::item { + padding-top: 5px; + padding-bottom: 5px; +} + +/* Locked CheckBox */ + +LockedCheckBox { + outline: none; + background: transparent; +} + +LockedCheckBox::indicator:checked { + image: url(:res/images/locked.svg); +} + +LockedCheckBox::indicator:unchecked { + image: url(:res/images/unlocked.svg); +} + +/* Visibilty CheckBox */ + +VisibilityCheckBox { + outline: none; + background: transparent; +} + +VisibilityCheckBox::indicator:checked { + image: url(:res/images/visible.svg); +} + +VisibilityCheckBox::indicator:unchecked { + image: url(:res/images/invisible.svg); +} + +* [themeID="trashIcon"] { + qproperty-icon: url(:res/images/trash.svg); +} + +* [themeID="revertIcon"] { + qproperty-icon: url(:res/images/revert.svg); +} diff --git a/UI/display-helpers.hpp b/UI/display-helpers.hpp index 27ef174..de0592f 100644 --- a/UI/display-helpers.hpp +++ b/UI/display-helpers.hpp @@ -17,6 +17,10 @@ #pragma once +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +#define SUPPORTS_FRACTIONAL_SCALING +#endif + static inline void GetScaleAndCenterPos( int baseCX, int baseCY, int windowCX, int windowCY, int &x, int &y, float &scale) @@ -51,5 +55,9 @@ static inline void GetCenterPosFromFixedScale( static inline QSize GetPixelSize(QWidget *widget) { +#ifdef SUPPORTS_FRACTIONAL_SCALING + return widget->size() * widget->devicePixelRatioF(); +#else return widget->size() * widget->devicePixelRatio(); +#endif } diff --git a/UI/double-slider.cpp b/UI/double-slider.cpp index 14a9586..80fa5d6 100644 --- a/UI/double-slider.cpp +++ b/UI/double-slider.cpp @@ -2,7 +2,7 @@ #include -DoubleSlider::DoubleSlider(QWidget *parent) : QSlider(parent) +DoubleSlider::DoubleSlider(QWidget *parent) : SliderIgnoreScroll(parent) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(intValChanged(int))); diff --git a/UI/double-slider.hpp b/UI/double-slider.hpp index fff6b5b..c0ec8e3 100644 --- a/UI/double-slider.hpp +++ b/UI/double-slider.hpp @@ -1,8 +1,9 @@ #pragma once #include +#include "slider-ignorewheel.hpp" -class DoubleSlider : public QSlider { +class DoubleSlider : public SliderIgnoreScroll { Q_OBJECT double minVal, maxVal, minStep; diff --git a/UI/expand-checkbox.hpp b/UI/expand-checkbox.hpp index 375a4ce..b83160d 100644 --- a/UI/expand-checkbox.hpp +++ b/UI/expand-checkbox.hpp @@ -1,3 +1,5 @@ +#pragma once + #include class ExpandCheckBox : public QCheckBox { diff --git a/UI/forms/AutoConfigStreamPage.ui b/UI/forms/AutoConfigStreamPage.ui index 594f99e..c52e379 100644 --- a/UI/forms/AutoConfigStreamPage.ui +++ b/UI/forms/AutoConfigStreamPage.ui @@ -13,243 +13,414 @@ - - - QFormLayout::ExpandingFieldsGrow + + + 0 - - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + 0 - - - - Basic.AutoConfig.StreamPage.Service + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + - - 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 - - - + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 87 + 0 + + + + + + - BandwidthTest.Region.US + Basic.AutoConfig.StreamPage.Service + + + service - - - - BandwidthTest.Region.EU - - - - - - - BandwidthTest.Region.Other - - + + - - - - - - - 500 - - - 10000 - - - 2500 - - - - - - - Basic.Settings.Output.VideoBitrate - - - bitrate + + + + 1 + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 87 + 17 + + + + + + + + + + Basic.AutoConfig.StreamPage.ConnectAccount + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 87 + 17 + + + + + + + + + + Basic.AutoConfig.StreamPage.UseStreamKey + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Basic.AutoConfig.StreamPage.Server + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + Basic.AutoConfig.StreamPage.StreamKey + + + true + + + key + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + QLineEdit::Password + + + + + + + Show + + + + + + + + + + Basic.Settings.Output.VideoBitrate + + + bitrate + + + + + + + + + + 500 + + + 10000 + + + 2500 + + + + + + + Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip + + + Basic.AutoConfig.StreamPage.PreferHardwareEncoding + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 87 + 17 + + + + + + + + Basic.AutoConfig.StreamPage.PerformBandwidthTest + + + true + + + + + + + BandwidthTest.Region + + + + + + BandwidthTest.Region.Asia + + + + + + + BandwidthTest.Region.US + + + + + + + BandwidthTest.Region.EU + + + + + + + BandwidthTest.Region.Other + + + + + + + + + + Basic.AutoConfig.StreamPage.ConnectAccount + + + + + + + Basic.AutoConfig.StreamPage.DisconnectAccount + + + + + - - streamType - service - server - customServer - key - show - preferHardware - doBandwidthTest - regionUS - regionEU - regionAsia - regionOther - - + + + connectAccount2 + clicked() + connectAccount + click() + + + 382 + 279 + + + 114 + 82 + + + + diff --git a/UI/forms/OBSAbout.ui b/UI/forms/OBSAbout.ui new file mode 100644 index 0000000..790a965 --- /dev/null +++ b/UI/forms/OBSAbout.ui @@ -0,0 +1,219 @@ + + + OBSAbout + + + + 0 + 0 + 840 + 519 + + + + About + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 30 + + + 30 + + + 10 + + + 6 + + + 6 + + + + + + 256 + 256 + + + + + 256 + 256 + + + + + + + :res/images/obs.png + + + true + + + + + + + 0 + + + + + OBS Studio + + + + + + + Version + + + + + + + About.Info + + + true + + + + + + + Contribute + + + true + + + + + + + Donate + + + + + + + Get Involved + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + true + + + + + + + + + + + + 0 + 60 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + About + + + Qt::AlignCenter + + + + + + + Authors + + + Qt::AlignCenter + + + + + + + License + + + Qt::AlignCenter + + + + + + + + + + + ClickableLabel + QLabel +
clickable-label.hpp
+
+
+ + +
diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 983a869..b082729 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -67,8 +67,18 @@ - 4 + 0 + + + + StudioMode.Preview + + + Qt::AlignBottom|Qt::AlignHCenter + + + @@ -106,7 +116,7 @@ 0 0 1079 - 21 + 22 @@ -152,6 +162,8 @@ + + @@ -176,6 +188,8 @@ + + @@ -293,6 +307,7 @@ + @@ -326,7 +341,7 @@ - + QDockWidget::AllDockWidgetFeatures @@ -458,7 +473,7 @@ - + QDockWidget::AllDockWidgetFeatures @@ -591,7 +606,7 @@
- + QDockWidget::AllDockWidgetFeatures @@ -641,7 +656,7 @@ 0 0 - 230 + 64 16 @@ -695,7 +710,7 @@ 0 0 16 - 230 + 28 @@ -728,7 +743,7 @@ - + QDockWidget::AllDockWidgetFeatures @@ -975,7 +990,7 @@ - + QDockWidget::AllDockWidgetFeatures @@ -1351,6 +1366,16 @@ Ctrl+D + + + Basic.MainMenu.Edit.Transform.VerticalCenter + + + + + Basic.MainMenu.Edit.Transform.HorizontalCenter + + Basic.MainMenu.Edit.Transform.FlipHorizontal @@ -1656,6 +1681,22 @@ Basic.MainMenu.Help.Discord + + + true + + + true + + + Basic.Stats + + + + + Basic.MainMenu.Help.About + + @@ -1686,6 +1727,12 @@ QListView
source-tree.hpp
+ + OBSDock + QDockWidget +
window-dock.hpp
+ 1 +
diff --git a/UI/forms/OBSBasicFilters.ui b/UI/forms/OBSBasicFilters.ui index b25d5d3..572a31f 100644 --- a/UI/forms/OBSBasicFilters.ui +++ b/UI/forms/OBSBasicFilters.ui @@ -425,6 +425,42 @@ + + + + :/res/images/list_remove.png:/res/images/list_remove.png + + + Remove + + + Del + + + + + + :/res/images/up.png:/res/images/up.png + + + Basic.MainMenu.Edit.Order.MoveUp + + + Ctrl+Up + + + + + + :/res/images/down.png:/res/images/down.png + + + Basic.MainMenu.Edit.Order.MoveDown + + + Ctrl+Down + +
diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index c92fc9e..328a887 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -44,8 +44,8 @@ - 48 - 48 + 32 + 32 @@ -57,7 +57,7 @@ - :/settings/images/settings/system-settings-3.png:/settings/images/settings/system-settings-3.png + :/settings/images/settings/general.svg:/settings/images/settings/general.svg @@ -66,7 +66,7 @@ - :/settings/images/settings/network.png:/settings/images/settings/network.png + :/settings/images/settings/stream.svg:/settings/images/settings/stream.svg @@ -75,7 +75,7 @@ - :/settings/images/settings/network-bluetooth.png:/settings/images/settings/network-bluetooth.png + :/settings/images/settings/output.svg:/settings/images/settings/output.svg @@ -84,7 +84,7 @@ - :/settings/images/settings/decibel_audio_player.png:/settings/images/settings/decibel_audio_player.png + :/settings/images/settings/audio.svg:/settings/images/settings/audio.svg @@ -93,7 +93,7 @@ - :/settings/images/settings/video-display-3.png:/settings/images/settings/video-display-3.png + :/settings/images/settings/video.svg:/settings/images/settings/video.svg @@ -102,7 +102,7 @@ - :/settings/images/settings/preferences-desktop-keyboard-shortcuts.png:/settings/images/settings/preferences-desktop-keyboard-shortcuts.png + :/settings/images/settings/hotkeys.svg:/settings/images/settings/hotkeys.svg @@ -111,7 +111,7 @@ - :/settings/images/settings/advanced.png:/settings/images/settings/advanced.png + :/settings/images/settings/advanced.svg:/settings/images/settings/advanced.svg @@ -137,6 +137,12 @@ + + QFrame::NoFrame + + + QFrame::Plain + true @@ -145,8 +151,8 @@ 0 0 - 801 - 836 + 808 + 989 @@ -540,6 +546,58 @@ + + + + StudioMode.Preview + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + + + Basic.Settings.General.OverflowAlwaysVisible + + + + + + + Qt::Horizontal + + + + 170 + 5 + + + + + + + + Basic.Settings.General.OverflowSelectionHidden + + + + + + + Basic.Settings.General.OverflowHidden + + + + + + @@ -582,6 +640,16 @@ + + + + Basic.Settings.General.TogglePreviewProgramLabels + + + true + + + @@ -686,89 +754,391 @@ 0 - + - + 0 0 - - - 0 + + + QFormLayout::AllNonFixedFieldsGrow - - 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - 0 - - - 0 - - - - - - 0 - 0 - + + + + Basic.AutoConfig.StreamPage.Service - - - 0 + + service + + + + + + + 20 + + + + + + + Qt::Horizontal + + + + 170 + 0 + + + + + + + + + + + + 0 + 0 + + + + 1 + + + + + + + Qt::Horizontal - - 0 + + QSizePolicy::Fixed - - 0 - - - 0 + + + 170 + 19 + + + + + - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - - 170 - 0 - - - - Basic.Settings.Stream.StreamType - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - streamType - - - - - - - - - - - - - Qt::Horizontal + + + Basic.AutoConfig.StreamPage.ConnectAccount + + + + Qt::Horizontal + + + + 40 + 10 + + + + - - - + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 170 + 19 + + + + + + + + + + Basic.AutoConfig.StreamPage.UseStreamKey + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Basic.AutoConfig.StreamPage.Server + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + Basic.AutoConfig.StreamPage.StreamKey + + + true + + + key + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + QLineEdit::Password + + + + + + + Show + + + + + + + + + + Qt::Horizontal + + + + 170 + 8 + + + + + + + + + + Basic.AutoConfig.StreamPage.ConnectAccount + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Basic.AutoConfig.StreamPage.DisconnectAccount + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Basic.Settings.Stream.Custom.UseAuthentication + + + + + + + + + + Basic.Settings.Stream.Custom.Username + + + authUsername + + + + + + + Basic.Settings.Stream.Custom.Password + + + authPw + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QLineEdit::Password + + + + + + + Show + + + + + + + + + + Basic.Settings.Stream.BandwidthTestMode + + + + + @@ -792,13 +1162,19 @@ true + + QFrame::NoFrame + + + QFrame::Plain + 0 0 - 818 - 697 + 747 + 808 @@ -874,13 +1250,6 @@ - - - - Qt::Horizontal - - - @@ -1654,13 +2023,6 @@ - - - - Qt::Horizontal - - - @@ -1680,6 +2042,270 @@ 0 + + + + + 0 + 0 + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + + + + + + 0 + 0 + + + + + 170 + 0 + + + + Basic.Settings.Output.Simple.SavePath + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + advOutRecPath + + + + + + + + + true + + + + + + + true + + + Browse + + + + + + + + + Basic.Settings.Output.NoSpaceFileName + + + true + + + + + + + Basic.Settings.Output.Format + + + advOutRecFormat + + + + + + + + flv + + + + + mp4 + + + + + mov + + + + + mkv + + + + + ts + + + + + m3u8 + + + + + + + + Basic.Settings.Output.Adv.AudioTrack + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + + + 2 + + + + + + + 3 + + + + + + + 4 + + + + + + + 5 + + + + + + + 6 + + + + + + + + + + Basic.Settings.Output.Encoder + + + advOutRecEncoder + + + + + + + + + + + 0 + 0 + + + + Qt::RightToLeft + + + Basic.Settings.Output.Adv.Rescale + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + true + + + + + + + + + + Basic.Settings.Output.CustomMuxerSettings + + + advOutMuxCustom + + + + + + + + + @@ -1695,270 +2321,6 @@ 0 - - - - - 0 - 0 - - - - - QFormLayout::AllNonFixedFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 0 - - - - - - 0 - 0 - - - - - 170 - 0 - - - - Basic.Settings.Output.Simple.SavePath - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - advOutRecPath - - - - - - - - - true - - - - - - - true - - - Browse - - - - - - - - - Basic.Settings.Output.NoSpaceFileName - - - true - - - - - - - Basic.Settings.Output.Format - - - advOutRecFormat - - - - - - - - flv - - - - - mp4 - - - - - mov - - - - - mkv - - - - - ts - - - - - m3u8 - - - - - - - - Basic.Settings.Output.Adv.AudioTrack - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - - - 2 - - - - - - - 3 - - - - - - - 4 - - - - - - - 5 - - - - - - - 6 - - - - - - - - - - Basic.Settings.Output.Encoder - - - advOutRecEncoder - - - - - - - - - - - 0 - 0 - - - - Qt::RightToLeft - - - Basic.Settings.Output.Adv.Rescale - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - false - - - true - - - - - - - - - - Basic.Settings.Output.CustomMuxerSettings - - - advOutMuxCustom - - - - - - - - - @@ -2247,7 +2609,7 @@ 0 - + 1 @@ -2257,35 +2619,35 @@ - + 2 - + 3 - + 4 - + 5 - + 6 @@ -3121,13 +3483,6 @@ - - - - Qt::Horizontal - - - @@ -3237,272 +3592,479 @@ - - - QFormLayout::AllNonFixedFieldsGrow + + + 0 - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + 0 - - - - Basic.Settings.Audio.SampleRate + + 0 + + + 0 + + + + + QFrame::NoFrame - - sampleRate + + QFrame::Plain - - - - - - 44.1khz - - - 0 - - - - 44.1khz - - - - - 48khz - - - - - - - - Basic.Settings.Audio.Channels - - - channelSetup - - - - - - - Mono - - - 0 - - - - Mono - - - - - Stereo - - - - - 2.1 - - - - - 4.0 - - - - - 4.1 - - - - - 5.1 - - - - - 7.1 - - - - - - - - - 170 - 0 - - - - Basic.Settings.Audio.DesktopDevice - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - desktopAudioDevice1 - - - - - - - true - - - - - - - Basic.Settings.Audio.DesktopDevice2 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - desktopAudioDevice2 - - - - - - - true - - - - - - - Basic.Settings.Audio.AuxDevice - - - auxAudioDevice1 - - - - - - - true - - - - - - - Basic.Settings.Audio.AuxDevice2 - - - auxAudioDevice2 - - - - - - - true - - - - - - - Basic.Settings.Audio.AuxDevice3 - - - auxAudioDevice3 - - - - - - - true - - - - - - - Basic.Settings.Audio.MeterDecayRate - - - meterDecayRate - - - - - - - Basic.Settings.Audio.MeterDecayRate.Fast - - - 0 - - - - Basic.Settings.Audio.MeterDecayRate.Fast - - - - - Basic.Settings.Audio.MeterDecayRate.Medium - - - - - Basic.Settings.Audio.MeterDecayRate.Slow - - - - - - - - Basic.Settings.Audio.PeakMeterType - - - - - - - 0 - - - - Basic.Settings.Audio.PeakMeterType.SamplePeak - - - - - Basic.Settings.Audio.PeakMeterType.TruePeak - - - - - - true - + + + 0 + 0 + 594 + 807 + + + + QFrame::Plain + + + true + + 0 0 - 800 - 69 + 594 + 807 + + + 0 + + + 0 + + + 0 + + + 9 + + + + + + + + Basic.Settings.General + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + + + + 170 + 0 + + + + Basic.Settings.Audio.SampleRate + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sampleRate + + + + + + + 44.1khz + + + 0 + + + + 44.1khz + + + + + 48khz + + + + + + + + Basic.Settings.Audio.Channels + + + channelSetup + + + + + + + Mono + + + 0 + + + + Mono + + + + + Stereo + + + + + 2.1 + + + + + 4.0 + + + + + 4.1 + + + + + 5.1 + + + + + 7.1 + + + + + + + + + + + Basic.Settings.Audio.Devices + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + + + + 170 + 0 + + + + Basic.Settings.Audio.DesktopDevice + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + desktopAudioDevice1 + + + + + + + true + + + + + + + Basic.Settings.Audio.DesktopDevice2 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + desktopAudioDevice2 + + + + + + + true + + + + + + + Basic.Settings.Audio.AuxDevice + + + auxAudioDevice1 + + + + + + + true + + + + + + + Basic.Settings.Audio.AuxDevice2 + + + auxAudioDevice2 + + + + + + + true + + + + + + + Basic.Settings.Audio.AuxDevice3 + + + auxAudioDevice3 + + + + + + + true + + + + + + + true + + + + + + + Basic.Settings.Audio.AuxDevice4 + + + auxAudioDevice4 + + + + + + + + + + Basic.Settings.Audio.Meters + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + + + + 170 + 0 + + + + Basic.Settings.Audio.MeterDecayRate + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + meterDecayRate + + + + + + + Basic.Settings.Audio.MeterDecayRate.Fast + + + 0 + + + + Basic.Settings.Audio.MeterDecayRate.Fast + + + + + Basic.Settings.Audio.MeterDecayRate.Medium + + + + + Basic.Settings.Audio.MeterDecayRate.Slow + + + + + + + + Basic.Settings.Audio.PeakMeterType + + + peakMeterType + + + + + + + 0 + + + + Basic.Settings.Audio.PeakMeterType.SamplePeak + + + + + Basic.Settings.Audio.PeakMeterType.TruePeak + + + + + + + + + + + Basic.Settings.Advanced + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + + + Basic.Settings.Advanced.Audio.MonitoringDevice + + + monitoringDevice + + + + + + + + + + Qt::Horizontal + + + + 170 + 20 + + + + + + + + Basic.Settings.Advanced.Audio.DisableAudioDucking + + + + + + + + + + Basic.Settings.Hotkeys + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + + + + + + - + @@ -3515,7 +4077,7 @@ - + @@ -3528,19 +4090,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -3696,6 +4245,11 @@ 24 NTSC + + + 25 PAL + + 29.97 @@ -3711,6 +4265,11 @@ 48 + + + 50 PAL + + 59.94 @@ -3823,15 +4382,15 @@ 0 - - color: rgb(255, 0, 4); - true + + error + @@ -3845,8 +4404,8 @@ 0 0 - 818 - 697 + 98 + 28 @@ -3892,8 +4451,8 @@ 0 0 - 803 - 807 + 765 + 993 @@ -4159,57 +4718,6 @@ - - - - Basic.Settings.Audio - - - - QFormLayout::AllNonFixedFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 2 - - - - - Basic.Settings.Advanced.Audio.MonitoringDevice - - - monitoringDevice - - - - - - - - - - Basic.Settings.Advanced.Audio.DisableAudioDucking - - - - - - - Qt::Horizontal - - - - 170 - 20 - - - - - - - @@ -4245,7 +4753,7 @@ - + 0 @@ -4277,7 +4785,7 @@ - + Basic.Settings.Output.ReplayBuffer.Prefix @@ -4300,6 +4808,26 @@ + + + + Basic.Settings.Advanced.AutoRemux + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -4668,28 +5196,28 @@ - - color: rgb(255, 0, 4); - true + + error + - - color: rgb(255, 0, 4); - true + + error + @@ -4711,15 +5239,59 @@ listWidget - streamType + scrollArea_2 + language + theme + enableAutoUpdates + openStatsOnStartup + warnBeforeStreamStart + warnBeforeStreamStop + recordWhenStreaming + keepRecordStreamStops + replayWhileStreaming + keepReplayStreamStops + snappingEnabled + snapDistance + screenSnapping + sourceSnapping + centerSnapping + hideProjectorCursor + projectorAlwaysOnTop + saveProjectors + systemTrayEnabled + systemTrayWhenStarted + systemTrayAlways + overflowHide + overflowAlwaysVisible + overflowSelectionHide + doubleClickSwitch + studioPortraitLayout + multiviewMouseSwitch + multiviewDrawNames + multiviewDrawAreas + multiviewLayout + service + connectAccount + useStreamKey + server + customServer + key + show + connectAccount2 + disconnectAccount + useAuth + authUsername + authPw + authPwShow + scrollArea_3 outputMode simpleOutputVBitrate + simpleOutStrEncoder simpleOutputABitrate simpleOutAdvanced + simpleOutEnforce simpleOutPreset simpleOutCustom - simpleOutEnforce - simpleOutStrEncoder simpleOutputPath simpleOutputBrowse simpleNoSpace @@ -4756,10 +5328,12 @@ advOutRecUseRescale advOutRecRescale advOutMuxCustom + advOutFFType advOutFFRecPath advOutFFPathBrowse - advOutFFURL + advOutFFNoSpace advOutFFFormat + advOutFFMCfg advOutFFVBitrate advOutFFVGOPSize advOutFFUseRescale @@ -4776,9 +5350,7 @@ advOutFFTrack6 advOutFFAEncoder advOutFFACfg - advOutFFType - advOutFFMCfg - advOutFFNoSpace + advOutFFURL advOutTrack1Bitrate advOutTrack1Name advOutTrack2Bitrate @@ -4791,6 +5363,9 @@ advOutTrack5Name advOutTrack6Bitrate advOutTrack6Name + advReplayBuf + advRBSecMax + advRBMegsMax sampleRate channelSetup desktopAudioDevice1 @@ -4798,7 +5373,11 @@ auxAudioDevice1 auxAudioDevice2 auxAudioDevice3 - audioSourceScrollArea + auxAudioDevice4 + meterDecayRate + peakMeterType + monitoringDevice + disableAudioDucking baseResolution outputResolution downscaleFilter @@ -4816,9 +5395,9 @@ colorRange disableOSXVSync resetOSXVSync - monitoringDevice filenameFormatting overwriteIfExists + autoRemux simpleRBPrefix simpleRBSuffix streamDelayEnable @@ -4830,27 +5409,8 @@ bindToIP enableNewSocketLoop enableLowLatencyMode - warnBeforeStreamStop - recordWhenStreaming - keepRecordStreamStops - replayWhileStreaming - keepReplayStreamStops - snappingEnabled - screenSnapping - centerSnapping - sourceSnapping - snapDistance - hideProjectorCursor - projectorAlwaysOnTop - saveProjectors - systemTrayEnabled - systemTrayWhenStarted - systemTrayAlways - enableAutoUpdates - warnBeforeStreamStart - scrollArea_2 - language - theme + browserHWAccel + disableFocusHotkeys @@ -4899,8 +5459,8 @@ 16 - 250 - 39 + 401 + 64 @@ -4911,12 +5471,12 @@ setVisible(bool) - 250 - 39 + 251 + 64 - 250 - 39 + 251 + 64 @@ -4927,12 +5487,12 @@ setVisible(bool) - 250 - 39 + 251 + 64 - 250 - 39 + 251 + 64 @@ -4943,12 +5503,12 @@ setVisible(bool) - 250 - 39 + 251 + 64 - 250 - 39 + 251 + 64 @@ -4959,12 +5519,12 @@ setVisible(bool) - 250 - 39 + 251 + 64 - 250 - 39 + 251 + 64 @@ -4975,12 +5535,12 @@ setCurrentIndex(int) - 232 - 41 + 260 + 73 - 241 - 30 + 242 + 85 @@ -4991,12 +5551,12 @@ setEnabled(bool) - 259 - 60 + 260 + 85 - 228 - 50 + 229 + 85 @@ -5007,12 +5567,12 @@ setEnabled(bool) - 259 - 39 + 260 + 64 - 228 - 29 + 229 + 64 @@ -5023,12 +5583,12 @@ setEnabled(bool) - 168 - 56 + 260 + 85 - 228 - 50 + 229 + 85 @@ -5039,12 +5599,12 @@ setCurrentIndex(int) - 250 - 50 + 260 + 85 - 250 - 52 + 260 + 85 @@ -5055,12 +5615,12 @@ setVisible(bool) - 250 - 39 + 251 + 64 - 250 - 39 + 251 + 64 @@ -5071,11 +5631,11 @@ setEnabled(bool) - 950 + 933 579 - 950 + 933 602 @@ -5087,11 +5647,11 @@ setEnabled(bool) - 950 + 933 579 - 950 + 933 625 @@ -5119,7 +5679,7 @@ setEnabled(bool) - 950 + 933 340 @@ -5135,11 +5695,11 @@ setEnabled(bool) - 950 + 933 340 - 950 + 933 366 @@ -5151,11 +5711,11 @@ setEnabled(bool) - 950 + 933 340 - 950 + 933 389 @@ -5167,11 +5727,11 @@ setEnabled(bool) - 950 + 933 340 - 950 + 933 412 @@ -5183,11 +5743,11 @@ setEnabled(bool) - 950 + 933 340 - 950 + 933 435 @@ -5199,11 +5759,11 @@ setEnabled(bool) - 950 + 933 222 - 950 + 933 245 @@ -5215,11 +5775,11 @@ setEnabled(bool) - 950 + 933 268 - 950 + 933 291 @@ -5288,5 +5848,21 @@ + + connectAccount2 + clicked() + connectAccount + click() + + + 484 + 142 + + + 454 + 87 + + + diff --git a/UI/forms/OBSLicenseAgreement.ui b/UI/forms/OBSLicenseAgreement.ui deleted file mode 100644 index e9c2d49..0000000 --- a/UI/forms/OBSLicenseAgreement.ui +++ /dev/null @@ -1,108 +0,0 @@ - - - OBSLicenseAgreement - - - - 0 - 0 - 457 - 430 - - - - - 200 - 300 - - - - LicenseAgreement - - - - - - LicenseAgreement.PleaseReview - - - Qt::RichText - - - true - - - true - - - Qt::TextBrowserInteraction - - - - - - - - - - true - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - OK - - - - - - - - - - - - agree - clicked() - OBSLicenseAgreement - accept() - - - 138 - 419 - - - 40 - 424 - - - - - diff --git a/UI/forms/OBSRemux.ui b/UI/forms/OBSRemux.ui index 4c74e56..0f762b3 100644 --- a/UI/forms/OBSRemux.ui +++ b/UI/forms/OBSRemux.ui @@ -6,77 +6,62 @@ 0 0 - 491 - 124 + 850 + 400 RemuxRecordings - - - - - Remux.SourceFile - - - - - - - Remux.TargetFile - - - - - - - - - - - - - Browse - - - - - - - - - - - - - - - Browse - - - - - - - - - 24 - - - - - - - - - QDialogButtonBox::Ok|QDialogButtonBox::Close - - - - - - + + + + + Remux.HelpText + + + + + + + 6 + + + + + QDialogButtonBox::Close|QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults + + + + + + + + + QAbstractItemView::NoSelection + + + 23 + + + 23 + + + false + + + 23 + + + + + + + 24 + + + + diff --git a/UI/forms/images/add.png b/UI/forms/images/add.png deleted file mode 100644 index 5413c1edf6dca084f234ad1c2dbdf2ec2f9ee8e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^4j{}3Bp8~6e2Re-Q%R6tFasmwl)vkMJULGn$B+uf vIOL&erLw6N>JxIT&tDnm{r-UW|9=aIZ diff --git a/UI/forms/images/collapse.png b/UI/forms/images/collapse.png deleted file mode 100644 index 04707a44d211004b6369a376b8af99bc2279e993..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3442 zcmcgv2{#*B*N&l75yfq*)N~0Q)I6)w8X`i(&{V56#5^l82BB(dDT3>z(X^$8hT@v3 zN^3|=?Q@>B*V*gr^E_*vlMaW01<#0|0RRAk5DQZ~ z0Dyx%E00@ji zOm8_vO|JiAoq@P3N!pYakWq&7kE?$WVgx6PBiq9K#ogzplOHXRyEAR?DgpRW*4;s|08=7q0wUWisdMx5Nt|~aDHFD70dV6r4l+ArOt&rZjhk1$a#Q*ei zb@lpXNXL+WdxzZb!~U;rHV~l;m~RHeT|7UI3)z9hoV^*0=L!wph{MuSjBK7zDExRl z$QO(Bdl(Ytq`8%hJ>Rg@aClkWBTB=1VM|mt#Xal|kQ3_#KGkeI)GU8|dq@(}-1P`L zM8$qo_O&f5xiH+m2^~U0X0lOg-BmpdNk|R!&_FkE^II^>Q8nw|=)F>~uF{v5S$l#3-> ztaxhz=y!Ys0mD)z}Z0eZBYN!_pyn z-N?Z5QYgdT~2x?c;%U7f8tV)E<@- zgj%80zlBea`TP|Kl<<34uv6%Bljjeuv4T>HWsFMtr$VVw9++Za>E9+NDwcQIcZVTo-r6|nfHOO7;Q}uC4UK(gRsI=F2LD%`}M()~4R66jw z{#6iV4Fz>5Emscs=ZYE|$c>t(6&IK&<&QWc92&X)Bj+3c^<1mf&^0$!uf3xWJ?Mj8 zH~DWYW|yfRjPQ&A2wEG;*p{WMqK;aN2wZET#FELbl(QPzYbrmge`n~V7y!>58V@aC zrw4{_Ci3Nob)USDK)OTI@<}FZdHcceW;L z^Q=UTS!UtS+Qn92>|a8832bpe7b;fJK{!d}s~Q&krHx;@M@`$=Km&X|vW!pg*@m2r(;rCu9b z$rGvuzP}Xt-Yor=l*~$8{hKE)zzNpZv#S}JPCvQH29*9ay7S$z^z4zuY0vOx=kEcX zkpaauN4OI=B`*E4SOl*&R&4Fy$qZsKcU>BaloVR7%hHSFY*JtAxNJh~6lH~M}M5beL6=M7Q59cmcy8IlewzI2yR*x=EjvD5x0x4#% z<1m30a+J!Fo<2R1_tqE4sXbGc2!FrywL)~<6h@3AcV6*hI;Zx`3%`4NN##nG0HfU&23T|= z%*T&BXear;E17bexwlxauS%?~*;$@7&fC}~@8Us<4)0Oa5Fmkz>nFG6Dsv1R8P8~A zcSr|&YgjFA7+;LwzBzDqsVpFTnFMl%m(!Gjxx}jIOs@^F8<_mLZzt4-p{i&NXYd zZKD?>l~Q1eYAIF|c7wu;n|+(QY8HyB$Gx3uc@Npdkb?xg1`8PQ4wycJB`gz5QjZ@k z?8O(7R7ZxW=J_PV;5FGG@^Kf?zJlU-V-$Ej0o(1)5|zk&${4uLr_kw zf<{g0pyLZb)fFMClUmhj;+>IFz0=bMcpBVh8;c{x9n=6FVXrI+ToKp(KWow8qp7(P z9DaS;F_#>qRL-@Q(rRQ0x3wqVy9EyKcev0VsaB$mu{$5a5_;1=C7$`oy*9=ByWq+% zI$v09-a?-{b;Aq-y#{~TU((Cy=-AVp%Uu3;hm=Rk^|>Jb@7`?42w97~i)OTn5nO{# zovO{);9IQI`uA}W1-SEhC82oXDw`qIzhLs={IT**lC{l(Xd{CV+wYMAv?;!CfjJx7 zEVE)0NHJ$1sFo+6EeA6QLE|OxThQQsNBwaUyCe-qbW|{*wuo-v{(pyZOU1(NlGA_PVtw2i?~A$h zkpk>SAE|iQDS)wUoM-qBP5RuUufYV!k1nT<%v}m~RE|@|iLNp)CmR~fc6@rfHo|dq zk-%<9hnt_#+-V_jaqhziCgQtflBc7TRy@N!P4x*cx^KNtuuOfOS7=+s0i(>;`eP@|@IFH?(zQgMIq)nXoQS4w9H0M@h#+w7wt5cM&K%){ zK%RU%e^9mCZK9czt1gg3RA|sXE*N~@)PZovk0Z1-^@{#w%O(u@SlEN&&m*^WTn)b$QZC><;r#(DI!bFdd( zG89B(R@9pm%Fo=OG1u0?)f3`-KAGZ}wq|^I{BY5FXuUVDltarPwJ}qE49Et;n#)g< zu8r@n7X_eICgNp~Hf`(qj1}XY30B9~Mou3$o8&UC2tjlO!u62nnkJ`fu+abyT653m zl=#Wu&mw?T7qe8{Ql%Vnq%n`j);U9<)F~wf+tO- z($JU;JxmK*4?CT`^Rz5t@B0~hjscEWq}5g% z`~GMz+a-VkDDJF2Ktr*Q15g16s}hnh?9a!4$$a`TfI}CXA-WM!_jd`18O-$kZP&;D E2ew*Y9RL6T diff --git a/UI/forms/images/configuration21_16.png b/UI/forms/images/configuration21_16.png deleted file mode 100644 index 3ddd7178bff38802616071a1508b902df518c79b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmVH?^(ve{@8!b`H)t*R5~?BZdv=3}uu0`oURD$?6z@-C+_1X_bvS$8GgiENUd zd_6f`)uu~3IQ|7Py;SP`wRjn{=Z*0(Wcs$4A8Rh3pKNYH@3ZSppt9O6QmMNc&9YVf pv8YSD;pW|_t_)YbR(9y`+7Gjl*9{GKEt3EM002ovPDHLkV1o5)XE6W( diff --git a/UI/forms/images/down.png b/UI/forms/images/down.png deleted file mode 100644 index 35a8c4b2f41d9f43810d6ea32164b3426c8ba10b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 515 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU_9mN;uuoF`1X2Z+UEd?;}2Ed zKHojVG5SE>iW9BtFKpwPQ5Jpdb%#cWkNR-QSZ({`NM*{ygY&+>S`x8rS zu%y@IZ`&XL?>` z9n=K5q?~d5W^*)oiA2a_O{JkFZm*Yr(@GRM9HP$cQ)$G_|cb4PG{(C<++&lX> zZ)dl9%{sx3da0xFx?hicc6VUq?Qy747P!xG#GL74FM>J$8`GoR9Y6F`_H}p8f70}5 z_lh5SI{Uh-`S&osd)Hf#p5L)ss?l1iakpfnwPfRNiAHOQ#_M@3ahnAz*19R|6YR)m zc_iKV12qxi)#e@S^TuOQaY8G8^*sS&t=Gv?eft#64a8T-G@yGywn) CvE_*X diff --git a/UI/forms/images/down.svg b/UI/forms/images/down.svg new file mode 100644 index 0000000..c827bbd --- /dev/null +++ b/UI/forms/images/down.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/editscene.png b/UI/forms/images/editscene.png deleted file mode 100644 index 65b96e8cc1ad37d4dc1420513cc14ae4cda4fed0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 511 zcmV}FfuY) zdg8>1YpJQJFN}>%o#20{tE&&%v}x1D$B!SQjvMmu-~GLQ*Z2GTUcdW~`!>$q1-5y|W&i+S z*rQG-0YDB6<$z5};Nnf<1b~auB{vr*K(;a2O(jeafrcMF_d5XmqOtMF0rwtgfuJ%O zd)!(1vjS96)%;f#hawQt3&x)%JBAU7gitd03IL8If*+Z1c^4&^9JI>?dmMM!l%Na% zo0qXpzu}`lObg%MA-L!%&xWA`iWjsrR5;IPo3{MT+5*vZ4KJY4@9ct4y=)qJlZyHc zV)YvicZi8VrWoZ49xdeL{(TuzbjwuXgr;kfF%-V=JtD#*!Xwny=+NY;)6*+)BjNm{ zV^%+kr=(M7B41c-xcuLvKdg6nti8{4q2148MV}aZA8V>2lM3MZuJal2K7`GKF&5m+ zFvdZk&ydDs%T)W5RbdtR$LH_EX>O!RnztFe&P-5}EDPm(ZCZVQn*Ys%*;s$DgA!He ziAbE`bot7%*0e=mvKiiScTlpaNWbW{PqUObNCZa-{A)R(c9wUwf_8y*dZOCvA?2DkIzgHr#3!mld(jq6VUKiA)dqm z_6l<0weUJ>4kRirD+l<%&7Qefx~cd9#1y_V&r$EMf{&1hvA-<0x^Guox@_DTLXEOg ztEg4Am&eKBJ$t{JSxQ!{0_R$rJFeJ-N0~#fPC_wFNcJ&#f=x#(Wn{fyk3OZ* zoM57pSZ0*4P=w_vIN=67v%HeZvJ|qTN+EPt%O`dw-Fy+x^Sdg>f*_>UgVc;a-iqj0 z0J$f%nVIY0IV1we96MBpq=9q5wOh4^op- zG9S|80lfESxZr&%B4HtBO|;>>`q@e7(<$f!?c?%iU|i-E0NJlAdahQSj@#ODvmhZc zn2QQO5T(ox_8IxKxD6?f@0AQB%!%l@t(?YRk1|TST*jUY6_)?=j_13V<4G;_$qdCa=?(EzP$k4Em(r5w}^n&t}nA6Z1GNG*5)T|=McL|x&eb~M?cUs z3xsk(N5`ylAoVG_nn-!rgIl<*ImB&uZ|NovAoIjOGfSP)^g%;8WgmC9+(^MBUKz<) ztU`s`J=CKcx&J#ImoZv}0(!FLvFMD^mg=jX=T$L^1XOwCn0Vkq)_)E0moTH)5owV6 z^C+><1j_!*U}wLci}?8^JW9B@yQMZ6%Yy){#qBmP@|^uUP6EhDLy~mJ)sT$Q z#%M|PYmKvah=)0apxp08SoRn+Nae7i;l;;bb#x1e|arH+#Cz&v_ASFIur+^%+zC^2SV zun0GqZd3ARjT^kf6uFLczR}?2ep;%xISusM&c-G;D(>eRVpiIk_qhQ@Sd*xbEb;P(TBzFkN*El6X!vzJR^|(4}U-f8q{oYR#-rO3eMZM71$)y<4H2oQq8F zau7zp^D)YMk1ap3>}ZeZ@~D;Fn^<;y)*u7BF-Y4j@B{m+Bi;AXF6vsj%;~{w;$I=_ zq_1Jr#vhl>rR_>k zy=yk{5I@@~9X#jIqkJr`FKnBz$!ma{H=bxOTHjwVZ%2DHA?h)k?QbLH`|#C}D(f3i ziKH!6N!BBh?r%ECJAZ_dj>wxfsnLhUaeV!?$W7+b@-(6Yr!aX!bZ}BvpS5NLjp)` z*a5W-kqaWJNP;U}q*+tRt1Th%zL%02b~z3&?L;S*`HQ_b6Kb-?Pf^S2*8*?$H57hQ z?h#1@p9dqZO)TqMhzrt4>@Va|3)u>*xneBYRfAXhl(@+H=ANiOHSlKss_fN2iL{|v z1*4}~uLjn8hkGav^=mll2;%K%Q<;)1WG{gRQZkXk^+28MHNvAA4hr2}_2Qt|i*}+U z6pbhE5-=x6qh~HCp}U{32>gmjaxDl~XE?K6Vk%v7lv{b{vtV2eu;XT}3dlWP)Dnet=C^aaZ6?|bUzz1xLbDeXO&YX1*6Jf+>s`|{ zWMPA!r)6?quiwtXhJu8p&OC0xJ&%rpEZHWiwZKnO;F?RFDE!vN z?LsPid8OckkY*=#rttsJ)ncuI`bT0@MV4~BBtz~*XijO@EJ_>X*lSqd5Lqj``cdqg zUafD07nP^~N#utiz$$Ogl%zia4;ECn!((iwxlchiJ2GYHpywh@Aq+;{FBsM-Te}g1 z@y~a=zmMB9A+jK#yH6<%)pnV7xsk21iSNBC12r&=#*)rjCSCYlv$iD-nTWL#C*!9z&0`ml)Ctso% zagpX?N}NhZujavNQ~E^cM86ADih611n^jY7eI3<<#dHcg>h4$Dz6;U<097qIo=X%|ENzqEJF#- zWJ)W!tgE~qh!p~Z`{Gre>;@d&{<-S2Q%nBsJ^M;(4`eQ4=WoNUj8?)K- diff --git a/UI/forms/images/invisible.svg b/UI/forms/images/invisible.svg new file mode 100644 index 0000000..0d1484d --- /dev/null +++ b/UI/forms/images/invisible.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/invisible_mask.png b/UI/forms/images/invisible_mask.png deleted file mode 100644 index 479e13ee1e2f7e49ce7a37d24054f20bf4e50f42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmV+k0rvihP)VICDaL zk8j}klh8F*>j*tt>E}(TteiZgbn|O3?R8#+sf(13)*7<{4__c6XyHmzs=Kf&Y10m07+9C;A@rT4XM5VhMmQEdqYx&pmhJF? zC=2f9FPX#kw8px0UrgVh+)8`DMbE>@O7-@lmbQ9AM{TV8X;%F|H$#X&mxKCi8jzqJ P00000NkvXXu0mjfW2bgt diff --git a/UI/forms/images/list_remove.png b/UI/forms/images/list_remove.png deleted file mode 100644 index 58586626d1e93c276f6f8adbe07c52f05958b74c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^4j{}3Bp8~6e2Re-Q%R6tFasmwl)vkMJV{R%$B+uf qw3%Nyy0o1WSr;eWS!}vTi1SDMu9XClJ1D^;I6Gi(fiosM2!5AcQv#S1l zPIu20;q>+%lwpj3s)iI_Ct#cdkUBdM(^^UZ3S0q7DNHdK)=izfI|q0v!tS0;fk!c^ znmz=%gA-uX0Ir34&IzVxfXO))S>|#8D+T?yDO$(~fKV(!rw}AE!N9dxk4QdPoMWmm zrU95ENZiMyTB;2cWRieWvw-Bif^!&TkfazlFgS)|6+_=qoL3lexKxe3QC~B;CK1zR z1Hwp5$(upn)5cAn-w&XlXAca1Ng|FtKZ#8hOd&9tn4~dGOg1$FGb{^TL5QrL-Vy+< z{9}F7w*jRx6CvkXOc=0uGHL^Y*2HuYYva#Nt?iwT`x8JrZ?T>3N41TqeM+{NVdig6aWAK diff --git a/UI/forms/images/locked.svg b/UI/forms/images/locked.svg new file mode 100644 index 0000000..bcb2cb7 --- /dev/null +++ b/UI/forms/images/locked.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/UI/forms/images/locked_mask.png b/UI/forms/images/locked_mask.png deleted file mode 100644 index 4f05b1a56495c005e037ea94e921a3f7aee425b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPtn~Z=Uo6`NLP@vE{PZ!4!i_=#p+j21(3bfvDIhrMY z)9qkz1xx;enQz|Y?Co6pBJR+!Q%)?b^-{TZ-0mESks`Cd9-MGy&z|Xu&P&3WUN8wK z@XIu42Gk{Qc+hIP;mhAw!5>es7B65ZN_e)lu*&XA1yAkTGjFoe%sr>7D6PBLAf=}g zll$5>fqBl}yTThjTU5Pe%4z%;JzaG>kIK_|)8!1gJm>D%{o(bA|CbLuQt>`k{@s1i f!3P)D^?zrv&~4yR{3sg;bUA~ktDnm{r-UW|XFhJy diff --git a/UI/forms/images/minus.svg b/UI/forms/images/minus.svg new file mode 100644 index 0000000..8a2e2f9 --- /dev/null +++ b/UI/forms/images/minus.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/mute.png b/UI/forms/images/mute.png deleted file mode 100644 index 97cb10a1c9e3e9acf30f871073f4ff15758d5e9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^E_P~Ln;`TUS?!taujL*xP5il zhOp_KOx1n6T*6id4oy5%j zMPS;>MqRsr3&)tOzZ{fi`p2Zov)?OOW6@C_eHjB+W_EkCgaUzeFBIMcGnv|m%rW%j zG diff --git a/UI/forms/images/no_sources.svg b/UI/forms/images/no_sources.svg new file mode 100644 index 0000000..55eb492 --- /dev/null +++ b/UI/forms/images/no_sources.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/UI/forms/images/plus.svg b/UI/forms/images/plus.svg new file mode 100644 index 0000000..de42f6b --- /dev/null +++ b/UI/forms/images/plus.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/properties.png b/UI/forms/images/properties.png deleted file mode 100644 index 3ddd7178bff38802616071a1508b902df518c79b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmVH?^(ve{@8!b`H)t*R5~?BZdv=3}uu0`oURD$?6z@-C+_1X_bvS$8GgiENUd zd_6f`)uu~3IQ|7Py;SP`wRjn{=Z*0(Wcs$4A8Rh3pKNYH@3ZSppt9O6QmMNc&9YVf pv8YSD;pW|_t_)YbR(9y`+7Gjl*9{GKEt3EM002ovPDHLkV1o5)XE6W( diff --git a/UI/forms/images/refresh.png b/UI/forms/images/refresh.png deleted file mode 100644 index e3e4c5b8b7e213b390b41e7d65f88c711107648f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2712 zcmV;J3TO3+P)ee4 z2z(d#3y>=?LW`INO0-~{k>6F})U0rVDqZVPZyR}J};w+Y~7V5GmP;_Xw`!IN1pt2QCKa z-)SU}#spB*a==r-SE{(bJAfy3saH;4U)Ono380{T1biz6eQpN63_Kk`1nW%zSMD9` zNlD*V0SAGxGG7O)O#qj-OTey_^uG}a963?u#4uw5SbXmwEwmzWk<$_TI$#1=y!EDR zJiZSMNAwdMF##;T)w(ZbW3m@GAEB>fq%gcb2~Xe$xJ4FK6I*~cBJ}kPRj`3nZ338g z4)vu>1e+p?;Biv4m7L^u@GRv;8&~Q{5ao5S*95S1+nzEJ?1?Zj93>@@MU@H&*`31G zf$tLW(KK}IjtU824md=*j;22xp`TmJ{n3V)J9Pu_LuBtfX%F0gkn@P|rN~+I4Dbby z&%A=1z^+;WG-}842@mFczorq_aavf1L@~8HUY`T*1Ac>16Eq?lNItKD6W9r)nqn1j z48Idd{ZW6csf^Dx>14|Y%LL-~?ZqtsHLI_JXIY4Hj-77c=jzQe!qbdI7Wo`VP0SV< z$8nD~9wZ^};6|2?)cGAX z7S>#0?$r0X>rnsa8lyZ8c?jTK+&A-S zPbbA%vtQY?;dczpD|?x5s@S_du000)+w2#_FYpJC>tj7QGvJ}bd(3vhxQi-@Aoh6_ z*=ishE0N5lF5~XC^Xl^o*y!Q#c|jjtM!5g+PB>n>LTwdX0JnKudy+aywww@CNWzQY zw(=1`5AN{;uOc2oBMg^-e>z^)WC1+da%1#kgL=3s|- z6~MU1#IBvNiJA?e1xbSKI$<0SDS#1=*FcwtQVn;qxCh$KxcUrS1u*O(fR*YuFv8M? zdywsn+ANp=rf_%etikSWJg$8z z&Kp^W`^lgwEPGW5IuX~d>$u+-_YIx%xS2y{zq0uO3)xSX8}GA8+|XeU0la1QE1L~m zo|30j`)Lw4)PrAj&s}E2vdIA7K#F6O8t)BB+)RhAGhLH5|Jm#pj5SE5@x_Gj5(y=V z${;DpT%Q6Lk(6gfIKGIa;K>z?^LI!v8fz1hoX}MSPhvqLp~)h}t6xF3es4+g$6Jwt z1wIunERSG3dF_8|$eOaMh3!99w#xY|hCRtLQ%fW;Yz*x_mQ6SStNuiH!jSCV1jI9ZhT zJQ9SOmc4^r_?LjMX#s$^PfwT8`7va_AuYRzdsseftF8n9upW55%t1J&p13pwmE4D9 z^)vw#aBBb&j3SXmJF5H%@GO^yYMB5Eo&=`@49Gf1d?Q06E$2UvDaa4Nu{ z%poD?Zy|?IDsckoMX6D0K|&3E)e}spNLT<|k-IQnXy7&Z<8J zzW<5|prL|7I0Af=@WG7JNT}M2NST!(eKe2h^nwDc>S1+T6Z%5MHtwIiATu0>-UBoJY zYM`5FyNLDDHd6<*>brHc4h;IP31DeD@DDFbQ~L!H=()`VP@1hswDCj@7=0Ys*C;OL z*z4H8x)i`X2X#h2l0w4|tHY|lME~}&{N6!b3jke`^#i}f??7s$+IzqwNEvsR)4B&J z9bQ)V0w|m)ZbFWx4_2Y!UqCj3dLk?x`&c^A)C91IC$U2Z`Y?Vui$b?Ez>k2>Ah~?Y zDU`9(1Q4Bdzys=Hp08Rq8^~QdSMYrk$Zp^Lc$8=LV0%5eg5zYP&boWl(R95ozcSjI zxH8gc-q17Ja!8=+n@H~6LH&2!>;{gqP;;TCUBOAxf@~CZs>?T9FiD%yC&xIl-8_u! z7gUP<$f@hDdT<3BORo;{#GG>1O1xDsN7X(PHWc+s6IO!HFbu;m48t&tp!^@zrY~HH Sm!w(%0000 diff --git a/UI/forms/images/revert.svg b/UI/forms/images/revert.svg new file mode 100644 index 0000000..d1e1314 --- /dev/null +++ b/UI/forms/images/revert.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/settings/advanced.png b/UI/forms/images/settings/advanced.png deleted file mode 100644 index 91593e0ff134bf3eab279cbd0450cd488859cfd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1271 zcmVnzCzO#m#mry+Oo$;9 z7g{(?%0EGH!fi(o(m8YXUnIbkjf55~ELT+5+IQ&$=g1T#@O5E)HWF;GGs?^BbO5N# z6O?1p5tM*5PIwqi2_B>wROWkto#o>#?Mfh8q!)R=xg>dFo&1FL>jhc}#F|}_-_sqT-5<@o8SdbhF;l9d4qJ>v( zxjr+z=4fJwA``A^*BnnGNeVNT#o?3VN2Lw8tLuFo@9%n_%)XV)4Bu;pYuf{dxnE6x zkqfJ^>v}(Cwr{p$wqw47lWIhoZ|}uR7o6u^UMIJ2u+5E{l+^F`E)TSPwAo{A9uYps z;!{>MMReTFetZ5`PbrpAK3yxn%QGG|1U`ouD+9&JnYiJa_!S$bz z04N8Sc!GxbTlx?A*~{IGz2Zq#eHUS+=R@~|TXf(wr&g;+qzOvks~wP_6L|O@nqlHW zP5_7b5v%HZm3aVskMKH9PEYE3pSC=%{+I(2SQ!u3mTRqNcndM`81Es9A7O0Y3?*Q8 zf9yzr#4sK&Ws!V&mE*X_jf>L+AM1XyLP#!@r6QtH3i|r^BcJ78=QOOmbmy!vO>mQ! z5lmFZ2dw9NB6U~%2Ri@{zpD0UIH50BzA$X=5r${*GS_fFKh84Fq69>;`XtX&0{nF0 zdB$_|toGuPxeC`9#$$|h@D1;96=u-vfIm+6IL5NWGoN2|iVZM@+qpfbl4JehEw?t% zU$pi~Pfp==QLF62nRIdKiRG6$#t9nc?7{bO8CLQmBcT?e@fGJdo`tShRD|VMn-W9v z^=V%t1_r+m-n5I?9t}{C?mD;b z`r0fuU3c#zSugkyF)#!UU{}4(F9m!DHlF18cM*?he8y__J9eSpY*xY4?2!^$c!bMeAvr{Fk4`AO#C3Q@LJfA>zz;~C zyuwYGLtLnWr(6`Y_kP3`Xcw|z3!-?6PY?qy^08Ld)ckt{9_k^aa||a(DW+{J@3IOf z@GHXGctaOpr6m;SB%J0>242)owDYi#R~YG`F%DRaMM4h)6q$d0CM$gtMY#N9f>H2~ h4;h?8ixT{g diff --git a/UI/forms/images/settings/applications-system-2.png b/UI/forms/images/settings/applications-system-2.png deleted file mode 100644 index 30f418daa65fe6426841a03833d1d1a78670c591..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4616 zcmV+j68G(iP)r--rEAVANr(7iWvlfh4+q3vW@GGs;U7I{ntCdOyi$e&wbVl`cWK)Z3oSh z0Je>YTrmFem%RK%3*cMB9e2~C(L2GZrtb%3Kk#Sa&{v3P=~b_JImwC)R|N?HN9eiF ze!+i^gkJrcSMXnS{jL;O0BEee)Sid39y6-mE{^^Sqx(B32}A34>7geD74ujCEblCC72-B`soF@l*Zm&8`!XClfcx72(Gs#0L;V|o zctp%FuLZ!9&35aZs-k`mq3=qv>?x`m5g|<{QdAr+TqH}R@AVE4bzgT?1!JQkZ6&5uZ$s*)hl)XhSrUb_!T6lPNynUg!*$Sg#Ll8GcqG6Tme zz_ZCTj6Jg1TpkV}8ioiAs#ieB0THD?Lt;N1Ef&nVxw);1tYoprzAzeH{}(6mD_-?d z@lX{uh43lWYIQl4O5JO8xbXVj+jgYlhQVc1SlBlrb{3K$EfOU)q9GFUrUNhnNb28D z>=3{)0wEFK^J#s(984cQ^4*g&C%%e=-xD*fYr#mm-R*qTb=|ka`3j`;&qv_+$z!zh z++CLqdXz!hj!Ug>hkV~>_65@s{EaRQ zjEe|{67hAs2u;o=C>xitl+eyEOmAO1)|$vENNZ^?%lZ zbI;j1h0UIoOJ;5xw_>vG*q3+hUVw+o8rZ}}3{1$uidm!~BJFOM=4WT1*>ytoIRl&{ z5}_!A*U7-W@pzmPi3Gn#4pGPVISLUZ_?Gd>81FCgDG1oPo^5Aao%a0^g)DkEc@Pf%Dmgd0Jjsp+=*@tTJ$b zhJ*YRM2Z388as#2{K|QRec&8lbA{tVirmpRz&DyrsyCbK0ROr5_4VTevS%mcLGZ%X zR@Y=~mQgfKSFy2>Osn0dE*S3hY<>-h$c)FRWq0Z1%!~k!YdsoVhydHb=fPKYsDr)M zN+kj?mLa0f-?OA(oo)~ES)y{adSY>L`CD`I^ZpZ_^tkNpZhza;#4Cf95+=>h&C_+) zU%MOOy(yW_Xjs(gbjWdC4yl4LXLC97z~aiRGJtb#d~)o2>d%qAyKW%EK9xFZC`W2 z`4@Zw4y>tt?O%P})e*?%LRC`6Yt0rak3;3PbxtUhQ_@8x(RHrrgUloJcyP_HRLUi+ zBa4emhvpX+PL} zge5ByWe0qi0EclLl?)C@BPP z%~4%c0E%WaS;`IN$py2GTAiwu%0`V=HBP|(YPFj*H$PL_yLa!me)iL!|8QY`aY0CV z6TA)c*n!i-1)BkUAe(d=CWtU%e?3!z@zT1v-cbuQkk0=ljL4r&k<|5KW z=~fkLpjhZ$l)Jm@I_-spMX|T03h=^8rLu%-<#pipWGZE7mB0qRjo zI{-gh`Q2}S_oeTC=lkD70xlC#1J;K12H8KWOv{^2L}6wJbrQ~ZT3Cqa1CS3FWivUs zXQPAyd>7{6nFcIrTAY7=7!byc>`*?RC!*6ipr8rKVA_-9GaXb8i5bu%QLY@gkz4=j z+Nv{s`0&qv_`@InXcK&8fc-)6;n_p&rr9uO(}F+$Y41TK^w$&dL|%{TW2&rj?{VOO zL>sa)ME|j4WT5j8<%Y(Af1HR8pOH|fs~`=$TqYZ)V{yqv6KT~OB>4du(Fo7-5~@NE zHG6e+r3AhIs#>WE@U>0$J23m708ax_@(1$+?5ltH{U7)3-o5MaP%c-9YP#Hn10ahl z56j5+*`c`TN4hFO;_6@yNC=mh$vq5E@Wvsh-XzKMN!KD9Q>R2CBD^7nIiomMv~a&D zlkGv>{}bS8;QMeOH@H$xPEAPhcq|!sfz;|Xxj~}#6Z#V6k&~8bav28_T3A_vyt)z% zHZlN_AP4XrhUuZ4B=qJXd0iw_x7`tQ3O`XrJuVK7q%XSQ{M#V$fA`I=e{1n<*$e=c z{sHhbkQ*M%MkP{iyy=F&&gX{qD3S~^J7SXKsy943%98eFL;{#SsUmg+H~LEpHI$ zKeNJ3C%jwV_GaVai!Qp$P&RkZgb|DRt}ir25r{*T6_yWfZUihi)jCTEfHy556`K`OIW3~7e^Y8yx3+qnZ>_Ia zzeN=8!``JztL}tY{aGi36kqbXS1OvKCJ^Z@$zCj=AeYcs2pGlto6D*51y-hGz9qS{35k*unyrc zC?kRv#VLvBev&C4Ps_2WF;#6h9#!wO@7Q(iX+-b9a4LK9)vtSH31{i9Xy&hZ^Xn5v zM4wP(X&0P6FQ(~RlRAz=YJ~X`QJ0mFG=Pj{GbC#o=Rpto=eZtF!?l%Vk-Q?2(eN>c zW1Wrw64G5OtsPw}t(Gd)%D9EbVHpPL$bK^(C)L!s?|C9nj!T_Nl|0*FNtjxcjAT~L zn#u95+Z*pX_KiKybv)k_QSb$27zQ=yI--LbLybrV$Pld4W>gsnq?tf}Dmz`Ol-CHe z6Xo(4-BAsX9zTlYUF)DrAFEU<-#;>aL^u%iBM@MdVCK~9@r8wjy-0*a6l>ScUHatM zBzc-b^;Q$QU8OdXdaO{Os9}=ZwFiq1hhJALN<~ctsbmCUU|?Uq3_rO9bLMS`7Oybw z?sWQRmndE_k&5TAV?5wt8eUmlLGpIz_wL*O)<692kH1}BUTzEAO8+1uUR%wkXIkdk z+}!N3F!0kw6#E$1UtGqis9Y8su`Y)&FUerv>2!(1C?-`Sy1|6!iOBcohJNqBYaci< za79%pmdlU{iz3p5>I0qdAeR{#COh=OyvMa4lLi~obmheH<6p%bHM`k&Tv_q;t`iY_fJ~G!^4yW`*y3td?ujR)Odl6kv!!l$H}l_geS3~ zK?YUl)&Q4$RddC1QkumBGAM-Uzs z)q$GqZ#JYJ=AI6s+44Pq@O;bDs0l#vbc)#uMB77d9Gy8yHAH96u3Z!dqupAa9ky_v zRT!p@>!1`5QFN_B^`(`KI*)zUnhm<;=u!TaV&{&XG%{7BO0`PU$EK-)Yy3+F971ms zVe4Ut{N3E_{9+$G=fK&G1?FHb!T{PlxVW@X!H|_fnu&07V_RBYq)~Lo%+v%m8g-h! z_>nG9FRL$KmXY;zHM1n0rGjE zrbd$0YNde+RZhf{;W^vSoq$7;#ib?wMIqR1ZsOHUC1_;(7CtHlXjlu2i=2?CdYt@) zJv+$lIV4*;1#`3g=(1GBm;k_>K&XnV7}r0JO5_)dh4k|B%8$SKjc*?rkWX-S1O}tv z?%TV61uT8kM(V4Iy5$s(UHkX#uiSLwO&0-t0jx#rUT;uh473HgSj7<%@|&5RC2+Cf z8~~Op6|yE`E+hk>o%j`}}_@ppKpU#-@T{qP4ro;D1oOB^}|GzzzHZaJe)6LYPaV&WwL9N8IL?Yf0<9Zj<-N3}lgX2H+%iP&g z;%h!QFS}$57%D6+ElJ5_it|GJ5>cs^m(d!R!Q8g7;#f)~dcRxaJM=xCe80oJyR3zJDe593=`cQP|U^a_LL+^NS~ls0Hi&1IJX@UO+vlO}Sk50!fnNOG}H>Kl|A)zB6-brg`7{rSI)}Zt9G#J*w8L zrDMmA9hjIH-k9ZB6R=p$(KGJGjn(_)J53`SS{Ab z`RTa=ve7ZH2D(rlDoLpO3KPxFMN%TOGAP@-TB>fS0aXKBDtE;Qkv61oF;^HE| zNGxoIlvzxsn5|w|hX^xqujLf3mC+h0 z@#}3?h7tsWL4x2lL#1eXJA diff --git a/UI/forms/images/settings/decibel_audio_player.png b/UI/forms/images/settings/decibel_audio_player.png deleted file mode 100644 index 21d685808ca4925a02257246faf6feaab1ad5e85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4613 zcmV+g68i0lP)3Me?tq8nVGTRGcz+YGc&_d-cpzyvtwJfY~e`5ztyEF zt!m-n-c-A1gy%HhtKhXh7dmrBVo)7RtkytXC?&wanb#$cCX-x-!&0G4h zb})gCW_y(_3DudJK9>GUG^%?lC1psl9k@3+xk8azw=acK!t^3ZduC5=+w0Hh+ zXV2ib4L>mSA-iTD1kheCLaZ6eVBG+|;w9r7xE^?56Taj7e-MBEhku~zQN4ft#!Y9* z%%9Ly^%FrL-suN84~U}x_5?w&O9<(prbSLdl23Vov^kp4s=i&C3$z|b&zDfamtO+k zpmqL{$M8@8@OS#@a}N)9_4c0iJ^zvA<&`bVD}|J0yHX$|2w6c8fIQ>Cu`PIx1xa`a zd>e&x3$LtPxbRd{U)w6(Qi-@g6$=YReudH0@u>o;tA;L&J2`Nl%A zv~7NVFpeo}Rv+S}v5K=rQQAI22Wb5Q0E6a`2-2 z1{2sd6Dtd|@NE+j4KU3Td8ax@_uuYUa=$;b4EGyOfD#e+?%fkxwrp9qZvF7PySjVc znah{fPtGm~C=nQmG!#VzV+{ZD1*_IC7l2tRps;u!$(Tlj$|%#lYuBC;e(l3O{exF8 zJoy4%X({+B*MS2EF*7}-96EGpN8jrHk7;`J(b4glu9Z?1Myd^(5k(j#j9Ubu>n8X zjO*i}dlNH>nlB@kZilQWj4}iohE4q+Ymyd^2obVkFW8j=I@{w|oS%hdRgp}_Zme3} z`{951>wkIr>-NCr&6|@SCG+~ht_(22si^TWA5c%E* zLUQ2MAOc8=3?Bj!Jqq10VB0mwva-Itt@DvHkG%2Hzy8BtRljBrJo4xxxHo!F>+kPB z-rdvtMcWIGjL*zRg%*cFE(h>Z&;-`^L(t9VLeI3I2jF`yc_ELHjcbw1&R}+W3h|hM z-TU@lkQC#C3-b#%|K`vB^sD#4$rC3qdi##NcK!Oj>9+QdS8eymJm1e;M5aD{3ZfV~(`Bw$yQkR*YcWx)p?h{mG3|L;Hk;}*Z`t0i#h@?~-O z@ZphUGW8)h5GNSCo?!5tr%li#9^d-`X)kD!2^zv7#=u*lgtz{p-!O*5@X}*kjQ|1uyv3!QFp_QCs6cc9n0N_+?>!DU z9vh)$ed6OEhgqq_RZV-;^@7Ld7Z!S}o)Ga&8UPw$;<5HUCef-Ha6_{+h%}QJ0-*y0 z8$;6+@_>$-X+kuTIb zA_z(NehYzcaS%xY9=$h11l+0#(=68?X9GBv1<$srKSozKrf08(fF&R#8Hicn^`tk2*ay*+M`_VAlbo_WYuDVuvaT9Q$dVFP5|%LLJ+`>hu)fpu0P9u{gpCbh zBD3{5{`v8N{sF|JM!nku0FiKE7*VcxM3zVt5{fLpl)#VuYtyAeg98}7H@-npwNoqk zQjY@(2_uF82+L$cht~Hv?inx(6Jp(x%)Z~!FT&Ul!v{jchT&n9i+NNj0u)8&_odp~ zp-1EO5Q3`6s-QCUs*UXC&D%sec8HQq7i`A?laW-kcXy(%uNPNdxKQWB9w8$l-O&k1GBiU+Je3NH&eAJ);_bV3)jza1 zscA=Zg>r{46lf6x2ty~-At(-^x_&VUrx`+i(?hDJpu4LFeXCZXEt6r(FE1}+YGxJ- z`5K1SuOlm0FgrWLm9AE+p{K78neJXxO_K`;ZH{Uy^I7K=2~3TRN^1rNwgeDc^Tmo9 zi6$Th@BK+FXi77mO@isQB z?Z?>Y9RhChwo_3QI*Qo?k;ouBKVKiU1Ime9wOle^k-*T}NCE|IR#ux4lgF%?vOMj=ZwdBVop zb!&0{+<6oo9~ZA&!S&0R+~Ug0#H|}Qs;_u1lxfR!MRa3(rCN*1nhr^l!!fv_eU?@- zVU=OfEnd;d${RNhV{mW~>2!)MABi+J_5qN~7qOTxW6i2Ans#q+*o5oA!Nb`4;1=vB z^4ms6U>HV)y7A4AeDq@;nxlKG2V(KW5QMb8Y+8~Ui31|+IU)FOLeB3AVZ-D+n5+f#o<(=qRsbF)=>Q#4O9g>ec-?c=Q<5h>mnb!^Sm3 z*fKI=Q*Ks%_Gf=41m5ZaJ)%lvo#BdE?Y10`k3gE+!n&YkQ{s~}36V5&gC!P?qMwp5 zl}sTPtB=RrM_?29Qn`fLg&ZojhqwmZx^a~oAOQZn6Q@q{airt>b>T4Fvtviw@#81n z@e@D$bA5mOr@W~V6rxQJq|+Hq)gtSubQ+Kp4j;BK0JMT<34u1RgJ!r1P^C$8*hGm) zM2~PWpf6+s`BDYTW`M4?BqncNLT+)9t#6tphKJYV%-M6~wKzOKK(1JVMeph9>Q)Hg zBZ*k-g-jy)>j2DfBP4<2zk|Xoy2`R0MQ)tqguugpyc$U@8h6cSnz=%W$l!IuAn-a= zMTG!(G$@rTC|bZO@{TSX9vdJ4$8kXb@#tfZVZ(+Eh^i{jIE7*nbJ;~yEvs(S41LEt z1Bg8UbTW!SNPtd?<1~48JFYLNfxO(#ttIw?@V+i+eLNfwB$9eZ4Nv1N+d|0>kZkKj zPg@k1o_~g{UI74({T;h@lI-RVtq?XDfFcCwXcCvNU*~ExHZifKVw!KDgkLQH86kmq zDid{mq#VzOBr741<_cPGn-8>d6##eQd_K?NQZJPO0uG;&>A;h9Bq9P&|IdG7a$=l4 zU8_>l>gmRzBS+v%5{ktFpPNP$i9|-ZV&dA>Yk2C3$B{3V^gxmiF6EX7@nQn8WLkH9 z->A26Ns^jySXhNPTb~mRXnJE-j*5F2f`M%Y_Q^77&ZbxN-SeT)y~1BdIMY zs)94;&SB%oX0B8Vi%XcCo<*fnMMPDh$>fE_EWY&Q;~1Zuz-)H0Z**)d#oN6wiD_U8 zsD-~YbVL)ZXaE$iDiSAbxl+O1+jkLQ*8-Bn8uQsKE?v8g>BS;?d%ECNR&eFw^KA8c)vB5`Y}>XC zZ+Y9>FhtFc`UU4qCY^#+t>X6RC}!v9@$^$qa&I|x`ZU(9S%a13Wo3L~BGSHnd$S6a ziuocXQVt^Ej>^Cr)@L084m}~Ur9+F;Z74GM@w!$GDDifT&>W<=CZ2xs|4=R!>jq*E zt?plqx4!)y7@^RK$KqsdmBTNYNb=C9NecIo;Y}m_nX7wykc>s^#lfZAsqo@%t zk%%8K&FT&}5JHPY5muc5XgQcdqxQomjpjZVAQhHVIfB9A5j=YCG!)Oo|NQ5_VtF~o zpH-=tNT<_y6M;W^^eEEF6gNR0C4}H|no4I#tGr9=?Cikw^b{h7fpJRwK*;knEdIvj z=bo8GNMLDU&hPE(UkBhs&2p4TG|tv-InxB1jjPcg=!P{fCJ6{h#mIx(@rK9Fz$z}{ z-~a8OQ7Gj3bIRok63GN!|E4$N=&_UN>geQ#m8W>WKR1Wlw{Bx8mtzurt9rSg#G(e` zjsCW&tZE<&jV~ z#J)XyaQ^gh%ud|JfBxsc^ASn?J*83|e3HPw5ogYvM-PqGK)_XJjuLk{x6H#{cUL!i zLN|0C2nAhJfnl%ZOC^Uk{eN`(>eYXnpPlhS0-^h>(P(TB#qq{^ZPp?N_k?f-jgV>F zH8=UL2T2GTiwAMy*deSL=*2S^p1?C-y1)rb#3=U5XzyspTi)>woIP_6?QI#}kxi4; zr|#c}tg1Bp^>OayayhO@)Y18D)Nx%VM&Rp-eRFDzYm8F?8UBK z+u&LiJVRCH%H@j`=OymOm2!niJV>ST_(NyVKeUDul}aG4-?@uQ&4v+8K$aCg3%Yvs zGK!Q7G;)wQh{fVWzJho>for#J+7~Zf{L_W|vp@f__q;3jQ$P8W2-k~agyL{zQjvq7 zN8t9y67Ejjh(l8pNRq>)QIF}ov+AOz6sPNa?mjL)^FO@pqtSuub(O-8>N)=)vhUyl z^siY5le{@KHHF!knV@V|MR#u>RwXh##MQ%fmOAfqPd&wB`mt|1jiKRT%-x>@kR>lI zl}7K~{j2Q!+z;;TN=)_*Z+zt+IFji&)S#xRM(nhr>faVIqPwX}tGaG*S5$}$IflI9 z^6{Tlt8oJJsBGCh3T)UgjI-y@WAFZh@BniCHC(=OxiU3Aal@)vciZU)uS6<6Y{X(o zva&>>ONPkR8x#iCtOflJ!Sm4V8#g@1s?L&E{%&S+;&<;{zjhCRLt>M7jYRYX(7HO( zD$J^*&=>4eH0`5`q7j)$heAa;oRh-MjMq4Y)-(+RLxU9K`>=Q4e)RVC`OEpdOEF!( z@brb5o7b*i%+B3^mOAWWdskOqD$_oaZg1a^Or?7vkZ~QyNM+g--H5^>7m~GCa*Nq< zDwmg+7PB|$`8)uVBu6qwvP@DVam1_q7lf3mh)t@d9;Y(4pRC_c1XA@rhX7K2QFTxf z>5S;=?eY2s2Hj*bWzm;a3d<`iQxoIa$?@@QjwU-w_)^1qK|?(HyugS?r9?8NCsL_+ zG#=L}1pVdQQX!vP&Y6{Rkql%tt= z5-EzRh)!OTc)wyhwnH-&g;kZ3xCS8n@(@5MwT4t1+9UCsf2BY2NhE4R v6cV|irN*^r2wDKNd})jLEfT)(>sbE}Q2^1qXl%QU00000NkvXXu0mjfydKu6 diff --git a/UI/forms/images/settings/general.svg b/UI/forms/images/settings/general.svg new file mode 100644 index 0000000..3521280 --- /dev/null +++ b/UI/forms/images/settings/general.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/settings/hotkeys.svg b/UI/forms/images/settings/hotkeys.svg new file mode 100644 index 0000000..9a406bb --- /dev/null +++ b/UI/forms/images/settings/hotkeys.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/settings/network-bluetooth.png b/UI/forms/images/settings/network-bluetooth.png deleted file mode 100644 index 764e766995287e6043905f45784408ec09fcc147..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2358 zcmV-63CZ?}P)4 zIU@m3aWuT;pyAt~9EP1jT|+=iYvdngebh#@TLYlN#Wcbu@;6)sVQyIb!EhzjX(GSF zLbn8<%WhKm7}Dr-x1rtXW4pGt_9*}=SZP<`4KRv6cRN-$wP8|Nds+c#do(F*=TzkG zAbaiFUgB~9svy#C&Ku~n{tDm$7l4WUskX2kec2aY0Ta0%JX~(P-ViK^-RBBA>2ZAT zNw`JOcb_ka(gr~^Rta^td$}0G6Rrjmc^Q0M39u1NfI*^NclA)rDjJ zS6;K(8*8Htp}wTD7Gf53hI%IfV!=edhJlU(G%E@{>ZHN~IIeIE(_yQyd9x>XG${x| zup{>YI81tl`X26drE}Jw@CP(G3UDKs;2)?|t*CA@+oj%t{Q%PMoVm=of<1bC_ixr7 z{r_8`&N!MApagy<*E$OD5&Q&JuA$;jP~Z!gL>{@9V1T*g5bdk@i2TwW+?+y z(c<_DcBoe%X=xFDVWpG$e5gZ!GSu~njo6dK39uDDgb$!n8DI{4N}4zoECW;kqVzbg zW5L(&Z)<)0@awBzKK_*W$f0s4Yw8)mx&j?<0F5HNMY@y$7Q_3bj5Av2q2q5d>_J_% zT+g2g$+sSmSHFBrzVBO$yyE$zCt_|nG?~+}v*8s6)A9x<3;9*DKp9{qyg@YT0VMV~ z_#(qyaK^tc2)wpeZg}*tyz+&ka@|9RYE*Sqh&xM72FN1oo zX;EdFK%-5UQ+cq@BeM0N%>RM7?UvDleD>G+jXSX>Cu!&6_hD~Qq zfF*Dn+zK(u0MisByH~v-Lm@q6fOM%)wt28x*|xT}hiYqUEn2PCl97?|An#cM^<8Eo z?$dArY=;}jL}dU^xSW#R45J<2iKW@-_{BD&RO_hR{Q2_-H#IdKEH5vY^YZd6DJdz( zA|oS5s2>dR#KyL0_71i2{NXC)#=wqsGI6c(Z}qZNBg*Sv4MeVNYA z&Js}+PZkyyveB0l5)v#3(DeVEZSO=j6=`ij_dHI!d2lIdaCDdphbtKFJfue1^8mw+ z#_Wi!EYsTBdO=N1jRkX`jlLy1I$B16d;U{T+vb7j@N#wxx2ZFPassS^^GJ}R0A65# zi%0nUk}ck}v@|nK9~mH8j}Au( zBFgG8qBe$l-op?(Ho#7j!+R$7h4rwTM5$KVan&%4d?O3Wo(Tk^Xu=wSY}I2IWiGuEXj4NLUGLNQ^7*g}k5> z){`#qcJg*2id~Ii;;wnMb-Lr2K5}MerX?vU$r2M2LjeFLYeY;>Lp7Uj+&a-Fw8B}W z1w37SwKyD>!a1Y@xDV{?MO8`jysoOMDsz5*zMP((ZovX(JJ5dugvKP^&&s;+YOx5G zk~uK^RA27SfMu|p{$B;$$449;F2~VPu36<@amz0#S>BDYdG8V8L z7Z=A3fSZ~;E2T)DotSG0ic38b8XoacczF0`@N;tjzQLN#Nm<3!x8kvp?fo(v{p93i z84H*jfC-I?J%R=OE4+!3zW@t6f9A}YBdPBG34jI%qO)3@Jio=lwRQ}PmplK#f=>Se zPhrHbLgyEUhK43hpFVvQM&Gkv1F#1D{r$(GBL%o=U5n0{K<4-fWv@f0&jk_NXe$0b zZR*sigB16K{tG}Myhe{6Jsd&&(3y0M>@v6%oc0#bv%NjNoNbKvwCufNwz&zZhKvR8haOF#D) zKoW32S%9bB43o+KXjN_GJD<9@J@nj;7@J#lDorp1%;XiP;|?_uNp;bYfTZ9mEDbDO zASL8INZ4?8$miub3iJw5|Edr&iHfC8^Y0d<8x*+l;OWGzkhygZF&}0EoFm zK?+L!T$&P`8ZUZ(H-Gcjd(-~#i{H!0OaBf`0shAac;@X0dHaorceZbP{N7G#EJbq} z_zFB9l!9w*>IStB=;yM85Cohn91?CqYv{kj(hF zo}#wGpsv!OJffqd!1f^TmRy?86+RrGM>^VN`0yxe zcP?l3rp;X}{o?NingN5)BAw5r=iUio+duW#-R)xE***ff0%ctlEUh((gmg5mG}w8P z5*){8a>~XqG=d?6T+YEMct|M-gbWIfPkzq96=93@Rf?C7^ziUA>*(o?KU|pEx{K)} zQ@{wIZY=`&+|wN`HMe!NMmck_kAoL_aCAj^RL512bHMgy4F!+V`b=IiHkM;%#vz+? zNhWP_#ib>qGdC={yrcNsD%MM z{l=<_XmEQ&jlsYE$03$)yo;*pN^B3}DM%NfDhOc{Obr0024|QaOQTTCl_&v9r2No= zbUM%IJd02uH{#rx3bCc{aETG=oBFf`P#}U0qA)*|)RS~~9f7i*z&%XRWH~mK@CnqrsN-0w) zH~@f-VHyZ4ID1nfH8n*b7{v-j>DaK1WMYu=Xp~$w%V7UC)-T(D3{{4FhyvEaPhvx> zvJ63Y?#4jFrJ+z6woL}#V9}s&=(E>nkE3D zp&3D>t^=SM0gQlRXy6*J|Hlj5e$RbWS4ELNXae#s7~v=lWl@9xUk^}TSxs^}iPSXa z%5@6FmapUXho3~*X{N_UIP$?RTszBM+qRSjAwU2+pe_79;JR76;89i$snnVb#UinPg7GJCTLmU zI@q}!>o=}rSw{oD3y$OXfVj2$fNNjN+AbC4Y#JWPI4|t7c>Ey zytyaCg0iaG>up%f%JV!lO`~(&dTOhK__ojF@DQ7~tR@oNL}f%$7KAesY5Z)`20TD5 z96&kU6G@v@^;M<6wx%1*%JY4cFIcv5Gj+{N$mIY^00?<=SakueurSOI+b{v}eV@we zI%4f@bgf*Dp+IPgvEdQk=W>^SmP+nO@YgZSo9j%xO0y-EH6ySN_++e!^l}j23MJth-QEDd;Dx;#Nj>cF6 z6%m7=4i#YyL)W?7GswZi=lQq)+>P*E`ueB99i9NvfW7btj2=2X6pqD{`Ie@xjz%u_ zjv|DhsyfU=Puxu~62|etalw?}`QRz=6{tCHU(r}4$yKYkbmA;1sI9N3y*);I+fqy& ze5Zh6m`G`0SYccr)~{|wxp48~ut)a75HJbk7aoBNe?3Kd?4?uPQ*7VbN$Er`di(bE zG}ng->JZY3Hg0zKLz@sO`TBYt+S(easII2Dy@hQ%H?eMATd9qp1_M_|kiJ7(b8YEy z#R`Pjaa$W7?7l*N`0NCk-mAbUkX?8Lz$4xL_xnyZef7PYmxh*i)H9QHS-+|t*QeCE ztAXi(r>@%v1t>N583+ zyweLadwPI=U>GRe3IH7Ym6L9$Gf_>(^=}5M95x$Z1G9}bJ9m~ z6M+Dv5a^nQ05k!*28M)&+7OM^O$2n%C3G}~DGP=9>0dj{#N~ryOzl4nbOYmnx-|gE zGw{l9oCwrDaL?w3)_c7&xwElGzn770)Q^Sury3fE3Gqi zC=0?X?_A@}H%~i^zi<+`0Q3NvkMc^X=zskRBOm<8@Bh=uf^KR&dv7b2RP%G+gvj-2 z*tprV+e_2gdA(>8f;t4W(mHCQaIQPW@BPUU82sn6q`OZ5CxH>b`>3^10hF`#r+>V> zecN~az*lXQJJ(l{v%z*}D-+-WYW5reP&cQL5Fo|u&8}p00lEZBhfolXUr6yYzqXHo z!++b&&_BKd6wT~=fciLfAqgyD(~te65!?24-~0t@wf}cdBbtO+d12Y{m42lNBZV%A1=Uqw+b;^Q7U+b5m% ziPh>-~#q}D^O9asN}!9@=f0S_d!G$8bTn??*eF-F*CO;VImaMlgX5Ok!B+}h`dFHaMTU`)2|n20 zucrIE({5UXpgU5gr;NI#74xNuD z4@d$jAdc@|RQ6nDtH1iIE!(!MGntIWd{OX) zzt&9XUl`rBDRw+gd2fU@sVuSN6zl4SqN%pZ<{?p4yJ7VTS4e#xfGn!me(JT?UP}W0 zq5~8Pg?kzr8vnhrvPOEItLAN&LGB!SU2fWx4)E}5DCpEkL5-!;dHmJrZv}(j@oMbL zzR3{20yTH=WHRZ{`SWLg5BOyu0oaQQkj-WScipuu{P^Qflgnk9r*H`I7o#}h5Jt_{ zL!k`t!O-!_dRW=yYAe3;yMmxV`P?YWvN&?&Fb59oTLY{G4lX)?Qc6xwXXx)A#kTXz zl^?1gGMc5aq5z3^As+w~7!pz=B*!b69!Ma>d;r5RnV3jXjB*W73z&fw^)6WwgX6X$*RhItMv@766e?GBs*(@xmp9@L}%dP}dn89K7i%e9-}fknZKn z=duF>J%;Cbf;q|?7CSF=%Pmb(BI6)6KwYnRW$Ei1^WT2=KYS?^Vh(^1qG)vA_dP(B zyx9M05R1j4wry|C<#KlvJF1`CiQ_o#ZKkpG*J`tm)T}bfTo(nPX^6}<*Za9E#<>@4 z@2qKAX#p-Uw-f2Q?nE(-_5p{1sl~n%Yd{512Lzcf1?a$1U>Q&g$Q!mz1HHfiP?!&Z z0309zOf2@p+(PqH0^!+m_#6Nmm;_RQ$7h#X=$HEc0*xKw{{7BlTL1t607*qoM6N<$ Eg6qg8m;e9( diff --git a/UI/forms/images/settings/output.svg b/UI/forms/images/settings/output.svg new file mode 100644 index 0000000..455c202 --- /dev/null +++ b/UI/forms/images/settings/output.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png b/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png deleted file mode 100644 index 2abb1b5d8396ce8ad5c78ab62f063823f347f1b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3523 zcmV;!4LtIRP)U18AR=h3C4arf z%&^u1O!&|J-wefy0Qdj`0Q#2k7=Q&30f-$re#h-^2Z&{SrIhq{oBk#Ly07_0sWa%= zw}0oiAWc)s0Am2gA;0lJ@#nEJBPF70V5fH+Ql;P@bk5E>c836zdgIX^=R1njvQqkd zhF_)jKHJf2V+)Z5j?Fz)<;?@E8- z%GCp7#j!8S#y_RC@q3+%vGkwD&1Uml7=}N=%ufMuHv@3a?kg6Dzu?XHzxiWNo}AHH z0N_{8bzT4%Qg+TEiX*UdQeIkGI(7cy%chu4{+TWBe8l53@A0+|9(gA++5^K$!@S=x zZQ`ZHe?9o(!hbohJznt(=7VQv=RW+zn@*@K0734xBuQ}P%EIZ37caPWyS)HlYwt2m zluD%sj~zd*CnqNnMj;$K5C}vHfJ0&p{v!|5G3~030{@w#jfRvl@Hm>iygi3n}j#DTy5a*Q-tqnSz z4y<+1N&{A*LYGDo)td33O5K4qd?+?xI4Mg2D8gh(5p|Psj1eOE8NN3zz>M4%$t5Wz zzOrNS-Q2RLnLsecN8mG<9qLgP&#nDsmTzHl_y~McgtZA$*G&bBmf)$ah!6ul+rNTNR3uTRw-W+MxXTuK)rv8WWS*k*09=`$K=pqR9eee z*m@2HuZ-bf9G(tjHH`AX7SQmdE)G-pC^=#sF*#>Bk>5&szc^h`qcG&lytnN(ET>?0 zEZ$4dPA10~Pkh$eh(7=$O$D1JzI^~7$ZZlQ5j^c9Oqz0VwHm`(?FtAQZ*%lRaCGbx z6ck8l*wo7HW0a8&c6WBMK~Z?!J-r4r=31_bjpPzW^+6C6QjX*dFc_8O3fis;@s7v> z;513l3ES|!00h9fxQ7SAsDm_3ZodM~xzt&gwNKG%H)XI|a|dY>BS~X);uZ`Amw$*x@8>`ojPP=J>vT$t0aRtQV*$m&PHx??D z>MuO|%yX5#01h8H=>G8^{^>s-dFqlJqgvYG}oVc>Y3%Q`P#3t z|Msu{{z?GT2kxPd`-G2qGzbb`P%f7rGTQh}p7tui*)hXGus=97O`e~g7=4@LLzv2Z zPm)%v`BJ^!_{rb+_1}8tlRo+5-)3}l^b?1NM^1a5C;iUw99d5N)Fp*9O04ZF{oA065EaoJe!_m=VV zvl%}9j_>-``QQ2N-{0!K`0H!y;(Ts>Tqv7);X)IW6NsZ21y?{f5GE_vTHqB>EEZ87 z8p6J;K3iVByu5rWJJ_>X+tbI69n%vTj|s!h9v;K;FnOMjDDL7H=XQ5@GJNTQ!SG9S zwOVcR{JHa6xQ(g3T!@qsE+QQfdW20&=SF!j+5?gNE{}`w97Tqi3t8Edw*BY2hgUtk zBm>~};63M@!E2bg4Z{~l;Q_v1fM)`@G=*;p2uu-y-(86S+UP!N%-m~h41CXrxYKh` z0@Xb{^XtV6ZlC~I4iS2dAszcBkk`}qidia1oiQGiCUBoal4Qk%20&;)zM7Kvpm&8I za0QU>MIlq3@r9XcXAtxPkUC@bIDd2R=56J7$;BZh_40_ZLvDlObIhOt2-j$LTCz=? zb3)XdbwYu`1nVqPn|Z!A=QNOxxUK3#~Crp`Wj?Ryuz+Q*HQ%t>DteZXj-81UW)4~TPKLR4g;0}P0{DDA|%&8t_p zJ1_lqyT(twYx)hxGm7zXna*?CXycJmZm%knCP|X5;`xm4{P<7*)XzQr`CsrkPiB1T zR{TJ^kmsDeuT&WNghx)i)4M%%>|K?w1Mr=cIYtk`a{VP~l<7SGe(bp`|H5N5xjSly zKlb$810T`Tc?8F|*LjjJ{jx9G&gjVJ&CQ+q=zjcQx{&YK9Vr(_XJ!sRs1A%DK{dXC zlsh1(e5dt^T6sbK*uqmKOnFCeKR=`{E!5Q+UUEVz){dLpp(s{sq*Se6z>|miubQ~}{Dd$N+3$Tk3=Q)8z zoaM0u2m&0^e0DgW)H~=rIbL$dd^fs~w6y?4Y+8@cV?-Uy^#%}R2KvwNO zvfGf^KicHi83eeN*A&WbewZZGp*uG>(6?QD-3G+ndqb`gugHvLT3ioc|zx$ zjpOL`>q4XuBT~zaX5;G8%JsvC_%M-XfC+;egB@XYb%nDlanacF^O{`6or4CC=OOFnK@7IM)fFy3JOU>=@4=$t^XlZrns8QNuVge{Xxvz>w zYQ=L%?yOB3&E`_Ok^CXq&Hvuqu2k5b<4mlY5uFF%*U))>>FKAQzVzhVJo!u3+91Q9 z%<+TjLaaS(wzlx6o!!p-wrL+FLW$(+AwvWWK=l&6azNo^DVbAdB<#@Pl$MlIXNuG9 z_22o?KiqiRcX<3-Mh&Kwd|dZh-=gz8o#{N^`Q6`|eB;-D+kZUyHh04J>q0kwe$Vgq z(Ai+Lw&9V2uYD+Gh?LI1v-5M@cV&T{NFmW4Kr-hyH<9FY%Qr1pf`D@Xr+~A7_6f5eV!F9_M#JMVQ|vg*oGb^InVI>RY01el95XX5Gw%kLAf2)B}qrT|Zxo=cmU0Gu|=xJ0vYQ1AKp3UkViBzFI zmn(GnB)zk<^V^00l>rY?O8AKYkv;Or?>Lq9(Muy?-5Lh^Tv>ChQ}Z{&2yJDOKN^F}-$XH0qNa z4s`BMBU>QRwgb~)Li|8HkDdDlkk45l;ox+g5!`09I*eJ)YTIJB#Jg^P`#)u%lrV`b zA)Ro_&t{ZY1iw3bX5C`dqrl=2FbSxt2*X3yc6TI^9x^bHEMVK7L6A;Yyer&x4q&;M z&SN$+ZyIR1W7ogaK#0%@d(E0P>JNX`_U(xiDnC_S9hPlKAYcIy=NvE@heI^hM3Bmw zc;dBvu&slmF2o)p+_7xefKBI)P3s-KdvCjg)A6qwP~6q2t6LE`{*;N&{w?Z~s=$#Y zLWU#Bu<6{E2u@}=hv{$(6)Ysu2BtMe@%rXoWV6PwXh#TlMbqk-u6Tlec31W=(&m-> z_c!PN?PwrO<^6-*qwy5}2Un5TE`Y3}-`4V0Cwh%7j|cEiON%pQ}-St9C8Rl&Rpn8!ma zor{dfusB5YyAnE5Ib*m)xNT#}gfa}K=z2znUy&e@u^io#%NB_za>%3#u6V0x#1JO7 z=XK+V_@3s>KWspfR>(&zDt-CF^Jgs$MEywX4v8)b=i1-!g8Pc+wj_(GlhtkH3N{)- zDiqE{xmYA7nK95*>O*^97TenrF!B~W;P7w`mDY%+LQ_z@->|TI&j3=CF*g&>c5Ts5 z=e(I=ilyV?8YvR+NT{y}Vc)(%45ji*I=6g% z^~YvFqT{e*zVqGjld53y;)pZ}F3W%-LsDgoE)T#*M%wZY(isO9V@R0{kR(Vlp~wQ6 z@TeS1LcKV0Vl_%iN}#GL-6I14PMcSQSGOkdvu9hu85e-yym+27EbM5BqpB=`Sk(7j zNy2Q-aY)4F5z()^^}PA>8)uRp5MoqX5&?nU=BISU1fR7Q7TI}G6b z;6MNpaHPB!OJ_Bqth^k4zaKs#7zjYqeCUrSk?7c3G^;8CNu_fa@iWm&C7CzunI~NE z+p9k^1Cn&UGGqSy`=X^m7$k^^5eYC7Gp;Uzq{XqmJ1ZKOc~m&e5gF1QHXK1wlCF=i z8OPkwWe5dBB-jhDR|8`Vzx&g}*tmTUTCySJv_^O|eo)pFaD|g_2ARp=1b1YlvnEto zx_Luu>XF$^CFu{$KzZHdh2zFojy4>QVg@+8nu@XY5p3H(gf^;k!)DM;N6h&Gktb~W znc41E!QtS%xOE7pI#5+r1&`Mw#A})cNs_VRTR*~6FRezQdn<-|cEGlBpv!Lg5wueI z(R!d1E2$S~Ytr=7AFVmzLraLFccw4s6C%k3MNUI$)Q7eEl2nh*0r5nE&~+PzVT-3M zCz#_5n{Z4MqtrZXx~3=!WD+Auz|hbT6fFpExB`Y&hKXYvpm@AOv_KM~>HN_3(sO2t zb=$7~WdQsiSVD?dKJ(p8`*B3bhsLHbd_E8M^$ek4F+g%eKQJzVF~G4oc<~Pqa07s1 z6Ow2rH-ym?wOlp_UP*==GHfvF0Syhamq3m-K=qYi`pgEbrbcEKgCsLRn2?IA%Ys?8 zs3;4HOmy`Pod_UC6b{KiW!-JFWsllGy3k77Vvw-y?MbmYGpu67jRU149#DsF%-qeI zfFV@qM@f|WCYeMbUw~m6&=|@0ZFDKd#tNwL_7! zixU@)qlVAHopKFdX?|(ih!SEFn<3N9UW)qorZPP9S_|T7UF`3vv`Dmrylx8v4hbF> zYl#wa5hX;cjcO+@N-7qM!EtPurb#7NK%r2;l+o2Vwqy^YN*-fFIh-~&jU^3PsJsA3 z;$Lwk!E>fm0f5+=ORll@p2XA<21tA@r3lj|Rbt)tK5zhoWMErI3Z+4>5N?`wv02?H zACdw0npkxb8G{&=4N79>pouUATJL7%-21@2G>Iw^3}{%jdJQN8qLjknFan_vdU|@$-QA6St)0jvd%zqE02ewL=Xm+; zc2M0TnKl3kVk%NKRE;n|-CxtxSc=xJ)WK5;U`*U3#5=>KGeWd`4UT1DtS5;vW5&X= zE#&igD5`|Ix*GiHPk$26ie#$T7mBP11MQui*xTL<)f0d$DFByTO_J!lm)>kcM^8pX zzp6ZluI@~9gaJr$h{{B`cEt}roVqCsHBrqi5jIk|yKc_H~V=->*IGl6VSy;bz z9iDymY0Q~D7qxY@NTt*0B*EQ1-Dugj4;wdc!h_Ge0o_}L$>T?p3>Dm+Xc4z`Et?@u zWdwi*gAcSV*G7!dl8A30n>PRsz!eWhVu?M}2aD&{p=Wm-Ow{4{WgX&8BjlvkQ`ooV-w_XJ%XV|R9BXx zrGG&DeOUqmJ{8F%15lJzTNy&{Kn{@6H^M+|Z8+K1mxZKx-O_P0;4nD0BgDetWWa4Q z4iZlW6iJ93BX{GI$BxIY-Mi4-d@E+pnvI5rMl4*o5ZP>&$l=Y_wdn2}z^;}q5}$`m zCF3t`M6`Z3v`7skNdXua2C7Q~7#PeJgGnf|jOvOI)^6+qxYRwu066ak$3fzYFXA%6 ziS@e%FuSP~>spiU{Rc8IMca>0o-`Bhyz>qoe)J)v5=oSmmBTO$tlhL7>$dDd&tMvg zM}wqlP`ojO>zbg2skzI?!W*t4@fvJLq#q%_7ZM z<=61~`rYsZq9|z`4J}$tqAQ^KB9LS+;eo9904|Fl^(P9bi1<+!(XeG#|8O?uPOHMs zeS@$qx11z&jx3>?vR89Pzoq9b5B)M6ibF;uytlm{^Tx*T?w&X#a2!*fz*VO$z=IDy zh&8XT#^AsJjy`H3CQO)sS6+J??@-n%rY%LFbQDxA3|ZBLXbu_R7~VID=|*DX=>ir^ zs=#Y&JBN3+3#M12qbrSMx*&cgEZH_!kC;La{_4DeNWlBH8+~_d5_S>pPJ)O}g$E8! z9F@W~XB>{-{N~qK^V%x(_w{4yl<7F;m}Btn`ptN8Z9B@wA3^m!6aH8OJi$^?^N>`4 z0VLcN&xU)grqmD1c96?kBJvC8)YEuPVt;Q&{Ok^xRr}U{E&YL;Im5P}2nE!I1@TJ> zn@~jbhhI=$Ti1>&k5I7a@CEqZ_rHhj+qR;AZ~&tlnsDy9=i;6B*W>JM9?C>P7M3fB=r|b3WWa$Ih>I zckR#ork0{1OESRV*1rP{1$_3Dax9!b3CmYBqqVIS{WP=vS^%H>+~=@!_g?(wiMJ4_ zn}uM-1juUOK=~cK*>laf;!#UwGpQklM5=(}7c^iumCV|$y>PSS>Xt<3a^Sa}uYPg= zN4^tFI&W@kPvskH!zhmhMe2e|4mVvo1~VqrGwNwPGA+^CsTe+>Ore3>kC9Gof3&b8zsytoWDz zU7uAN=H7po>i6ALJt~H#8U;DAI12wQw^xp(iH zPu;{h_{V+zKF8g zNnlPrWF-jhK=jT*4p0)-C=g+U177&O3Px3iu=4#5>}pF2p(I|QDGKsA6C4sGx(~7p z*o?%p_E0QzpAi4|WI&?h(rcP;`0R~m{&=9jTl&$De<*&3GvKw05MrO>MQ)XVA*)2 zvpU+Tq}HtMf@!nitVptGO!q^U0+9Gv#D}4L@m<@|O(3ruM!~r8;%Bb?{jm6tGC<;qoCV(f6pMQqJ9K5S^VdQ#Q?Z{!6jn&g#rlrJg~`NfeiD!i6FBE zbjHs;8IcK&_fUH`PR3-_4=eqlg8a}!wrX%a7~sKEMLBy zp1Ru-ci;QK56i}1Hg~{Eo~%_cDG&=G5cVTW0^B@`2m?T3Fb|Ck&6!$5*MQyKLrCRp z2>I>ePn&sbteH2Pbi+RIw3G?S zH310@m(Wdh-JR4isyv9siU7((Uc>?(L^TCLiIZ>(B{F|c^YE+Ub5i%O*IHA{7cb`c zI}ercc|4w(mSxEl`HuJAdv7OzzECLCS5{UwZSUT_`I~OK>D%;4@L2$z=;=}R^n9sJ z(;_#NOgw*)KRV`gZ>a1jH5e$B0~!>c7qVAzjTD=kanOD~UVKS1ZLop~yO2+sL&>+S z?7nAGZI7>VQuL0N&-(xv((>ij-~GRoM9H*i)6Txp=Z#vIeNy789z;> zQX19%_gY$79t5zD@DO35gouGwMNvw)>MM;@%$@41oHE_xFD+NS!H}$l{Hhl8i6^O* zHykrpaEx5m(G!EY#Ez}Gz70DZtB`8yhET znx@m?OC%EZ&wlo^A8*{a@ecrY5DpO{qOO5bxF*CZ%i_8WM7ShJsCNR8B~3ilI5(NY zauRp4be>{xMKYFfMZ4=T8Pe|jy%~^PAxE5i^2yhpbka#j^!N7A9JJeY2u9e1rS002ov JPDHLkV1f)5S^WS2 diff --git a/UI/forms/images/settings/stream.svg b/UI/forms/images/settings/stream.svg new file mode 100644 index 0000000..6474c06 --- /dev/null +++ b/UI/forms/images/settings/stream.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/settings/system-settings-3.png b/UI/forms/images/settings/system-settings-3.png deleted file mode 100644 index afc55ffd6d7edd18e692b63d0958adae81ca2215..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4173 zcmV-T5VG%yP)buG5n&Z`{jU=X~cD2>$=Qdv85KiNvz0 zsfk~G|NEc&B9Im=uHFdp@7z|eUaiRXyyoU*Z@KyA?QOAG%oSmTLSZEw4&xzjE-`|4 z-rE-;{(jKg7Xto%05R{MQ*j&@0Dn-?G=uMc_s7-pT1N~+KLR8cY{0V2cw5`5_dekX z&u@IhBW|R&w&Gu$I@`t*CZZg?Xqwg)(ZMhbPp}uo1ajWQwp~Ca7Av8lp`od)tZG{# zG1(6!1$RLP3WZ`ZolaerH)>3$bDjbI{O4}^`!9+R1+8e}*bbVe`B6tM_|X&S306d~ zXG+Dkz$Jhw5s8MO5Q-$(b6XcXckZFGvNBj+UezuJDuIAtEy%#==%`a$TR$i!uFvN+ z0H&ue^P1PZAJg5760U%NimL=6D5!!Qqm)VvN({^&1E}i!F$%aIfD?degNDWt_UwV` z>MEtGO5Sjgav;251AuGW&ZS&#j(lDRpslTms;XV`zT0r?!Vn40Qxckw;Q)lZs6x;) zpnxLGCp_4KWrHa=4)lZ>8M%yM6sW7Kme;PU0!n=Y1+Lgsu3?jW?at*gZm~FTpsA^W z*5)X^rbKT?oDYYrDp5yG4Xn zOMLJxyII_}mVDkKnaoP_m?e>%Ba=zD11-X|V5SiFdTGU$Fd!c9zV#v_xyHfVq$V~!8HJQ zFMKqg&xrvO*M+*;N-5+ROr=1#AgJP}kS{!Zwb*#vW8Mr3B(l((q(0Hl;>H?u%_1b` z;?mkFy{HnpT{Wd>oaQT2Uxy((20TSXK@(W4?e58&U-*m=Mm*7jekHiK&3=;es-s zcFW@DdCT4~x8C$n4jeqn-*tC;rZMy^RaNDzT-jA}x&(pi1u#%)8= zQrCEC1kl-51JU96lqpgD;+a}Yu8y&5&Ji^ONm5+V$r~Lp{2Q=j+PK84!+>4mKA|WfN&%zF+!7hfj2Dg<#kWo z#Y{ryOW*!F-#%|MSpR5fT!lA-mg6#()CmTlva&qT($c;Rs1QO6odMr~bupcuv!wkFIqsoY_}^V2yu2m^Ts(sRY9zRMn-U(9MfCSnRoVKHq=%hd*)HT28)g2ccjL zQ!~JJAyC55R2IiE@m3M_^-YaHwGb8b1sl-yVsB11nF;AcCM6YdRxYcCZf8Ev1A;^~ zE?PDNx(Mx%19% z;3_6h-Q7iVeVnG6I8~*!vS@1%zflx5h-o`mwu|F}s)8-Y^5G^LiIhm&&V>O*a2I6Y zt#AF}j#w=42pg9pzxy4>Kk(KgkQu12 zuYBd2HA@3?ISnNgrqR@S);GV&`LBJI!tJ-Sh>hI(m`Bl6-^TC$csm1wXL#^~_p)x? zMnJ(ZOcAM(&udIg%{iw|_1fd(6UO-XW$$BaVQwy!o1LAUo17e-xqNw0KE@iuz!@PW zxC=7y`qzJY%c@mNAJW`X&&aV$p^{wyA}Zy$$#{8Jh_y7=B*-)DHR zSC-seOit?7>C?X{j*JZDrl%*<>2%T~|uq%^G{O9nIJx^5ou$O#!CDNGA9LVCe7 z5Q&5zzpQg{I2;KxeD57Nzxfe+UjAb8m&eGDkFk93ZOr`W$NcUi@1SJURywa+DdNZY z(T~0{c;Uk71Hgzd>x;_?d7pxhE*L(#p!xfbz=91_H`H#J8|>%M7e7z!xf9&{+-Fk1 zbvx-Zrzwn#P|W9OtF0x`+rvb2GnGr1g8SEgUZp6?B^lwr0Ap7OHT`J&xcm#-f(*QJ z%g(yQ-M?$Q>ofb=u<|;#fBfSRjgju@p)fo|er%L%e?P@kf{sj@{DJ+Xq{FP+d7IZw zYRATgA0(gXehZ8X7T2&{vw@dYRO||#+W(dH_VTuy-}Mfv*Q_NoG(_P-4@FOSZ!g)? zrzuX)pqmZ>8)BA)9*@)B(LvjiHjW+Z-lOZrALizglOO;1cPvFwmr6fg6AFdas4A_g zRO(E3_dP$p{q|p8_GiXj@OPuHtE`L{HT?rEvEWM|^`hs8mOteg7$Pip@jOK_pj{rH z7k~5!iEN(!%38AF5|mP zLSZVZYKWIr;J6C5qvBmd0n7HWkn@5I#iB)OatKAA1v5`?-++GX*rBif{`Wuph^7^% z0rOup@TN#fnWpPs+0a`1lt+L1Q&irv7j0w^eXw7|52Hz|)J8?TjJzQQJE@u6yQqz- zC%%{`J?|MTZmC1lZCq5;peGniQ?Ta6ddHS9zb|+Ou81CSFzju~Y0=8ZUxee>+>)cfzc>sN092LF+P*9U?f0n_-wqaVFz^R|zF8Yfmk z@zhaF8Fh1f6x|znF;W!a&g3LhrbBo0b(BBiDXhPBFI8owNQWUYJ53@nOGQOF)pga> zE~+CC4B*p6 zj8Ldf3_6sSWod3LBOZ^40TpSnl$3-ych2izBPP%)lwM^Z=r|vL)ZT6D*MID@Sm`uY z|3z#`8FPF@#0w+dER3Pfr5Gm6pVYYa+tl2l+`c=;rwiK3<=3t3+_7!zX{lJlA;Ud%W}jgw9Z zf3{0Yg-~hBG|SKxSkoWVR+FW z*}(ljKSd(*KQ$SKh1YtAhetVcriYU!&zJ`fc2D&6o$r^GX^&{`0X!2uLHsQa&$(&W zc90h`dro4@s9TaQ9$xXfGgBD4$q^^YueHF`y_sZp+P01W_X+2OVm_ZefByXOLp?nw zmYh0uc*mW0cHGj{wYhW4mQC^X>sQmzP>*Q$#CxLsF5|8Ji%d+Un4Fx(qqnzDKX>kY zZg6lQA>U3<&&*7W$ZL-PL&AVCC`<~4`)AHRJo1-gY^AtSg~EKf>>I$^PVm$R~?WA#Jj%iv9W_wK6Muwk|0 zrBgEbSHbS3OTFgBi+zPuDw$6vXXa*RCMOb!$uV8mru@}vLYVOdPYEfZAUIdu2VShG zA9+Z%`o@Ru?82;R#26mHnHZ;-E6}3{xV;e6|6I_{p4GHNz!AZV!~fhZ9#Dm-P$@J> zy0kA{x_nPd%hIi;X#|qVL?$VfoyjDpWEPUH>!g8K^R zN!XQ5cf?{xfJA+gy8c<87V$6OJY$TXyLv75QaC>bupn8~YPH?Hd-vXoqUgkGwemOC zwfaj@nmNJVgc=7AF7@{kdRxnXTK`L~!zoyiw)?06RM-o7D6b@EPt`V(9r zCx3n!RB`_L4G=cv;YeAeOSnE&TUA(!(Q7cbdJNYJTzk4-Ao$c;CwarnO+CC17t zklYT*19rab2gH>|pv0mH5a9My0uKZ6$Ax&U1SLYnw_5?HkaH3!l>$b?Oqol=KLfGmPP%BNPzWGoo`6=s8j}II&?SH~U&*@tU4qwh0s)*Wjqm3| z>^*Fb%@Z9cq?ZnyZzf6toNFdQ3F7)JL=%6djn&f536->r&o&as?w3N-I|h+qtpH*r zfKe$FC;_Jq5u26KUjuh^mrDHAH@p-PxV&u4ElUuQ|A9gxr$oyug_3|LNeGcg|JqF#8^yGLY9Rd2UT$@NngCq+TPFdxPFBL&5*2n zAbxh{DYn?eeh^(=WU$nr9JVqj4i1~T5mUx#R+@2JvQ8vIvd;DIu3jReIKIg2$IR{R zE%H39wUbdOUQ!HHlBxvoLj@lDhO;+n_V}`I7TO4SuL7(uxUBhv>7QYnF?tnZ58U0| z;r{;K98cwhp>UTDz(Lmi*nAv#?1{0$fdI|Fj})#G4t}E#RzwQfIY~%_!v!I2I6JpCXdM*K@=us&pIHL*!=1F8-@{=a=~lYS(0}zG63kh&SviJ ze8#z5tcj5rd5(R$5DR6VdQK~W71XLm!fpGUSBgW%UI0)^U49>s`xn{uJXC8#nQU+w zWXY`s7(h9enOYiy`?4>SiH>7$u16*!N?#VM!25EMqv z*mNIzibP5P4NZU_kJCOl4So*@a2=7IGBofWt8>}GBPvYLd3x6O6=@A1Org8uv{t~T z(UA^1Hff z0lc9w`MNi>3TgS0oFutg0e@l>>p({z=BO$|40Q>tL9^!Ec5qBVVdSieBz?fxJM?p) z&|v_rIfbk_{d1r;pb6w%>K{59)3b-I64BzRIC+uH#EUI7P7pg8z#iru6T zL17#S{LEuT@+!zVDiVnWJn(h^UIowk$%3d2SQ}LzULTmYqtQ5lz?Nl;Rvak407wr; zWzXFXC={PYp!Oa9`tB<b&~vLSO58zTU4^Z2jz1@$lzE3_>olG7m}b-o3*& z-+Y7a{DRw?pKyPBy8$sU+2oNqqfW*doW2`Pvv0CBO>6dY$m_zmn#Q-V$7E@;JlQag zIKMpO-`cDJM$RyZo^9K<6{m=+sBTyzY*aUD(J29h!9}I;Df8HYw7y+>tiu6WeHv13N^@>b-s2#0^qqvDKQHH zb~_$m!X^(7j~uD`8X?yBC+pPR-8~kfaT{zMMdqvWe$-VKl=`YgM)Na*7j>r=iiI* zJ2!9MY`S;vUatTO3k$QVt*tc#hT#OZZ{KdUckf<;b~t|gcnrYi!FqA{9*(OJ17PW} zw6FSP$A=`6;5!BG(}Ks=uh25At;U?v%F4?3TefT|#k}Ft($ewR*jURDfY=;8dNe63 zD{CcsRdaE86s+z|D~iS8qmzI|_?uXn9Ul;&%h2uQFMK-;P>C+gy)R$BoL{qM%@2l) zB?#D}hgOoAnYkZ8@^G-kkLr_5Z(?oLZ#mB99E%2OY{(dO6t4E+^XJc7mn>QGl-%_J zrFX`G#oUsUlQ$wj3t??*$dtV?Ry1hgsRZDqEF!9QTx>xw4^6xcnDY@X*lAV*IK&=- z=;Y($V-3(#)+s6S`&1%vF_mccw8j|@JWFr4(AU)n3OBpnz`($GxR4*LWGj9TVDc_a zxG9x$R8>l`O1|m2a#rflCRkah;iS(ecMo1SN9z*(t}K62dlZmWgPG>l|AjY ztooaF&L;_`%G%9OsHSyyJbprtp{L%7&Yf(Kx#>>;w(*>U$xBw|~sI#-PuerIo9hs&J z?$QYkUAUTE8fz2uhz9t$#cpI%tQEB(DXM%{#?(&gsKzx$mCxgfn*UC0nf(CPOfmt6 WF#1msEs|yc0000 diff --git a/UI/forms/images/sound.ico b/UI/forms/images/sound.ico deleted file mode 100644 index c7b86de5d40495ec25c4ff2a394f70f8cd7b7441..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmb7?ZAep57{_mK1QjL-`&I~js4u}RDCkQeSTF+%vA&2nYcxv*CB;^$n>k-{>Rip2 zyu;Zxw|RA&Zo8?NG6RRErlwhGENdS^%%LM{r^aGk^1Fs57{>mEkh$l8kzKp-L(9ZG|J+CoPSMx3V)A?Ti_! zOY-J7!EZ+mS79fkPs~hDoi8rThYni{Xvzvc^L#==LK*t8e7wr;LVu6BL7j5#O46r1 zSq`X5i@<4TKvSCkh1UT9E0IVlFpeQPu~@vj`H@CzOK9`-<+)x_=D-`fpjzIZ+WmWHZdYxwa|XIJXg#UZSHxzpu<@sUAz z_ht(G?`NUMZs7P;_jixKKT;Q-=b+#3Kg_c10ViY9wlqKHYM&S#f{(r#c;>eLCORDC z|HY8AM?^#vU>ru0Ylo}NY#@C%(BBKbnQ7>1)dzSD@_pDdDvY`Q;qz;)v(4-z=OE{2 zt-7E5x=17{L4Upec0=95e~X(891h3cm!pG27P@+TBV^??MJ?m> Y9;c|jC{#smJPU9wh?-^85{9sU0Jfi;_W%F@ diff --git a/UI/forms/images/sound_muted.ico b/UI/forms/images/sound_muted.ico deleted file mode 100644 index e68529b68ae162fdf051cf9256e642856d7c0d5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmb7BYe>nU6~YbFE^6sYBaCWRXcvRBqAa^e8EI1MVr5#H z*Ujm-P3M=HIp-x?UYpiR4Wk_NBg#glPWJR_{TNh(-k0Y+=Y7s|4(D(vY67{owiJ2i zQ1O!~icL|JCjp@d^NS~j;*r?@96Ilw@c7X@!}4X~A9i*PhM6-P4fgg*m5WQMz{yE% zv6`YFcl+Aa*N0qP+p%hu77-B|eNJRVS<*Vy%}pn9cGiN+)phv!^^e%um8*n8;k2SVqTQM4DSJh!$@@2M z2>o<0VGq`BEY{sk2_Nph<6IqcWr( zJMitu{#XR~dgH{Qc%+<4LRxAH_U)}kc-S*++1!HQpk{=w>rnXnYpllRoj0#C+`&OR z;@}`xDWwmF>B4M8hOI_n&NbY=A;N>ZS-6}bL-NTUtPj^>^Cq<^B&6Aq$E&mU@nOQ< z-RnNMxU?AE+@ylRPWABT_pgu;9}8Wp8fB6~Fy%DrYN~N5y%Fcm^&v8%4Ss(0dT;M1 z`V}i0MgjwyjSCl+qoP4X${C6X4KZbjE)0|2N2`=_ylB(l?VFbvc-My- zS^luup8s$GxDE^q%x4&8c7?Psv!o~wV?AiwpW{P+FB&Q(#y^rQ5X!RR{BpVkPwQ*Z z->bu;(%c^wQVpX}kLAnqZ-25N{bxunmrw6% o*E|>JTz&B`k!b=&jgnw9fy!LOGW!q>6-ZHM2!;qof5SxVPih00d;kCd diff --git a/UI/forms/images/trash.svg b/UI/forms/images/trash.svg new file mode 100644 index 0000000..fdc9bc2 --- /dev/null +++ b/UI/forms/images/trash.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/unlocked.svg b/UI/forms/images/unlocked.svg new file mode 100644 index 0000000..33641a3 --- /dev/null +++ b/UI/forms/images/unlocked.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/UI/forms/images/unlocked_mask.png b/UI/forms/images/unlocked_mask.png deleted file mode 100644 index 192be789c8bed7b49d434a2ddaddfecb6b71d865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPtn~Z><9o-U3d7N^f%+UVt+$iw>JeO$IH z*SWi*bGY^LUS1Iwh^g(^=Xc%CU85uUIbUqd(TyBI7oYOIJ^wZ6WdL*Jq{AmIJuON@ za*v+bbHMhU)ES}kf*%8uUdMOH8yvL0XK*Z8dPCmKx?aIJgZ!e#*=P3Xe%!lBKrq{f z`%-vKQpZ(AF}_<@dP|2z0!WyL?I nfPV+YxBYmxBP#qq%SYxLw^Ral-g$l$=mQ2%S3j3^P6TQ)#?@d4p8@@ySj$i@VOgUCD6iSvjnC?c_e zV+e!D!##w0n12X>bpvJiz$Z%317R3OZVd=z8A1p(5O(}D&;nr&h4{lu&<2WvG_VRG zgjxtYUKgfHVvFX=tn-DAv9VQQaj#L8d$&vvM>wb6d87=vynv>XaF);kh^r&Igb6; zJUJ!WXK(lUx>=5+-u$fT=GU894*fq>62`RiU6Uf2I#Ka+1hU(0^^yl-9?o)`a{@$#*0&-MJy7Em?so5ODJNRP{bsmh`!>euFp|z#nDZlt@qekY^mkAAJ?*f z#b1j#ww6-)`}Ia}_hai($X|MbP1^W35Q zZ7k2V{-j74PkAsyI;Zu14Cl6!&n(h^lyb%`yUDTtrclnLK-qt9%(Z5k1w1$W_q;dE kd3A*9;&x`_U}j+W!%(|sPoSA>rXNV$)78&qol`;+05R#&%K!iX diff --git a/UI/forms/images/up.svg b/UI/forms/images/up.svg new file mode 100644 index 0000000..758bb33 --- /dev/null +++ b/UI/forms/images/up.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/visible.svg b/UI/forms/images/visible.svg new file mode 100644 index 0000000..26b604e --- /dev/null +++ b/UI/forms/images/visible.svg @@ -0,0 +1 @@ + diff --git a/UI/forms/images/visible_mask.png b/UI/forms/images/visible_mask.png deleted file mode 100644 index 67e832672d6943a6a6bb904a5ee4bbc51b186594..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6sy$sCLp09My|_`Z*@4F;@T^Qq zhy&{thceL%;-(iEt_E;03LbPl-&pwIz_T39%Qc>>Lp$C}?q^#2P1qn}?t?8-_ZrW! ziA8Q}@cqtI#V*mR!l`WWMaksbYHOdaB%$k5VujPrpP0ndr4oO2C)1ADLhVhD&TuVW ewdUXV+57>f0R=9Pe|rO+!QkoY=d#Wzp$PyM$3vO` diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc index a68bc70..7c647c7 100644 --- a/UI/forms/obs.qrc +++ b/UI/forms/obs.qrc @@ -1,33 +1,29 @@ - images/mute.png - images/unmute.png - images/refresh.png - images/configuration21_16.png - images/invisible_mask.png - images/visible_mask.png - images/list_remove.png - images/add.png - images/down.png - images/editscene.png - images/live.png - images/properties.png - images/up.png + images/mute.svg + images/refresh.svg + images/no_sources.svg + images/minus.svg + images/plus.svg + images/down.svg + images/up.svg images/obs.png images/tray_active.png - images/locked_mask.png - images/unlocked_mask.png - images/collapse.png - images/expand.png + images/expand.svg + images/unlocked.svg + images/locked.svg + images/invisible.svg + images/visible.svg + images/trash.svg + images/revert.svg - images/settings/advanced.png - images/settings/network.png - images/settings/video-display-3.png - images/settings/decibel_audio_player.png - images/settings/applications-system-2.png - images/settings/system-settings-3.png - images/settings/network-bluetooth.png - images/settings/preferences-desktop-keyboard-shortcuts.png + images/settings/output.svg + images/settings/stream.svg + images/settings/advanced.svg + images/settings/video.svg + images/settings/audio.svg + images/settings/general.svg + images/settings/hotkeys.svg diff --git a/UI/frontend-plugins/CMakeLists.txt b/UI/frontend-plugins/CMakeLists.txt index bd2d336..908b5c7 100644 --- a/UI/frontend-plugins/CMakeLists.txt +++ b/UI/frontend-plugins/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(decklink-output-ui) add_subdirectory(frontend-tools) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt new file mode 100644 index 0000000..da82a6b --- /dev/null +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -0,0 +1,61 @@ +project(decklink-output-ui) + +if(APPLE) + find_library(COCOA Cocoa) + include_directories(${COCOA}) +endif() + +if(UNIX AND NOT APPLE) + find_package(X11 REQUIRED) + link_libraries(${X11_LIBRARIES}) + include_directories(${X11_INCLUDE_DIR}) +endif() + +set(decklink-ouput-ui_HEADERS + ${decklink-ouput-ui_HEADERS} + ../../properties-view.hpp + ../../properties-view.moc.hpp + ../../vertical-scroll-area.hpp + ../../double-slider.hpp + ../../slider-ignorewheel.hpp + ../../combobox-ignorewheel.hpp + ../../spinbox-ignorewheel.hpp + ./DecklinkOutputUI.h + decklink-ui-main.h + ) +set(decklink-ouput-ui_SOURCES + ${decklink-ouput-ui_SOURCES} + ../../properties-view.cpp + ../../vertical-scroll-area.cpp + ../../double-slider.cpp + ../../slider-ignorewheel.cpp + ../../combobox-ignorewheel.cpp + ../../spinbox-ignorewheel.cpp + ./DecklinkOutputUI.cpp + decklink-ui-main.cpp + ) +set(decklink-ouput-ui_UI + ${decklink-ouput-ui_UI} + forms/output.ui + ) + +if(APPLE) + set(decklink-ouput-ui_PLATFORM_LIBS + ${COCOA}) +endif() + +qt5_wrap_ui(decklink-ouput-ui_UI_HEADERS + ${decklink-ouput-ui_UI}) + +add_library(decklink-ouput-ui MODULE + ${decklink-ouput-ui_HEADERS} + ${decklink-ouput-ui_SOURCES} + ${decklink-ouput-ui_UI_HEADERS} + ) +target_link_libraries(decklink-ouput-ui + ${frontend-tools_PLATFORM_LIBS} + obs-frontend-api + Qt5::Widgets + libobs) + +install_obs_plugin_with_data(decklink-ouput-ui data) diff --git a/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.cpp b/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.cpp new file mode 100644 index 0000000..59f25b0 --- /dev/null +++ b/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.cpp @@ -0,0 +1,139 @@ +#include "DecklinkOutputUI.h" +#include +#include +#include +#include "decklink-ui-main.h" + +DecklinkOutputUI::DecklinkOutputUI(QWidget *parent) + : QDialog(parent), + ui(new Ui_Output) +{ + ui->setupUi(this); + + setSizeGripEnabled(true); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + propertiesView = nullptr; + previewPropertiesView = nullptr; + + connect(ui->startOutput, SIGNAL(released()), this, SLOT(StartOutput())); + connect(ui->stopOutput, SIGNAL(released()), this, SLOT(StopOutput())); + + connect(ui->startPreviewOutput, SIGNAL(released()), this, SLOT(StartPreviewOutput())); + connect(ui->stopPreviewOutput, SIGNAL(released()), this, SLOT(StopPreviewOutput())); +} + +void DecklinkOutputUI::ShowHideDialog() +{ + SetupPropertiesView(); + SetupPreviewPropertiesView(); + + setVisible(!isVisible()); +} + +void DecklinkOutputUI::SetupPropertiesView() +{ + if (propertiesView) + delete propertiesView; + + obs_data_t *settings = obs_data_create(); + + OBSData data = load_settings(); + if (data) + obs_data_apply(settings, data); + + propertiesView = new OBSPropertiesView(settings, + "decklink_output", + (PropertiesReloadCallback) obs_get_output_properties, + 170); + + ui->propertiesLayout->addWidget(propertiesView); + obs_data_release(settings); + + connect(propertiesView, SIGNAL(Changed()), this, SLOT(PropertiesChanged())); +} + +void DecklinkOutputUI::SaveSettings() +{ + BPtr modulePath = obs_module_get_config_path(obs_current_module(), ""); + + os_mkdirs(modulePath); + + BPtr path = obs_module_get_config_path(obs_current_module(), + "decklinkOutputProps.json"); + + obs_data_t *settings = propertiesView->GetSettings(); + if (settings) + obs_data_save_json_safe(settings, path, "tmp", "bak"); +} + +void DecklinkOutputUI::SetupPreviewPropertiesView() +{ + if (previewPropertiesView) + delete previewPropertiesView; + + obs_data_t *settings = obs_data_create(); + + OBSData data = load_preview_settings(); + if (data) + obs_data_apply(settings, data); + + previewPropertiesView = new OBSPropertiesView(settings, + "decklink_output", + (PropertiesReloadCallback) obs_get_output_properties, + 170); + + ui->previewPropertiesLayout->addWidget(previewPropertiesView); + obs_data_release(settings); + + connect(previewPropertiesView, SIGNAL(Changed()), this, SLOT(PreviewPropertiesChanged())); +} + +void DecklinkOutputUI::SavePreviewSettings() +{ + char *modulePath = obs_module_get_config_path(obs_current_module(), ""); + + os_mkdirs(modulePath); + + char *path = obs_module_get_config_path(obs_current_module(), + "decklinkPreviewOutputProps.json"); + + obs_data_t *settings = previewPropertiesView->GetSettings(); + if (settings) + obs_data_save_json_safe(settings, path, "tmp", "bak"); +} + + +void DecklinkOutputUI::StartOutput() +{ + SaveSettings(); + output_start(); +} + +void DecklinkOutputUI::StopOutput() +{ + output_stop(); +} + +void DecklinkOutputUI::PropertiesChanged() +{ + SaveSettings(); +} + + +void DecklinkOutputUI::StartPreviewOutput() +{ + SavePreviewSettings(); + preview_output_start(); +} + +void DecklinkOutputUI::StopPreviewOutput() +{ + preview_output_stop(); +} + +void DecklinkOutputUI::PreviewPropertiesChanged() +{ + SavePreviewSettings(); +} \ No newline at end of file diff --git a/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h b/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h new file mode 100644 index 0000000..418e280 --- /dev/null +++ b/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include "ui_output.h" +#include "../../UI/properties-view.hpp" + +class DecklinkOutputUI : public QDialog { +Q_OBJECT +private: + OBSPropertiesView *propertiesView; + OBSPropertiesView *previewPropertiesView; + +public slots: + void StartOutput(); + void StopOutput(); + void PropertiesChanged(); + + void StartPreviewOutput(); + void StopPreviewOutput(); + void PreviewPropertiesChanged(); + +public: + std::unique_ptr ui; + DecklinkOutputUI(QWidget *parent); + + void ShowHideDialog(); + + void SetupPropertiesView(); + void SaveSettings(); + + void SetupPreviewPropertiesView(); + void SavePreviewSettings(); +}; diff --git a/UI/frontend-plugins/decklink-output-ui/data/.keepme b/UI/frontend-plugins/decklink-output-ui/data/.keepme new file mode 100644 index 0000000..e69de29 diff --git a/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp b/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp new file mode 100644 index 0000000..98c2c7b --- /dev/null +++ b/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "DecklinkOutputUI.h" +#include "../../../plugins/decklink/const.h" + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("decklink-output-ui", "en-US") + +DecklinkOutputUI *doUI; + +bool main_output_running = false; +bool preview_output_running = false; + +obs_output_t *output; + +struct preview_output { + bool enabled; + obs_source_t *current_source; + obs_output_t *output; + + video_t *video_queue; + gs_texrender_t *texrender; + gs_stagesurf_t *stagesurface; + uint8_t *video_data; + uint32_t video_linesize; + + obs_video_info ovi; +}; + +static struct preview_output context = {0}; + +OBSData load_settings() +{ + BPtr path = obs_module_get_config_path(obs_current_module(), + "decklinkOutputProps.json"); + BPtr jsonData = os_quick_read_utf8_file(path); + if (!!jsonData) { + obs_data_t *data = obs_data_create_from_json(jsonData); + OBSData dataRet(data); + obs_data_release(data); + + return dataRet; + } + + return nullptr; +} + +void output_start() +{ + if (!main_output_running) { + OBSData settings = load_settings(); + + if (settings != nullptr) { + output = obs_output_create("decklink_output", + "decklink_output", settings, NULL); + + obs_output_start(output); + obs_data_release(settings); + + main_output_running = true; + } + } +} + +void output_stop() +{ + if (main_output_running) { + obs_output_stop(output); + obs_output_release(output); + main_output_running = false; + } +} + + +OBSData load_preview_settings() +{ + BPtr path = obs_module_get_config_path(obs_current_module(), + "decklinkPreviewOutputProps.json"); + BPtr jsonData = os_quick_read_utf8_file(path); + if (!!jsonData) { + obs_data_t *data = obs_data_create_from_json(jsonData); + OBSData dataRet(data); + obs_data_release(data); + + return dataRet; + } + + return nullptr; +} + +void on_preview_scene_changed(enum obs_frontend_event event, void *param); +void render_preview_source(void *param, uint32_t cx, uint32_t cy); + +void preview_output_start() +{ + if (!preview_output_running) { + OBSData settings = load_preview_settings(); + + if (settings != nullptr) { + context.output = obs_output_create("decklink_output", + "decklink_preview_output", settings, NULL); + + obs_get_video_info(&context.ovi); + + uint32_t width = context.ovi.base_width; + uint32_t height = context.ovi.base_height; + + obs_enter_graphics(); + context.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE); + context.stagesurface = gs_stagesurface_create(width, height, GS_BGRA); + obs_leave_graphics(); + + const video_output_info *mainVOI = video_output_get_info(obs_get_video()); + + video_output_info vi = {0}; + vi.format = VIDEO_FORMAT_BGRA; + vi.width = width; + vi.height = height; + vi.fps_den = context.ovi.fps_den; + vi.fps_num = context.ovi.fps_num; + vi.cache_size = 16; + vi.colorspace = mainVOI->colorspace; + vi.range = mainVOI->range; + vi.name = "decklink_preview_output"; + + video_output_open(&context.video_queue, &vi); + + obs_frontend_add_event_callback(on_preview_scene_changed, &context); + if (obs_frontend_preview_program_mode_active()) { + context.current_source = obs_frontend_get_current_preview_scene(); + } else { + context.current_source = obs_frontend_get_current_scene(); + } + obs_add_main_render_callback(render_preview_source, &context); + + obs_output_set_media(context.output, context.video_queue, obs_get_audio()); + obs_output_start(context.output); + + preview_output_running = true; + } + } +} + +void preview_output_stop() +{ + if (preview_output_running) { + obs_output_stop(context.output); + video_output_stop(context.video_queue); + + obs_remove_main_render_callback(render_preview_source, &context); + obs_frontend_remove_event_callback(on_preview_scene_changed, &context); + + obs_source_release(context.current_source); + + obs_enter_graphics(); + gs_stagesurface_destroy(context.stagesurface); + gs_texrender_destroy(context.texrender); + obs_leave_graphics(); + + video_output_close(context.video_queue); + + preview_output_running = false; + } +} + +void on_preview_scene_changed(enum obs_frontend_event event, void *param) +{ + auto ctx = (struct preview_output*)param; + switch (event) { + case OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED: + case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED: + obs_source_release(ctx->current_source); + ctx->current_source = obs_frontend_get_current_preview_scene(); + break; + case OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED: + obs_source_release(ctx->current_source); + ctx->current_source = obs_frontend_get_current_scene(); + break; + case OBS_FRONTEND_EVENT_SCENE_CHANGED: + if (!obs_frontend_preview_program_mode_active()) { + obs_source_release(ctx->current_source); + ctx->current_source = obs_frontend_get_current_scene(); + } + break; + default: + break; + } +} + +void render_preview_source(void *param, uint32_t cx, uint32_t cy) +{ + auto ctx = (struct preview_output*)param; + + if (!ctx->current_source) return; + + uint32_t width = obs_source_get_base_width(ctx->current_source); + uint32_t height = obs_source_get_base_height(ctx->current_source); + + gs_texrender_reset(ctx->texrender); + + if (gs_texrender_begin(ctx->texrender, width, height)) { + struct vec4 background; + vec4_zero(&background); + + gs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0); + gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f); + + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + + obs_source_video_render(ctx->current_source); + + gs_blend_state_pop(); + gs_texrender_end(ctx->texrender); + + struct video_frame output_frame; + if (video_output_lock_frame(ctx->video_queue, &output_frame, 1, os_gettime_ns())) + { + gs_stage_texture(ctx->stagesurface, gs_texrender_get_texture(ctx->texrender)); + + if (gs_stagesurface_map(ctx->stagesurface, &ctx->video_data, &ctx->video_linesize)) { + uint32_t linesize = output_frame.linesize[0]; + for (uint32_t i = 0; i < ctx->ovi.base_height; i++) { + uint32_t dst_offset = linesize * i; + uint32_t src_offset = ctx->video_linesize * i; + memcpy(output_frame.data[0] + dst_offset, + ctx->video_data + src_offset, + linesize); + } + + gs_stagesurface_unmap(ctx->stagesurface); + ctx->video_data = nullptr; + } + + video_output_unlock_frame(ctx->video_queue); + } + } +} + +void addOutputUI(void) +{ + QAction *action = (QAction*)obs_frontend_add_tools_menu_qaction( + obs_module_text("Decklink Output")); + + QMainWindow *window = (QMainWindow*)obs_frontend_get_main_window(); + + obs_frontend_push_ui_translation(obs_module_get_string); + doUI = new DecklinkOutputUI(window); + obs_frontend_pop_ui_translation(); + + auto cb = []() { + doUI->ShowHideDialog(); + }; + + action->connect(action, &QAction::triggered, cb); +} + +static void OBSEvent(enum obs_frontend_event event, void *) +{ + if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { + OBSData settings = load_settings(); + + if (settings && obs_data_get_bool(settings, "auto_start")) + output_start(); + + OBSData previewSettings = load_preview_settings(); + + if (previewSettings && obs_data_get_bool(previewSettings, "auto_start")) + preview_output_start(); + } +} + +bool obs_module_load(void) +{ + addOutputUI(); + + obs_frontend_add_event_callback(OBSEvent, nullptr); + + return true; +} diff --git a/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.h b/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.h new file mode 100644 index 0000000..ec91b8c --- /dev/null +++ b/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.h @@ -0,0 +1,8 @@ +#pragma once + +void output_start(); +void output_stop(); +OBSData load_settings(); +void preview_output_start(); +void preview_output_stop(); +OBSData load_preview_settings(); \ No newline at end of file diff --git a/UI/frontend-plugins/decklink-output-ui/forms/output.ui b/UI/frontend-plugins/decklink-output-ui/forms/output.ui new file mode 100644 index 0000000..bfaa385 --- /dev/null +++ b/UI/frontend-plugins/decklink-output-ui/forms/output.ui @@ -0,0 +1,128 @@ + + + Output + + + + 0 + 0 + 785 + 497 + + + + + 0 + 0 + + + + Decklink Output + + + true + + + false + + + + QLayout::SetDefaultConstraint + + + + + Output + + + + + + + + + + -1 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Start + + + + + + + Stop + + + + + + + + + Preview Output + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Start + + + + + + + Stop + + + + + + + + + Keyer output requires RGB mode in advanced settings. + + + + + + + + diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index ca29cad..d595485 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -28,6 +28,9 @@ set(frontend-tools_HEADERS ../../horizontal-scroll-area.hpp ../../vertical-scroll-area.hpp ../../double-slider.hpp + ../../slider-ignorewheel.hpp + ../../combobox-ignorewheel.hpp + ../../spinbox-ignorewheel.hpp ) set(frontend-tools_SOURCES ${frontend-tools_SOURCES} @@ -38,6 +41,9 @@ set(frontend-tools_SOURCES ../../horizontal-scroll-area.cpp ../../vertical-scroll-area.cpp ../../double-slider.cpp + ../../slider-ignorewheel.cpp + ../../combobox-ignorewheel.cpp + ../../spinbox-ignorewheel.cpp ) set(frontend-tools_UI ${frontend-tools_UI} diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp index 06c0307..e8429c4 100644 --- a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp @@ -89,6 +89,8 @@ SceneSwitcher::SceneSwitcher(QWidget *parent) { ui->setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + lock_guard lock(switcher->m); switcher->Prune(); diff --git a/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp index f8b6425..2861949 100644 --- a/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp +++ b/UI/frontend-plugins/frontend-tools/captions-mssapi-stream.cpp @@ -249,8 +249,9 @@ STDMETHODIMP CaptionStream::Stat(STATSTG *stg, DWORD flag) stg->cbSize.QuadPart = (ULONGLONG)buf->size; if (flag == STATFLAG_DEFAULT) { - stg->pwcsName = (wchar_t*)CoTaskMemAlloc(sizeof(stat_name)); - memcpy(stg->pwcsName, stat_name, sizeof(stat_name)); + size_t byte_size = (wcslen(stat_name) + 1) * sizeof(wchar_t); + stg->pwcsName = (wchar_t*)CoTaskMemAlloc(byte_size); + memcpy(stg->pwcsName, stat_name, byte_size); } return S_OK; diff --git a/UI/frontend-plugins/frontend-tools/captions.cpp b/UI/frontend-plugins/frontend-tools/captions.cpp index 5c68d87..a53c09b 100644 --- a/UI/frontend-plugins/frontend-tools/captions.cpp +++ b/UI/frontend-plugins/frontend-tools/captions.cpp @@ -90,6 +90,8 @@ CaptionsDialog::CaptionsDialog(QWidget *parent) : { ui->setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + auto cb = [this] (obs_source_t *source) { uint32_t caps = obs_source_get_output_flags(source); diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ar-SA.ini b/UI/frontend-plugins/frontend-tools/data/locale/ar-SA.ini new file mode 100644 index 0000000..7c74e15 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/ar-SA.ini @@ -0,0 +1,42 @@ +SceneSwitcher="مبدل المَشاهِد التلقائي" +SceneSwitcher.OnNoMatch="عندما لا تتطابق أي نافذة:" +SceneSwitcher.OnNoMatch.DontSwitch="لا تقم بالتبديل" +SceneSwitcher.OnNoMatch.SwitchTo="التبديل إلى:" +SceneSwitcher.CheckInterval="التأكد من عنوان النافذة النشطة كل:" +SceneSwitcher.ActiveOrNotActive="حالة مبدل المشاهد:" +InvalidRegex.Title="تعبير نمطي غير صحيح" +InvalidRegex.Text="التعبير النمطي الذي أدخلته غير صحيح." +Active="مفعّل" +Inactive="غير مُفعّل" +Start="ابدأ" +Stop="ايقاف" + +Captions="ترجمة فورية (تجريبي)" +Captions.AudioSource="مصدر الصوت" +Captions.CurrentSystemLanguage="لغة النظام الحالية (%1)" +Captions.Provider="موفر الخدمة" +Captions.Error.GenericFail="فشل في تشغيل الترجمة الفورية" + +OutputTimer="مؤقِّت الإخراج" +OutputTimer.Stream="إيقاف البث بعد:" +OutputTimer.Record="إيقاف التسجيل بعد:" +OutputTimer.Stream.StoppingIn="سيتم إيقاف البث بعد:" +OutputTimer.Record.StoppingIn="سيتم إيقاف التسجيل بعد:" +OutputTimer.Stream.EnableEverytime="تفعيل مؤقِّت البث في كل مرة" +OutputTimer.Record.EnableEverytime="تفعيل مؤقِّت التسجيل في كل مرة" + +Scripts="سكريبتات" +LoadedScripts="السكريبتات المحملة" +AddScripts="إضافة سكريبت" +RemoveScripts="إزالة السكريبت" +ReloadScripts="إعادة تحميل السكربتات" +PythonSettings="إعدادات بايثون" +PythonSettings.PythonInstallPath32bit="مسار تثبيت بايثون (32 بت)" +PythonSettings.PythonInstallPath64bit="مسار تثبيت بايثون (64 بت)" +PythonSettings.BrowsePythonPath="استعراض مسار البايثون" +ScriptLogWindow="سجل السكريبت" +Description="الوصف" + +FileFilter.ScriptFiles="ملفات السكريبت" +FileFilter.AllFiles="كافة الملفات" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/bg-BG.ini b/UI/frontend-plugins/frontend-tools/data/locale/bg-BG.ini new file mode 100644 index 0000000..86bb093 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/bg-BG.ini @@ -0,0 +1,35 @@ +SceneSwitcher="Автоматичен превключвател на сцени" +SceneSwitcher.OnNoMatch.DontSwitch="Не превключвай" +SceneSwitcher.OnNoMatch.SwitchTo="Превключи на:" +SceneSwitcher.CheckInterval="Проверка название на активния прозорец всеки:" +SceneSwitcher.ActiveOrNotActive="Превключвателят на сцени е:" +Active="Активен" +Inactive="Изключен" +Start="Начало" +Stop="Край" + +Captions="Надписи (експериментално)" +Captions.AudioSource="Аудио източник" +Captions.CurrentSystemLanguage="Текущ основен език (%1)" +Captions.Provider="Доставчик" +Captions.Error.GenericFail="Неуспешно зареждане на надписи" + +OutputTimer.Stream="Прекъсни излъчването след:" +OutputTimer.Record="Прекъсни записването след:" +OutputTimer.Stream.StoppingIn="Излъчването ще спре след:" +OutputTimer.Record.StoppingIn="Записването ще спре след:" +OutputTimer.Stream.EnableEverytime="Показвай брояча на излъчването всеки път" + +Scripts="Скриптове" +LoadedScripts="Заредени скриптове" +AddScripts="Добавяне скриптове" +RemoveScripts="Премахване скриптове" +ReloadScripts="Опресняване скриптове" +PythonSettings="Настройки Python" +PythonSettings.PythonInstallPath32bit="Път за инсталация на Python (32 бита)" +PythonSettings.PythonInstallPath64bit="Път за инсталация на Python (64 бита)" +ScriptLogWindow="Записи на скриптове" +Description="Описание" + +FileFilter.AllFiles="Всички файлове" + 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 db752c7..72eb53f 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini @@ -1,9 +1,9 @@ -SceneSwitcher="Automatisk sceneomskifter" +SceneSwitcher="Automatisk sceneskifter" SceneSwitcher.OnNoMatch="Når intet vindue matcher:" SceneSwitcher.OnNoMatch.DontSwitch="Skift ikke" SceneSwitcher.OnNoMatch.SwitchTo="Skift til:" -SceneSwitcher.CheckInterval="Kontroller aktivt vinduestitel hvert:" -SceneSwitcher.ActiveOrNotActive="Sceneomskifter er:" +SceneSwitcher.CheckInterval="Tjek aktiv vinduestitel hver:" +SceneSwitcher.ActiveOrNotActive="Sceneskifter er:" InvalidRegex.Title="Ugyldigt regulært udtryk" InvalidRegex.Text="Det af dig angivne regulære udtryk er ugyldigt." Active="Aktiv" @@ -11,19 +11,19 @@ Inactive="Inaktiv" Start="Start" Stop="Stop" -Captions="Undertekster (eksperimentel)" +Captions="Billedtekster (eksperimentel)" Captions.AudioSource="Lydkilde" Captions.CurrentSystemLanguage="Aktuelt systemsprog (%1)" Captions.Provider="Leverandør" -Captions.Error.GenericFail="Kunne ikke starte tekster" +Captions.Error.GenericFail="Kunne ikke starte billedtekster" -OutputTimer="Output-timer" +OutputTimer="Outputtimer" OutputTimer.Stream="Stands streaming efter:" OutputTimer.Record="Stands optagelse efter:" OutputTimer.Stream.StoppingIn="Streaming standser om:" -OutputTimer.Record.StoppingIn="Streaming standser om:" -OutputTimer.Stream.EnableEverytime="Aktivér streaming-timer hver gang" -OutputTimer.Record.EnableEverytime="Aktivér optage-timer hver gang" +OutputTimer.Record.StoppingIn="Optagelse standser om:" +OutputTimer.Stream.EnableEverytime="Aktivér streamingtimer hver gang" +OutputTimer.Record.EnableEverytime="Aktivér optagetimer hver gang" Scripts="Scripts" LoadedScripts="Indlæste scripts" @@ -33,7 +33,7 @@ ReloadScripts="Genindlæs scripts" PythonSettings="Python-indstillinger" PythonSettings.PythonInstallPath32bit="Python-installationssti (32bit)" PythonSettings.PythonInstallPath64bit="Python-installationssti (64bit)" -PythonSettings.BrowsePythonPath="Gennemse Python-sti" +PythonSettings.BrowsePythonPath="Find Python-sti" ScriptLogWindow="Scriptlog" Description="Beskrivelse" 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 7d444c0..555c8df 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini @@ -8,8 +8,8 @@ InvalidRegex.Title="Ungültiger regulärer Ausdruck" InvalidRegex.Text="Der reguläre Ausdruck, den Sie eingegeben haben, ist ungültig." Active="Aktiv" Inactive="Inaktiv" -Start="Start" -Stop="Stop" +Start="Starten" +Stop="Stoppen" Captions="Untertitel (experimentell)" Captions.AudioSource="Audioquelle" @@ -22,8 +22,8 @@ OutputTimer.Stream="Stoppe Stream nach:" OutputTimer.Record="Stoppe Aufnahme nach:" OutputTimer.Stream.StoppingIn="Stream stoppt in:" OutputTimer.Record.StoppingIn="Aufnahme stoppt in:" -OutputTimer.Stream.EnableEverytime="Streaming-Timer jedes Mal aktivieren" -OutputTimer.Record.EnableEverytime="Aufnahme-Timer jedes Mal aktivieren" +OutputTimer.Stream.EnableEverytime="Streamingtimer jedes Mal aktivieren" +OutputTimer.Record.EnableEverytime="Aufnahmetimer jedes Mal aktivieren" Scripts="Skripte" LoadedScripts="Geladene Skripte" @@ -31,8 +31,8 @@ AddScripts="Skripte hinzufügen" RemoveScripts="Skripte entfernen" ReloadScripts="Skripte neu laden" PythonSettings="Python-Einstellungen" -PythonSettings.PythonInstallPath32bit="Python Installationspfad (32bit)" -PythonSettings.PythonInstallPath64bit="Python Installationspfad (64bit)" +PythonSettings.PythonInstallPath32bit="Python-Installationspfad (32bit)" +PythonSettings.PythonInstallPath64bit="Python-Installationspfad (64bit)" PythonSettings.BrowsePythonPath="Python-Pfad öffnen" ScriptLogWindow="Skriptprotokoll" Description="Beschreibung" 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 38ce98c..1b6fef5 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini @@ -25,11 +25,11 @@ OutputTimer.Record.StoppingIn="Finalizando grabación en:" OutputTimer.Stream.EnableEverytime="Activar temporizador en cada transmisión" OutputTimer.Record.EnableEverytime="Activar temporizador en cada grabación" -Scripts="Guion" -LoadedScripts="Guiones cargados" -AddScripts="Agregar Guiones" -RemoveScripts="Quitar Guiones" -ReloadScripts="Recargar Guiones" +Scripts="Scripts" +LoadedScripts="Scripts Cargados" +AddScripts="Añadir Scripts" +RemoveScripts="Eliminar Scripts" +ReloadScripts="Recargar Scripts" PythonSettings="Configuración de Python" PythonSettings.PythonInstallPath32bit="Dirección de la instalación de Python (32bit)" PythonSettings.PythonInstallPath64bit="Dirección de la instalación de Python (64bit)" @@ -37,6 +37,6 @@ PythonSettings.BrowsePythonPath="Buscar Ruta de Phyton" ScriptLogWindow="Registro de secuencia de comandos" Description="Descripción" -FileFilter.ScriptFiles="Archivos de Guiones" +FileFilter.ScriptFiles="Archivos de Script" FileFilter.AllFiles="Todos los archivos" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/fa-IR.ini b/UI/frontend-plugins/frontend-tools/data/locale/fa-IR.ini new file mode 100644 index 0000000..dac6ec2 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/fa-IR.ini @@ -0,0 +1,25 @@ +SceneSwitcher.OnNoMatch="وقتی هیچ پنجره‌ای همخوانی نداشت:" +SceneSwitcher.OnNoMatch.DontSwitch="سوییچ نکن" +SceneSwitcher.OnNoMatch.SwitchTo="سویچ کن به:" +Active="فعال" +Inactive="غیر فعال" +Start="شروع" +Stop="توقف" + + +OutputTimer.Stream.EnableEverytime="هربار زمان سنج پخش جریانی را فعال کنید" +OutputTimer.Record.EnableEverytime="هربار زمان سنج ضبط را فعال کنید" + +Scripts="اسکریپت ها" +LoadedScripts="اسکریپت های بارگیری شده" +AddScripts="اضافه کردن اسکریپت ها" +RemoveScripts="پاک کردن اسکریپت ها" +ReloadScripts="بارگیری مجدد اسکریپت ها" +PythonSettings="تنظیمات پایتون" +PythonSettings.BrowsePythonPath="مرور مسیر پایتون" +ScriptLogWindow="اسکریپت نویسی" +Description="توضیحات" + +FileFilter.ScriptFiles="فایل های اسکریپت" +FileFilter.AllFiles="همه‌ی فایل ها" + 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 4b9abd7..6ebdffd 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini @@ -3,8 +3,8 @@ SceneSwitcher.OnNoMatch="Si aucune fenêtre ne correspond :" SceneSwitcher.OnNoMatch.DontSwitch="Ne rien faire" SceneSwitcher.OnNoMatch.SwitchTo="Basculer vers :" SceneSwitcher.CheckInterval="Vérifier le titre de la fenêtre active toutes les :" -SceneSwitcher.ActiveOrNotActive="Etat du sélecteur automatique :" -InvalidRegex.Title="Expression invalide" +SceneSwitcher.ActiveOrNotActive="État du sélecteur automatique :" +InvalidRegex.Title="Expression régulière invalide" InvalidRegex.Text="L'expression régulière saisie est invalide." Active="Actif" Inactive="Inactif" @@ -18,12 +18,12 @@ 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 :" -OutputTimer.Record="Arrêter l'enregistrement dans :" +OutputTimer.Stream="Arrêter le streaming après :" +OutputTimer.Record="Arrêter l'enregistrement après :" OutputTimer.Stream.StoppingIn="Arrêt du streaming dans :" OutputTimer.Record.StoppingIn="Arrêt de l'enregistrement dans :" -OutputTimer.Stream.EnableEverytime="Activer le minuteur automatiquement à chaque stream" -OutputTimer.Record.EnableEverytime="Activer le minuteur automatiquement à chaque enregistrement" +OutputTimer.Stream.EnableEverytime="Démarrer le minuteur automatiquement à chaque stream" +OutputTimer.Record.EnableEverytime="Démarrer le minuteur automatiquement à chaque enregistrement" Scripts="Scripts" LoadedScripts="Scripts chargés" @@ -33,7 +33,7 @@ ReloadScripts="Recharger les Scripts" PythonSettings="Paramètres Python" PythonSettings.PythonInstallPath32bit="Chemin d’installation Python (32 bits)" PythonSettings.PythonInstallPath64bit="Chemin d’installation Python (64 bits)" -PythonSettings.BrowsePythonPath="Parcourir le chemin de Python" +PythonSettings.BrowsePythonPath="Localiser l'installation de Python" ScriptLogWindow="Journal de script" Description="Description" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini b/UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini index da8fbed..f0de18e 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini @@ -16,7 +16,7 @@ Captions="Opisi (experimentalno)" OutputTimer="Tempomat snimanja i emitovanja" OutputTimer.Stream="Zaustavi emitovanje nakon:" OutputTimer.Record="Zaustavi snimanje nakon:" -OutputTimer.Stream.StoppingIn="Prekidanje emitovanja za:" +OutputTimer.Stream.StoppingIn="Prekidanje emitiranja za:" OutputTimer.Record.StoppingIn="Prekidanje snimanja za:" OutputTimer.Stream.EnableEverytime="Omogući štopovanje emitovanja svaki put" OutputTimer.Record.EnableEverytime="Omogući štopovanje snimanja svaki put" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini b/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini index d07ae54..45e909c 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini @@ -1,4 +1,4 @@ -SceneSwitcher="Cambia scena automatico" +SceneSwitcher="Cambio scena automatico" SceneSwitcher.OnNoMatch="Quando nessuna finestra coincide:" SceneSwitcher.OnNoMatch.DontSwitch="Non passare" SceneSwitcher.OnNoMatch.SwitchTo="Passa a:" @@ -17,13 +17,13 @@ Captions.CurrentSystemLanguage="Lingua del sistema (%1)" Captions.Provider="Sintetizzatore" Captions.Error.GenericFail="Impossibile avviare i sottititoli" -OutputTimer="Timer di uscita" -OutputTimer.Stream="Termina diretta dopo:" -OutputTimer.Record="Termina registrazione dopo:" -OutputTimer.Stream.StoppingIn="La diretta terminerà in:" -OutputTimer.Record.StoppingIn="La registrazione terminerà in:" -OutputTimer.Stream.EnableEverytime="Abilita il timer per lo streaming ogni volta" -OutputTimer.Record.EnableEverytime="Abilita il timer per la registrazione ogni volta" +OutputTimer="Conto alla rovescia" +OutputTimer.Stream="Termina la diretta dopo:" +OutputTimer.Record="Termina la registrazione dopo:" +OutputTimer.Stream.StoppingIn="La diretta terminerà tra:" +OutputTimer.Record.StoppingIn="La registrazione terminerà tra:" +OutputTimer.Stream.EnableEverytime="Attiva il conto alla rovescia per le dirette ogni volta" +OutputTimer.Record.EnableEverytime="Attiva il conto alla rovescia per le registrazioni ogni volta" Scripts="Script" LoadedScripts="Script caricati" @@ -31,9 +31,9 @@ AddScripts="Aggiungi script" RemoveScripts="Rimuovi script" ReloadScripts="Ricarica script" PythonSettings="Impostazioni di Python" -PythonSettings.PythonInstallPath32bit="Percorso d'installazione di Python (32bit)" -PythonSettings.PythonInstallPath64bit="Percorso d'installazione di Python (64bit)" -PythonSettings.BrowsePythonPath="Sfoglia Percorso Python" +PythonSettings.PythonInstallPath32bit="Percorso d'installazione di Python (32 bit)" +PythonSettings.PythonInstallPath64bit="Percorso d'installazione di Python (64 bit)" +PythonSettings.BrowsePythonPath="Sfoglia il percorso di Python" ScriptLogWindow="Log degli script" Description="Descrizione" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini b/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini index d6288a3..fe12723 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ka-GE.ini @@ -20,7 +20,7 @@ Captions.Error.GenericFail="წარწერების დადება OutputTimer="ჩაწერის და ნაკადის წამზომი" OutputTimer.Stream="ნაკადი გაეშვას არაუმეტეს:" OutputTimer.Record="ჩაწერა გაგრძელდეს არაუმეტეს:" -OutputTimer.Stream.StoppingIn="ნაკადის შეჩერების დროა:" +OutputTimer.Stream.StoppingIn="ნაკადის შეწყვეტის დრო:" OutputTimer.Record.StoppingIn="ჩაწერის შეწყვეტის დრო:" OutputTimer.Stream.EnableEverytime="ნაკადის წამზომის ჩართვა ყოველ ჯერზე" OutputTimer.Record.EnableEverytime="ჩაწერის წამზომის ჩართვა ყოველ ჯერზე" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/mn-MN.ini b/UI/frontend-plugins/frontend-tools/data/locale/mn-MN.ini new file mode 100644 index 0000000..5884e0d --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/mn-MN.ini @@ -0,0 +1,32 @@ +SceneSwitcher.OnNoMatch.DontSwitch="Бүү соль" +SceneSwitcher.OnNoMatch.SwitchTo="Үүнрүү шилжүүл:" +Active="Идэвхтэй" +Inactive="Идэвхгүй" +Start="Эхлэх" +Stop="Зогсоох" + +Captions.AudioSource="Аудио эх сурвалж" +Captions.CurrentSystemLanguage="Одоогийн системийн хэл (%1)" +Captions.Provider="Үйлчилгээ үзүүлэгч" + +OutputTimer="Гаралтын цагийн тохируулга" +OutputTimer.Stream="Цацалтыг үүний дараа зогсоох:" +OutputTimer.Record="Бичлэгийг үүний дараа зогсоох:" +OutputTimer.Stream.StoppingIn="Урсгалыг зогсооход:" +OutputTimer.Record.StoppingIn="Бичлэгийг зогсооход:" + +Scripts="Скриптүүд" +LoadedScripts="Уншигдсан скриптүүд" +AddScripts="Скрипт нэмэх" +RemoveScripts="Скрипт хасах" +ReloadScripts="Скрипт дахин унших" +PythonSettings="Python тохиргоо" +PythonSettings.PythonInstallPath32bit="Python суусан байрлал (32 бит)" +PythonSettings.PythonInstallPath64bit="Python суусан байрлал (64 бит)" +PythonSettings.BrowsePythonPath="Python-ы замыг заах" +ScriptLogWindow="Скриптийн тэмдэглэл" +Description="Тайлбар" + +FileFilter.ScriptFiles="Скрипт файлууд" +FileFilter.AllFiles="Бүх файлууд" + 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 65481a8..618338a 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini @@ -1,4 +1,4 @@ -SceneSwitcher="Automatyczne Przełączanie Scen" +SceneSwitcher="Automatyczne przełączanie scen" SceneSwitcher.OnNoMatch="Gdy nie pasuje żadne okno:" SceneSwitcher.OnNoMatch.DontSwitch="Nie przełączaj" SceneSwitcher.OnNoMatch.SwitchTo="Przełącz na:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/pt-PT.ini b/UI/frontend-plugins/frontend-tools/data/locale/pt-PT.ini index a11f5d0..2167c08 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/pt-PT.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/pt-PT.ini @@ -14,6 +14,7 @@ Stop="Parar" Captions="Legendas (Experimental)" Captions.AudioSource="Fonte de audio" Captions.CurrentSystemLanguage="Linguagem de Sistema Atual (%1)" +Captions.Provider="Fornecedor" Captions.Error.GenericFail="Ocorreu um erro a iniciar legendas" OutputTimer="Temporizador de saída" @@ -24,5 +25,15 @@ OutputTimer.Record.StoppingIn="A gravação irá parar em:" OutputTimer.Stream.EnableEverytime="Ativar temporizador de transmissão sempre" OutputTimer.Record.EnableEverytime="Ativar temporizador de gravação sempre" +Scripts="Scripts" +LoadedScripts="Scripts Carregados" +AddScripts="Adicionar Scripts" +RemoveScripts="Remover Scripts" +ReloadScripts="Recarregar Scripts" +PythonSettings="Definições do Python" +PythonSettings.PythonInstallPath32bit="Caminho da Instalação do Python (32bits)" +PythonSettings.PythonInstallPath64bit="Caminho da Instalação do Python (64bits)" +Description="Descrição" +FileFilter.AllFiles="Todos os Ficheiros" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini b/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini index 11aa798..3e20647 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini @@ -1,18 +1,33 @@ -SceneSwitcher="Schimbator automat de scenă" +SceneSwitcher="Schimbător automat de scene" SceneSwitcher.OnNoMatch="Când nicio fereastră nu se potrivește:" +SceneSwitcher.OnNoMatch.DontSwitch="Nu schimba" SceneSwitcher.OnNoMatch.SwitchTo="Schimbă la:" +SceneSwitcher.CheckInterval="Verifică titlul ferestrei active la fiecare:" +SceneSwitcher.ActiveOrNotActive="Schimbătorul de scene este:" Active="Activ" Inactive="Inactiv" -Start="Pornire" -Stop="Oprire" +Start="Pornește" +Stop="Oprește" Captions="Subtitrări (experimentale)" Captions.AudioSource="Sursa audio" -Captions.CurrentSystemLanguage="Limba curentă a sistemului (%1)" +Captions.CurrentSystemLanguage="Limba actuală a sistemului (%1)" Captions.Provider="Furnizor" -OutputTimer.Record="Opriți inregistrarea dupa:" -OutputTimer.Record.StoppingIn="Oprire înregistrare în:" - +OutputTimer.Record="Opriți înregistrarea după:" +OutputTimer.Stream.StoppingIn="Se oprește transmisiunea în:" +OutputTimer.Record.StoppingIn="Înregistrarea se oprește în:" + +Scripts="Scripturi" +LoadedScripts="Scripturi încărcate" +AddScripts="Adaugă scripturi" +RemoveScripts="Elimină scripturi" +ReloadScripts="Reîncarcă scripturile" +PythonSettings="Setări Python" +PythonSettings.PythonInstallPath32bit="Calea instalării Python (32bit)" +PythonSettings.PythonInstallPath64bit="Calea instalării Python (64bit)" +PythonSettings.BrowsePythonPath="Răsfoiește calea Python" +ScriptLogWindow="Jurnalul scripturilor" +Description="Descriere" 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 b5de83b..c3b8be2 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini @@ -17,10 +17,10 @@ Captions.CurrentSystemLanguage="Текущий язык системы (%1)" Captions.Provider="Поставщик" Captions.Error.GenericFail="Не удалось запустить субтитры" -OutputTimer="Таймер записи и трансляции" -OutputTimer.Stream="Завершить трансляцию через:" +OutputTimer="Таймер записи и стрима" +OutputTimer.Stream="Завершить стрим через:" OutputTimer.Record="Завершить запись через:" -OutputTimer.Stream.StoppingIn="Трансляция будет завершена через:" +OutputTimer.Stream.StoppingIn="Стрим будет завершён через:" OutputTimer.Record.StoppingIn="Запись будет завершена через:" OutputTimer.Stream.EnableEverytime="Включать таймер трансляции каждый раз" OutputTimer.Record.EnableEverytime="Включать таймер записи каждый раз" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sk-SK.ini b/UI/frontend-plugins/frontend-tools/data/locale/sk-SK.ini index 8208407..83de074 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/sk-SK.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/sk-SK.ini @@ -25,5 +25,18 @@ OutputTimer.Record.StoppingIn="Nahrávanie za zastaví za:" OutputTimer.Stream.EnableEverytime="Povoliť časovač streamovania zakaždým" OutputTimer.Record.EnableEverytime="Zapnúť časovač nahrávania zakaždým" +Scripts="Skripty" +LoadedScripts="Načítané skripty" +AddScripts="Pridať skripty" +RemoveScripts="Odstrániť skripty" +ReloadScripts="Znovu-načítať skripty" +PythonSettings="Python nastavenia" +PythonSettings.PythonInstallPath32bit="Python inštalačná cesta (32bitová)" +PythonSettings.PythonInstallPath64bit="Python inštalačná cesta (64bitová)" +PythonSettings.BrowsePythonPath="Prehliadať cestu k Pythonu" +ScriptLogWindow="Log skriptu" +Description="Popis" +FileFilter.ScriptFiles="Súbory skriptu" +FileFilter.AllFiles="Všetky súbory" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini b/UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini index bf7c94f..52a522c 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini @@ -11,14 +11,32 @@ Inactive="Neaktivan" Start="Pokreni" Stop="Zaustavi" +Captions="Natpisi (Eksperimentalno)" +Captions.AudioSource="Izvor zvuka" +Captions.CurrentSystemLanguage="Trenutni jezik sistema (%1)" +Captions.Provider="Provajder" +Captions.Error.GenericFail="Nemogućnost prikazivanja natpisa" OutputTimer="Tempomat snimanja i emitovanja" OutputTimer.Stream="Zaustavi emitovanje nakon:" OutputTimer.Record="Zaustavi snimanje nakon:" OutputTimer.Stream.StoppingIn="Prekidanje emitovanja za:" OutputTimer.Record.StoppingIn="Prekidanje snimanja za:" -OutputTimer.Stream.EnableEverytime="Omogući štopovanje emitovanja svaki put" -OutputTimer.Record.EnableEverytime="Omogući štopovanje snimanja svaki put" +OutputTimer.Stream.EnableEverytime="Uključi tajmer strimovanja svaki put" +OutputTimer.Record.EnableEverytime="Uključi tajmer snimanja svaki put" +Scripts="Skripte" +LoadedScripts="Učitane skripte" +AddScripts="Dodaj skripte" +RemoveScripts="Izbaci skripte" +ReloadScripts="Ponovo učitaj skripte" +PythonSettings="Python podešavanja" +PythonSettings.PythonInstallPath32bit="Putanja do foldera sa Python instalacijom (32bit)" +PythonSettings.PythonInstallPath64bit="Putanja do foldera sa Python instalacijom (64bit)" +PythonSettings.BrowsePythonPath="Pretraži putanju do Python foldera" +ScriptLogWindow="Prijava skripte" +Description="Opis" +FileFilter.ScriptFiles="Fajlovi skripte" +FileFilter.AllFiles="Svi fajlovi" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini b/UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini index f1e7a07..5d3cf49 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini @@ -11,14 +11,32 @@ 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="Омогући штоповање снимања сваки пут" +OutputTimer.Stream.EnableEverytime="Укључи тајмер стримовања сваки пут" +OutputTimer.Record.EnableEverytime="Укључи тајмер снимања сваки пут" +Scripts="Скрипте" +LoadedScripts="Учитане скрипте" +AddScripts="Додај скрипте" +RemoveScripts="Избаци скрипте" +ReloadScripts="Поново учитај скрипте" +PythonSettings="Python подешавања" +PythonSettings.PythonInstallPath32bit="Путања до фолдера са Python инсталацијом (32bit)" +PythonSettings.PythonInstallPath64bit="Путања до фолдера са Python инсталацијом (64bit)" +PythonSettings.BrowsePythonPath="Претражи путању до Python фолдера" +ScriptLogWindow="Пријава скрипте" +Description="Опис" +FileFilter.ScriptFiles="Фајлови скрипте" +FileFilter.AllFiles="Сви фајлови" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ta-IN.ini b/UI/frontend-plugins/frontend-tools/data/locale/ta-IN.ini new file mode 100644 index 0000000..d6aa356 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/ta-IN.ini @@ -0,0 +1,27 @@ +SceneSwitcher="தானியங்கு காட்சி நிலைமாற்றி" +SceneSwitcher.OnNoMatch.DontSwitch="மாற வேண்டாம்" +SceneSwitcher.OnNoMatch.SwitchTo="மாற்று:" +SceneSwitcher.CheckInterval="செயலில் உள்ள சாளர தலைப்பை சரிபார்க்கவும்:" +SceneSwitcher.ActiveOrNotActive="காட்சி இடமாற்றி உள்ளது:" +InvalidRegex.Title="செல்லாத வழக்கமான வெளிப்பாடு" +InvalidRegex.Text="நீங்கள் உள்ளிட்ட வழக்கமான தொடர் தவறானது." +Active="செயலில்" +Inactive="செயலற்ற" +Start="தொடங்கு" +Stop="நிறுத்து" + +Captions="தலைப்புகளை (சோதனை)" +Captions.AudioSource="ஒலி ஆதாரம்" +Captions.CurrentSystemLanguage="தற்போதைய அமைப்பு மொழி (%1)" +Captions.Provider="வழங்குநர்" +Captions.Error.GenericFail="தலைப்புகள் தொடங்குவதில் தோல்வி" + +OutputTimer="வெளியீடு நேரம்" +OutputTimer.Stream="நேரலை நிறுத்த நேரம்:" +OutputTimer.Record="பதிவு நிறுத்த நேரம்:" +OutputTimer.Stream.StoppingIn="நேரலை முடிவடையும் நேரம்:" +OutputTimer.Record.StoppingIn="பதிவு முடிவடையும் நேரம்:" + +Scripts="எழுத்து" + + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini b/UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini new file mode 100644 index 0000000..d53ebb1 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini @@ -0,0 +1,13 @@ +Active="ใช้งานอยู่" +Inactive="ไม่ใช้งาน" +Start="เริ่ม" +Stop="หยุด" + + + +Scripts="สคริปต์" +AddScripts="เพิ่มสคริปต์" +Description="คำอธิบาย" + +FileFilter.AllFiles="ไฟล์ทั้งหมด" + 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 316d412..acd3d5f 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini @@ -15,7 +15,7 @@ Captions="字幕 (实验)" Captions.AudioSource="音频源" Captions.CurrentSystemLanguage="当前系统语言 (%1)" Captions.Provider="提供程序" -Captions.Error.GenericFail="启动捕获失败" +Captions.Error.GenericFail="字幕启动失败" OutputTimer="输出计时器" OutputTimer.Stream="停止流处理后:" diff --git a/UI/frontend-plugins/frontend-tools/data/scripts/instant-replay.lua b/UI/frontend-plugins/frontend-tools/data/scripts/instant-replay.lua index 8137990..b1f82ff 100644 --- a/UI/frontend-plugins/frontend-tools/data/scripts/instant-replay.lua +++ b/UI/frontend-plugins/frontend-tools/data/scripts/instant-replay.lua @@ -2,6 +2,7 @@ obs = obslua source_name = "" hotkey_id = obs.OBS_INVALID_HOTKEY_ID attempts = 0 +last_replay = "" ---------------------------------------------------------- @@ -22,27 +23,45 @@ function try_play() obs.obs_output_release(replay_buffer) + if path == last_replay then + path = nil + end + -- If the path is valid and the source exists, update it with the -- replay file to play back the replay. Otherwise, stop attempting to - -- replay after 10 seconds + -- replay after 10 retries if path == nil then attempts = attempts + 1 if attempts >= 10 then obs.remove_current_callback() end else + last_replay = path local source = obs.obs_get_source_by_name(source_name) if source ~= nil then local settings = obs.obs_data_create() - obs.obs_data_set_string(settings, "local_file", path) - obs.obs_data_set_bool(settings, "is_local_file", true) - obs.obs_data_set_bool(settings, "close_when_inactive", true) - obs.obs_data_set_bool(settings, "restart_on_activate", true) + source_id = obs.obs_source_get_id(source) + if source_id == "ffmpeg_source" then + obs.obs_data_set_string(settings, "local_file", path) + obs.obs_data_set_bool(settings, "is_local_file", true) - -- updating will automatically cause the source to - -- refresh if the source is currently active, otherwise - -- the source will play whenever its scene is activated - obs.obs_source_update(source, settings) + -- updating will automatically cause the source to + -- refresh if the source is currently active + obs.obs_source_update(source, settings) + elseif source_id == "vlc_source" then + -- "playlist" + array = obs.obs_data_array_create() + item = obs.obs_data_create() + obs.obs_data_set_string(item, "value", path) + obs.obs_data_array_push_back(array, item) + obs.obs_data_set_array(settings, "playlist", array) + + -- updating will automatically cause the source to + -- refresh if the source is currently active + obs.obs_source_update(source, settings) + obs.obs_data_release(item) + obs.obs_data_array_release(array) + end obs.obs_data_release(settings) obs.obs_source_release(source) @@ -65,11 +84,11 @@ function instant_replay(pressed) local ph = obs.obs_output_get_proc_handler(replay_buffer) obs.proc_handler_call(ph, "save", nil) - -- Set a 1-second timer to attempt playback every 1 second + -- Set a 2-second timer to attempt playback every 1 second -- until the replay is available if obs.obs_output_active(replay_buffer) then attempts = 0 - obs.timer_add(try_play, 1000) + obs.timer_add(try_play, 2000) else obs.script_log(obs.LOG_WARNING, "Tried to save an instant replay, but the replay buffer is not active!") end @@ -90,7 +109,7 @@ end -- A function named script_description returns the description shown to -- the user function script_description() - return "When the \"Instant Replay\" hotkey is triggered, saves a replay with the replay buffer, and then plays it in a media source as soon as the replay is ready. Requires an active replay buffer.\n\nMade by Jim" + return "When the \"Instant Replay\" hotkey is triggered, saves a replay with the replay buffer, and then plays it in a media source as soon as the replay is ready. Requires an active replay buffer.\n\nMade by Jim and Exeldro" end -- A function named script_properties defines the properties that the user @@ -106,6 +125,11 @@ function script_properties() if source_id == "ffmpeg_source" then local name = obs.obs_source_get_name(source) obs.obs_property_list_add_string(p, name, name) + elseif source_id == "vlc_source" then + local name = obs.obs_source_get_name(source) + obs.obs_property_list_add_string(p, name, name) + else + -- obs.script_log(obs.LOG_INFO, source_id) end end end diff --git a/UI/frontend-plugins/frontend-tools/output-timer.cpp b/UI/frontend-plugins/frontend-tools/output-timer.cpp index f6de097..c889f4f 100644 --- a/UI/frontend-plugins/frontend-tools/output-timer.cpp +++ b/UI/frontend-plugins/frontend-tools/output-timer.cpp @@ -18,6 +18,8 @@ OutputTimer::OutputTimer(QWidget *parent) { ui->setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QObject::connect(ui->outputTimerStream, SIGNAL(clicked()), this, SLOT(StreamingTimerButton())); QObject::connect(ui->outputTimerRecord, SIGNAL(clicked()), this, diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index 364757e..160032b 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.cpp +++ b/UI/frontend-plugins/frontend-tools/scripts.cpp @@ -230,6 +230,15 @@ void ScriptsTool::ReloadScript(const char *path) const char *script_path = obs_script_get_path(script); if (strcmp(script_path, path) == 0) { obs_script_reload(script); + + OBSData settings = obs_data_create(); + obs_data_release(settings); + + obs_properties_t *prop = + obs_script_get_properties(script); + obs_properties_apply_settings(prop, settings); + obs_properties_destroy(prop); + break; } } @@ -317,6 +326,14 @@ void ScriptsTool::on_addScripts_clicked() QListWidgetItem *item = new QListWidgetItem(script_file); item->setData(Qt::UserRole, QString(file)); ui->scripts->addItem(item); + + OBSData settings = obs_data_create(); + obs_data_release(settings); + + obs_properties_t *prop = + obs_script_get_properties(script); + obs_properties_apply_settings(prop, settings); + obs_properties_destroy(prop); } } } diff --git a/UI/frontend-plugins/frontend-tools/scripts.hpp b/UI/frontend-plugins/frontend-tools/scripts.hpp index f638c3f..e6e5560 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.hpp +++ b/UI/frontend-plugins/frontend-tools/scripts.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include diff --git a/UI/hotkey-edit.cpp b/UI/hotkey-edit.cpp index e76dc37..30af64a 100644 --- a/UI/hotkey-edit.cpp +++ b/UI/hotkey-edit.cpp @@ -272,13 +272,17 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx) edit->setToolTip(toolTip); auto revert = new QPushButton; - revert->setProperty("themeID", "hotkeyButtons"); - revert->setText(QTStr("Revert")); + revert->setProperty("themeID", "revertIcon"); + revert->setToolTip(QTStr("Revert")); + revert->setFixedSize(24, 24); + revert->setFlat(true); revert->setEnabled(false); auto clear = new QPushButton; - clear->setProperty("themeID", "hotkeyButtons"); - clear->setText(QTStr("Clear")); + clear->setProperty("themeID", "trashIcon"); + clear->setToolTip(QTStr("Clear")); + clear->setFixedSize(24, 24); + clear->setFlat(true); clear->setEnabled(!obs_key_combination_is_empty(combo)); QObject::connect(edit, &OBSHotkeyEdit::KeyChanged, @@ -289,15 +293,15 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx) }); auto add = new QPushButton; - add->setProperty("themeID", "hotkeyButtons"); - add->setText("+"); - add->setMinimumWidth(50); + add->setProperty("themeID", "addIconSmall"); + add->setFixedSize(24, 24); + add->setFlat(true); auto remove = new QPushButton; - remove->setProperty("themeID", "hotkeyButtons"); - remove->setText("-"); + remove->setProperty("themeID", "removeIconSmall"); remove->setEnabled(removeButtons.size() > 0); - remove->setMinimumWidth(50); + remove->setFixedSize(24, 24); + remove->setFlat(true); auto CurrentIndex = [&, remove] { @@ -320,6 +324,7 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx) }); QHBoxLayout *subLayout = new QHBoxLayout; + subLayout->setContentsMargins(0, 4, 0, 0); subLayout->addWidget(edit); subLayout->addWidget(revert); subLayout->addWidget(clear); diff --git a/UI/hotkey-edit.hpp b/UI/hotkey-edit.hpp index 3299e28..e7e7422 100644 --- a/UI/hotkey-edit.hpp +++ b/UI/hotkey-edit.hpp @@ -15,6 +15,8 @@ along with this program. If not, see . ******************************************************************************/ +#pragma once + #include #include #include diff --git a/UI/installer/mp-installer.nsi b/UI/installer/mp-installer.nsi index 06802c8..b46cf07 100644 --- a/UI/installer/mp-installer.nsi +++ b/UI/installer/mp-installer.nsi @@ -42,6 +42,10 @@ RequestExecutionLevel admin !define MUI_FINISHPAGE_RUN_TEXT "Launch OBS Studio ${SHORTVERSION}" !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchOBS" +; GPL is not an EULA, no need to agree to it. +!define MUI_LICENSEPAGE_BUTTON $(^NextBtn) +!define MUI_LICENSEPAGE_TEXT_BOTTOM "You are now aware of your rights. Click Next to continue." + !define MUI_PAGE_CUSTOMFUNCTION_LEAVE PreReqCheck !insertmacro MUI_PAGE_WELCOME diff --git a/UI/locked-checkbox.cpp b/UI/locked-checkbox.cpp deleted file mode 100644 index 91a049f..0000000 --- a/UI/locked-checkbox.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include -#include "locked-checkbox.hpp" - -#include - -LockedCheckBox::LockedCheckBox() : QCheckBox() -{ - lockedImage = - QPixmap::fromImage(QImage(":/res/images/locked_mask.png")); - unlockedImage = - QPixmap::fromImage(QImage(":/res/images/unlocked_mask.png")); - setMinimumSize(16, 16); - - setStyleSheet("outline: none;"); -} - -void LockedCheckBox::paintEvent(QPaintEvent *event) -{ - UNUSED_PARAMETER(event); - - QPixmap &pixmap = isChecked() ? lockedImage : unlockedImage; - QImage image(pixmap.size(), QImage::Format_ARGB32); - - QPainter draw(&image); - draw.setCompositionMode(QPainter::CompositionMode_Source); - draw.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap); - draw.setCompositionMode(QPainter::CompositionMode_SourceIn); - draw.fillRect(QRectF(QPointF(0.0f, 0.0f), pixmap.size()), - palette().color(foregroundRole())); - - QPainter p(this); - p.drawPixmap(0, 0, image.width(), image.height(), - QPixmap::fromImage(image)); -} diff --git a/UI/locked-checkbox.hpp b/UI/locked-checkbox.hpp index 017470f..0eb2d82 100644 --- a/UI/locked-checkbox.hpp +++ b/UI/locked-checkbox.hpp @@ -1,17 +1,7 @@ -#include -#include +#pragma once -class QPaintEvernt; +#include class LockedCheckBox : public QCheckBox { Q_OBJECT - - QPixmap lockedImage; - QPixmap unlockedImage; - -public: - LockedCheckBox(); - -protected: - void paintEvent(QPaintEvent *event) override; }; diff --git a/UI/obf.c b/UI/obf.c new file mode 100644 index 0000000..0ca8cc5 --- /dev/null +++ b/UI/obf.c @@ -0,0 +1,27 @@ +#include "obf.h" +#include + +#define LOWER_HALFBYTE(x) ((x) & 0xF) +#define UPPER_HALFBYTE(x) (((x) >> 4) & 0xF) + +void deobfuscate_str(char *str, uint64_t val) +{ + uint8_t *dec_val = (uint8_t*)&val; + int i = 0; + + while (*str != 0) { + int pos = i / 2; + bool bottom = (i % 2) == 0; + uint8_t *ch = (uint8_t*)str; + uint8_t xor = bottom ? + LOWER_HALFBYTE(dec_val[pos]) : + UPPER_HALFBYTE(dec_val[pos]); + + *ch ^= xor; + + if (++i == sizeof(uint64_t) * 2) + i = 0; + + str++; + } +} diff --git a/UI/obf.h b/UI/obf.h new file mode 100644 index 0000000..e264e19 --- /dev/null +++ b/UI/obf.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void deobfuscate_str(char *str, uint64_t val); + +#ifdef __cplusplus +} +#endif diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 52b15a0..a2c7ab4 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -39,7 +39,6 @@ #include "obs-app.hpp" #include "window-basic-main.hpp" #include "window-basic-settings.hpp" -#include "window-license-agreement.hpp" #include "crash-report.hpp" #include "platform.hpp" @@ -51,10 +50,13 @@ #include #else #include +#include #endif #include +#include "ui-config.h" + using namespace std; static log_handler_t def_log_handler; @@ -78,6 +80,9 @@ string opt_starting_collection; string opt_starting_profile; string opt_starting_scene; +bool remuxAfterRecord = false; +string remuxFilename; + // GPU hint exports for AMD/NVIDIA laptops #ifdef _MSC_VER extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1; @@ -416,10 +421,12 @@ bool OBSApp::InitGlobalConfigDefaults() "ShowListboxToolbars", true); config_set_default_bool(globalConfig, "BasicWindow", "ShowStatusBar", true); + config_set_default_bool(globalConfig, "BasicWindow", + "StudioModeLabels", true); if (!config_get_bool(globalConfig, "General", "Pre21Defaults")) { config_set_default_string(globalConfig, "General", - "CurrentTheme", "Dark"); + "CurrentTheme", DEFAULT_THEME); } config_set_default_bool(globalConfig, "BasicWindow", @@ -709,6 +716,17 @@ bool OBSApp::InitGlobalConfig() changed = true; } + if (!config_has_user_value(globalConfig, "General", "Pre23Defaults")) { + uint32_t lastVersion = config_get_int(globalConfig, "General", + "LastVersion"); + bool useOldDefaults = lastVersion && + lastVersion < MAKE_SEMANTIC_VERSION(23, 0, 0); + + config_set_bool(globalConfig, "General", "Pre23Defaults", + useOldDefaults); + changed = true; + } + if (config_has_user_value(globalConfig, "BasicWindow", "MultiviewLayout")) { const char *layout = config_get_string(globalConfig, @@ -1011,18 +1029,21 @@ bool OBSApp::InitTheme() const char *themeName = config_get_string(globalConfig, "General", "CurrentTheme"); + if (!themeName) { /* Use deprecated "Theme" value if available */ themeName = config_get_string(globalConfig, "General", "Theme"); if (!themeName) - themeName = "Default"; + themeName = DEFAULT_THEME; + if (!themeName) + themeName = "Dark"; } - if (strcmp(themeName, "Default") != 0 && SetTheme(themeName)) - return true; + if (strcmp(themeName, "Default") == 0) + themeName = "System"; - return SetTheme("Default"); + return SetTheme(themeName); } OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store) @@ -1030,6 +1051,8 @@ OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store) profilerNameStore(store) { sleepInhibitor = os_inhibit_sleep_create("OBS Video/audio"); + + setWindowIcon(QIcon::fromTheme("obs", QIcon(":/res/images/obs.png"))); } OBSApp::~OBSApp() @@ -1159,6 +1182,20 @@ void OBSApp::AppInit() config_set_default_string(globalConfig, "Basic", "SceneCollectionFile", Str("Untitled")); + if (!config_has_user_value(globalConfig, "Basic", "Profile")) { + config_set_string(globalConfig, "Basic", "Profile", + Str("Untitled")); + config_set_string(globalConfig, "Basic", "ProfileDir", + Str("Untitled")); + } + + if (!config_has_user_value(globalConfig, "Basic", "SceneCollection")) { + config_set_string(globalConfig, "Basic", + "SceneCollection", Str("Untitled")); + config_set_string(globalConfig, "Basic", + "SceneCollectionFile", Str("Untitled")); + } + #ifdef _WIN32 bool disableAudioDucking = config_get_bool(globalConfig, "Audio", "DisableAudioDucking"); @@ -1212,60 +1249,59 @@ void OBSApp::EnableInFocusHotkeys(bool enable) ResetHotkeyState(applicationState() != Qt::ApplicationActive); } +Q_DECLARE_METATYPE(VoidFunc) + +void OBSApp::Exec(VoidFunc func) +{ + func(); +} + bool OBSApp::OBSInit() { ProfileScope("OBSApp::OBSInit"); - bool licenseAccepted = config_get_bool(globalConfig, "General", - "LicenseAccepted"); - OBSLicenseAgreement agreement(nullptr); + setAttribute(Qt::AA_UseHighDpiPixmaps); - if (licenseAccepted || agreement.exec() == QDialog::Accepted) { - if (!licenseAccepted) { - config_set_bool(globalConfig, "General", - "LicenseAccepted", true); - config_save(globalConfig); - } + qRegisterMetaType(); - if (!StartupOBS(locale.c_str(), GetProfilerNameStore())) - return false; + if (!StartupOBS(locale.c_str(), GetProfilerNameStore())) + return false; #ifdef _WIN32 - bool browserHWAccel = config_get_bool(globalConfig, "General", - "BrowserHWAccel"); + bool browserHWAccel = config_get_bool(globalConfig, "General", + "BrowserHWAccel"); - obs_data_t *settings = obs_data_create(); - obs_data_set_bool(settings, "BrowserHWAccel", browserHWAccel); - obs_apply_private_data(settings); - obs_data_release(settings); + obs_data_t *settings = obs_data_create(); + obs_data_set_bool(settings, "BrowserHWAccel", browserHWAccel); + obs_apply_private_data(settings); + obs_data_release(settings); - blog(LOG_INFO, "Browser Hardware Acceleration: %s", - browserHWAccel ? "true" : "false"); + blog(LOG_INFO, "Current Date/Time: %s", CurrentDateTimeString().c_str()); + + blog(LOG_INFO, "Browser Hardware Acceleration: %s", + browserHWAccel ? "true" : "false"); #endif - blog(LOG_INFO, "Portable mode: %s", - portable_mode ? "true" : "false"); + blog(LOG_INFO, "Portable mode: %s", + portable_mode ? "true" : "false"); - setQuitOnLastWindowClosed(false); + setQuitOnLastWindowClosed(false); - mainWindow = new OBSBasic(); + mainWindow = new OBSBasic(); - mainWindow->setAttribute(Qt::WA_DeleteOnClose, true); - connect(mainWindow, SIGNAL(destroyed()), this, SLOT(quit())); + mainWindow->setAttribute(Qt::WA_DeleteOnClose, true); + connect(mainWindow, SIGNAL(destroyed()), this, SLOT(quit())); - mainWindow->OBSInit(); + mainWindow->OBSInit(); - connect(this, &QGuiApplication::applicationStateChanged, - [this](Qt::ApplicationState state) - { - ResetHotkeyState( - state != Qt::ApplicationActive); - }); - ResetHotkeyState(applicationState() != Qt::ApplicationActive); - return true; - } else { - return false; - } + connect(this, &QGuiApplication::applicationStateChanged, + [this](Qt::ApplicationState state) + { + ResetHotkeyState( + state != Qt::ApplicationActive); + }); + ResetHotkeyState(applicationState() != Qt::ApplicationActive); + return true; } string OBSApp::GetVersionString() const @@ -1506,8 +1542,18 @@ string GenerateTimeDateFilename(const char *extension, bool noSpace) string GenerateSpecifiedFilename(const char *extension, bool noSpace, const char *format) { + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + bool autoRemux = config_get_bool(main->Config(), "Video", "AutoRemux"); + + if ((strcmp(extension, "mp4") == 0) && autoRemux) + extension = "mkv"; + BPtr filename = os_generate_formatted_filename(extension, !noSpace, format); + + remuxFilename = string(filename); + remuxAfterRecord = autoRemux; + return string(filename); } @@ -1646,6 +1692,10 @@ static int run_program(fstream &logFile, int argc, char *argv[]) ScopeProfiler prof{run_program_init}; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + QCoreApplication::addLibraryPath("."); OBSApp program(argc, argv, profilerNameStore.get()); @@ -2177,10 +2227,37 @@ static void upgrade_settings(void) os_closedir(dir); } +void ctrlc_handler (int s) { + UNUSED_PARAMETER(s); + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + main->close(); +} + int main(int argc, char *argv[]) { #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); + + struct sigaction sig_handler; + + sig_handler.sa_handler = ctrlc_handler; + sigemptyset(&sig_handler.sa_mask); + sig_handler.sa_flags = 0; + + sigaction(SIGINT, &sig_handler, NULL); + + + /* Block SIGPIPE in all threads, this can happen if a thread calls write on + a closed pipe. */ + sigset_t sigpipe_mask; + sigemptyset(&sigpipe_mask); + sigaddset(&sigpipe_mask, SIGPIPE); + sigset_t saved_mask; + if (pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &saved_mask) == -1) { + perror("pthread_sigmask"); + exit(1); + } #endif #ifdef _WIN32 @@ -2196,6 +2273,8 @@ int main(int argc, char *argv[]) move_to_xdg(); #endif + obs_set_cmdline_args(argc, argv); + for (int i = 1; i < argc; i++) { if (arg_is(argv[i], "--portable", "-p")) { portable_mode = true; diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index 9fd7448..2815331 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,8 @@ public: const char *disambiguation, int n) const override; }; +typedef std::function VoidFunc; + class OBSApp : public QApplication { Q_OBJECT @@ -166,6 +169,9 @@ public: translatorHooks.pop_front(); } +public slots: + void Exec(VoidFunc func); + signals: void StyleChanged(); }; @@ -197,6 +203,10 @@ static inline int GetProfilePath(char *path, size_t size, const char *file) } extern bool portable_mode; + +extern bool remuxAfterRecord; +extern std::string remuxFilename; + extern bool opt_start_streaming; extern bool opt_start_recording; extern bool opt_start_replaybuffer; diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index c3c933a..3fe84f4 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -71,6 +71,13 @@ void *obs_frontend_get_main_window_handle(void) : nullptr; } +void *obs_frontend_get_system_tray(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_system_tray() + : nullptr; +} + char **obs_frontend_get_scene_names(void) { if (!callbacks_valid()) @@ -125,6 +132,19 @@ void obs_frontend_set_current_transition(obs_source_t *transition) c->obs_frontend_set_current_transition(transition); } +int obs_frontend_get_transition_duration(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_transition_duration() + : 0; +} + +void obs_frontend_set_transition_duration(int duration) +{ + if (callbacks_valid()) + c->obs_frontend_set_transition_duration(duration); +} + char **obs_frontend_get_scene_collections(void) { if (!callbacks_valid()) @@ -249,6 +269,13 @@ void obs_frontend_add_tools_menu_item(const char *name, private_data); } +void *obs_frontend_add_dock(void *dock) +{ + return !!callbacks_valid() + ? c->obs_frontend_add_dock(dock) + : nullptr; +} + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) { @@ -388,6 +415,12 @@ void obs_frontend_set_preview_program_mode(bool enable) c->obs_frontend_set_preview_program_mode(enable); } +void obs_frontend_preview_program_trigger_transition(void) +{ + if (callbacks_valid()) + c->obs_frontend_preview_program_trigger_transition(); +} + void obs_frontend_set_preview_enabled(bool enable) { if (callbacks_valid()) diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index ae67494..52f0bee 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -82,6 +82,7 @@ static inline void obs_frontend_source_list_free( EXPORT void *obs_frontend_get_main_window(void); EXPORT void *obs_frontend_get_main_window_handle(void); +EXPORT void *obs_frontend_get_system_tray(void); EXPORT char **obs_frontend_get_scene_names(void); EXPORT void obs_frontend_get_scenes(struct obs_frontend_source_list *sources); @@ -92,6 +93,8 @@ EXPORT void obs_frontend_get_transitions( struct obs_frontend_source_list *sources); EXPORT obs_source_t *obs_frontend_get_current_transition(void); EXPORT void obs_frontend_set_current_transition(obs_source_t *transition); +EXPORT int obs_frontend_get_transition_duration(void); +EXPORT void obs_frontend_set_transition_duration(int duration); EXPORT char **obs_frontend_get_scene_collections(void); EXPORT char *obs_frontend_get_current_scene_collection(void); @@ -108,6 +111,9 @@ EXPORT void *obs_frontend_add_tools_menu_qaction(const char *name); EXPORT void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data); +/* takes QDockWidget and returns QAction */ +EXPORT void *obs_frontend_add_dock(void *dock); + typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data); @@ -168,6 +174,7 @@ EXPORT void obs_frontend_save_streaming_service(void); EXPORT bool obs_frontend_preview_program_mode_active(void); EXPORT void obs_frontend_set_preview_program_mode(bool enable); +EXPORT void obs_frontend_preview_program_trigger_transition(void); EXPORT void obs_frontend_set_preview_enabled(bool enable); EXPORT bool obs_frontend_preview_enabled(void); diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index b1a9064..3ed9283 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -9,6 +9,7 @@ struct obs_frontend_callbacks { virtual ~obs_frontend_callbacks() {} virtual void *obs_frontend_get_main_window(void)=0; virtual void *obs_frontend_get_main_window_handle(void)=0; + virtual void *obs_frontend_get_system_tray(void)=0; virtual void obs_frontend_get_scenes( struct obs_frontend_source_list *sources)=0; @@ -20,6 +21,8 @@ struct obs_frontend_callbacks { virtual obs_source_t *obs_frontend_get_current_transition(void)=0; virtual void obs_frontend_set_current_transition( obs_source_t *transition)=0; + virtual int obs_frontend_get_transition_duration(void)=0; + virtual void obs_frontend_set_transition_duration(int duration)=0; virtual void obs_frontend_get_scene_collections( std::vector &strings)=0; @@ -50,6 +53,8 @@ struct obs_frontend_callbacks { virtual void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data)=0; + virtual void *obs_frontend_add_dock(void *dock)=0; + virtual void obs_frontend_add_event_callback( obs_frontend_event_cb callback, void *private_data)=0; virtual void obs_frontend_remove_event_callback( @@ -86,6 +91,8 @@ struct obs_frontend_callbacks { virtual bool obs_frontend_preview_program_mode_active(void)=0; virtual void obs_frontend_set_preview_program_mode(bool enable)=0; + virtual void obs_frontend_preview_program_trigger_transition(void)=0; + virtual bool obs_frontend_preview_enabled(void)=0; virtual void obs_frontend_set_preview_enabled(bool enable)=0; diff --git a/UI/obs.rc b/UI/obs.rc deleted file mode 100644 index 505bd21..0000000 --- a/UI/obs.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "../cmake/winrc/obs-studio.ico" diff --git a/UI/obs.rc.in b/UI/obs.rc.in new file mode 100644 index 0000000..523d41f --- /dev/null +++ b/UI/obs.rc.in @@ -0,0 +1,26 @@ +IDI_ICON1 ICON DISCARDABLE "../cmake/winrc/obs-studio.ico" + +1 VERSIONINFO +FILEVERSION ${UI_VERSION_MAJOR},${UI_VERSION_MINOR},${UI_VERSION_PATCH},0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "OBS" + VALUE "FileDescription", "OBS Studio" + VALUE "FileVersion", "${UI_VERSION}" + VALUE "InternalName", "obs" + VALUE "OriginalFilename", "obs" + VALUE "ProductName", "OBS Studio" + VALUE "ProductVersion", "${UI_VERSION}" + VALUE "Comments", "Free and open source software for video recording and live streaming" + VALUE "LegalCopyright", "(C) Hugh Bailey" + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04B0 + END +END \ No newline at end of file diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 4a286b6..a1f9ba8 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -18,7 +18,12 @@ #include #include #include +#include +#include #include "double-slider.hpp" +#include "slider-ignorewheel.hpp" +#include "spinbox-ignorewheel.hpp" +#include "combobox-ignorewheel.hpp" #include "qt-wrappers.hpp" #include "properties-view.hpp" #include "properties-view.moc.hpp" @@ -147,6 +152,8 @@ void OBSPropertiesView::RefreshProperties() QLabel *noPropertiesLabel = new QLabel(NO_PROPERTIES_STRING); layout->addWidget(noPropertiesLabel); } + + emit PropertiesRefreshed(); } void OBSPropertiesView::SetScrollPos(int h, int v) @@ -320,7 +327,7 @@ void OBSPropertiesView::AddInt(obs_property_t *prop, QFormLayout *layout, const char *name = obs_property_name(prop); int val = (int)obs_data_get_int(settings, name); - QSpinBox *spin = new QSpinBox(); + QSpinBox *spin = new SpinBoxIgnoreScroll(); if (!obs_property_enabled(prop)) spin->setEnabled(false); @@ -328,18 +335,20 @@ void OBSPropertiesView::AddInt(obs_property_t *prop, QFormLayout *layout, int minVal = obs_property_int_min(prop); int maxVal = obs_property_int_max(prop); int stepVal = obs_property_int_step(prop); + const char *suffix = obs_property_int_suffix(prop); spin->setMinimum(minVal); spin->setMaximum(maxVal); spin->setSingleStep(stepVal); spin->setValue(val); spin->setToolTip(QT_UTF8(obs_property_long_description(prop))); + spin->setSuffix(QT_UTF8(suffix)); WidgetInfo *info = new WidgetInfo(this, prop, spin); children.emplace_back(info); if (type == OBS_NUMBER_SLIDER) { - QSlider *slider = new QSlider(); + QSlider *slider = new SliderIgnoreScroll(); slider->setMinimum(minVal); slider->setMaximum(maxVal); slider->setPageStep(stepVal); @@ -377,12 +386,14 @@ void OBSPropertiesView::AddFloat(obs_property_t *prop, QFormLayout *layout, double minVal = obs_property_float_min(prop); double maxVal = obs_property_float_max(prop); double stepVal = obs_property_float_step(prop); + const char *suffix = obs_property_float_suffix(prop); spin->setMinimum(minVal); spin->setMaximum(maxVal); spin->setSingleStep(stepVal); spin->setValue(val); spin->setToolTip(QT_UTF8(obs_property_long_description(prop))); + spin->setSuffix(QT_UTF8(suffix)); WidgetInfo *info = new WidgetInfo(this, prop, spin); children.emplace_back(info); @@ -480,7 +491,7 @@ static string from_obs_data_autoselect(obs_data_t *data, const char *name, QWidget *OBSPropertiesView::AddList(obs_property_t *prop, bool &warning) { const char *name = obs_property_name(prop); - QComboBox *combo = new QComboBox(); + QComboBox *combo = new ComboBoxIgnoreScroll(); obs_combo_type type = obs_property_list_type(prop); obs_combo_format format = obs_property_list_format(prop); size_t count = obs_property_list_item_count(prop); @@ -912,7 +923,7 @@ static QWidget *CreateSimpleFPSValues(OBSFrameRatePropertyWidget *fpsProps, auto items = vector{}; items.reserve(sizeof(common_fps)/sizeof(common_frame_rate)); - auto combo = fpsProps->simpleFPS = new QComboBox{}; + auto combo = fpsProps->simpleFPS = new ComboBoxIgnoreScroll{}; combo->addItem("", QVariant::fromValue(make_fps(0, 0))); for (const auto &fps : common_fps) { @@ -992,7 +1003,7 @@ static QWidget *CreateRationalFPS(OBSFrameRatePropertyWidget *fpsProps, auto str = QTStr("Basic.PropertiesView.FPS.ValidFPSRanges"); auto rlabel = new QLabel{str}; - auto combo = fpsProps->fpsRange = new QComboBox{}; + auto combo = fpsProps->fpsRange = new ComboBoxIgnoreScroll{}; auto convert_fps = media_frames_per_second_to_fps; //auto convert_fi = media_frames_per_second_to_frame_interval; @@ -1013,8 +1024,8 @@ static QWidget *CreateRationalFPS(OBSFrameRatePropertyWidget *fpsProps, layout->addRow(rlabel, combo); - auto num_edit = fpsProps->numEdit = new QSpinBox{}; - auto den_edit = fpsProps->denEdit = new QSpinBox{}; + auto num_edit = fpsProps->numEdit = new SpinBoxIgnoreScroll{}; + auto den_edit = fpsProps->denEdit = new SpinBoxIgnoreScroll{}; num_edit->setRange(0, INT_MAX); den_edit->setRange(0, INT_MAX); @@ -1043,7 +1054,7 @@ static OBSFrameRatePropertyWidget *CreateFrameRateWidget(obs_property_t *prop, swap(widget->fps_ranges, fps_ranges); - auto combo = widget->modeSelect = new QComboBox{}; + auto combo = widget->modeSelect = new ComboBoxIgnoreScroll{}; combo->addItem(QTStr("Basic.PropertiesView.FPS.Simple"), QVariant::fromValue(frame_rate_tag::simple())); combo->addItem(QTStr("Basic.PropertiesView.FPS.Rational"), @@ -1328,6 +1339,44 @@ void OBSPropertiesView::AddFrameRate(obs_property_t *prop, bool &warning, }); } +void OBSPropertiesView::AddGroup(obs_property_t *prop, QFormLayout *layout) +{ + const char *name = obs_property_name(prop); + bool val = obs_data_get_bool(settings, name); + const char *desc = obs_property_description(prop); + enum obs_group_type type = obs_property_group_type(prop); + + // Create GroupBox + QGroupBox *groupBox = new QGroupBox(QT_UTF8(desc)); + groupBox->setCheckable(type == OBS_GROUP_CHECKABLE); + groupBox->setChecked(groupBox->isCheckable() ? val : true); + groupBox->setAccessibleName("group"); + groupBox->setEnabled(obs_property_enabled(prop)); + + // Create Layout and build content + QFormLayout *subLayout = new QFormLayout(); + subLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + groupBox->setLayout(subLayout); + + obs_properties_t *content = obs_property_group_content(prop); + obs_property_t *el = obs_properties_first(content); + while (el != nullptr) { + AddProperty(el, subLayout); + obs_property_next(&el); + } + + // Insert into UI + layout->setWidget(layout->rowCount(), + QFormLayout::ItemRole::SpanningRole, groupBox); + + // Register Group Widget + WidgetInfo *info = new WidgetInfo(this, prop, groupBox); + children.emplace_back(info); + + // Signals + connect(groupBox, SIGNAL(toggled()), info, SLOT(ControlChanged())); +} + void OBSPropertiesView::AddProperty(obs_property_t *property, QFormLayout *layout) { @@ -1377,6 +1426,8 @@ void OBSPropertiesView::AddProperty(obs_property_t *property, case OBS_PROPERTY_FRAME_RATE: AddFrameRate(property, warning, layout, label); break; + case OBS_PROPERTY_GROUP: + AddGroup(property, layout); } if (widget && !obs_property_enabled(property)) @@ -1384,7 +1435,8 @@ void OBSPropertiesView::AddProperty(obs_property_t *property, if (!label && type != OBS_PROPERTY_BOOL && - type != OBS_PROPERTY_BUTTON) + type != OBS_PROPERTY_BUTTON && + type != OBS_PROPERTY_GROUP) label = new QLabel(QT_UTF8(obs_property_description(property))); if (warning && label) //TODO: select color based on background color @@ -1625,8 +1677,7 @@ bool WidgetInfo::ColorChanged(const char *setting) long long val = obs_data_get_int(view->settings, setting); QColor color = color_from_int(val); - QColorDialog::ColorDialogOptions options = - QColorDialog::ShowAlphaChannel; + QColorDialog::ColorDialogOptions options = 0; /* The native dialog on OSX has all kinds of problems, like closing * other open QDialogs on exit, and @@ -1703,6 +1754,13 @@ bool WidgetInfo::FontChanged(const char *setting) return true; } +void WidgetInfo::GroupChanged(const char *setting) +{ + QGroupBox *groupbox = static_cast(widget); + obs_data_set_bool(view->settings, setting, + groupbox->isCheckable() ? groupbox->isChecked() : true); +} + void WidgetInfo::EditableListChanged() { const char *setting = obs_property_name(property); @@ -1772,6 +1830,7 @@ void WidgetInfo::ControlChanged() if (!FrameRateChanged(widget, setting, view->settings)) return; break; + case OBS_PROPERTY_GROUP: GroupChanged(setting); return; } if (view->callback && !view->deferUpdate) @@ -1977,9 +2036,21 @@ void WidgetInfo::EditListEdit() QListWidgetItem *item = selectedItems[0]; if (type == OBS_EDITABLE_LIST_TYPE_FILES) { - QString path = QFileDialog::getOpenFileName( + QDir pathDir(item->text()); + QString path; + + if (pathDir.exists()) + path = QFileDialog::getExistingDirectory( + App()->GetMainWindow(), + QTStr("Browse"), + item->text(), + QFileDialog::ShowDirsOnly | + QFileDialog::DontResolveSymlinks); + else + path = QFileDialog::getOpenFileName( App()->GetMainWindow(), QTStr("Browse"), item->text(), QT_UTF8(filter)); + if (path.isEmpty()) return; diff --git a/UI/properties-view.hpp b/UI/properties-view.hpp index 4a153c7..ee9d255 100644 --- a/UI/properties-view.hpp +++ b/UI/properties-view.hpp @@ -33,6 +33,7 @@ private: void ListChanged(const char *setting); bool ColorChanged(const char *setting); bool FontChanged(const char *setting); + void GroupChanged(const char *setting); void EditableListChanged(); void ButtonClicked(); @@ -103,6 +104,8 @@ private: void AddFrameRate(obs_property_t *prop, bool &warning, QFormLayout *layout, QLabel *&label); + void AddGroup(obs_property_t *prop, QFormLayout *layout); + void AddProperty(obs_property_t *property, QFormLayout *layout); void resizeEvent(QResizeEvent *event) override; @@ -118,6 +121,7 @@ public slots: signals: void PropertiesResized(); void Changed(); + void PropertiesRefreshed(); public: OBSPropertiesView(OBSData settings, void *obj, diff --git a/UI/qt-display.cpp b/UI/qt-display.cpp index 90bd9be..f2ca597 100644 --- a/UI/qt-display.cpp +++ b/UI/qt-display.cpp @@ -6,6 +6,27 @@ #include #include +static inline long long color_to_int(QColor color) +{ + auto shift = [&](unsigned val, int shift) + { + return ((val & 0xff) << shift); + }; + + return shift(color.red(), 0) | + shift(color.green(), 8) | + shift(color.blue(), 16) | + shift(color.alpha(), 24); +} + +static inline QColor rgba_to_color(uint32_t rgba) +{ + return QColor::fromRgb(rgba & 0xFF, + (rgba >> 8) & 0xFF, + (rgba >> 16) & 0xFF, + (rgba >> 24) & 0xFF); +} + OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags) { @@ -41,6 +62,26 @@ OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) connect(windowHandle(), &QWindow::screenChanged, sizeChanged); } +QColor OBSQTDisplay::GetDisplayBackgroundColor() const +{ + return rgba_to_color(backgroundColor); +} + +void OBSQTDisplay::SetDisplayBackgroundColor(const QColor &color) +{ + uint32_t newBackgroundColor = (uint32_t)color_to_int(color); + + if (newBackgroundColor != backgroundColor) { + backgroundColor = newBackgroundColor; + UpdateDisplayBackgroundColor(); + } +} + +void OBSQTDisplay::UpdateDisplayBackgroundColor() +{ + obs_display_set_background_color(display, backgroundColor); +} + void OBSQTDisplay::CreateDisplay() { if (display || !windowHandle()->isExposed()) @@ -56,7 +97,7 @@ void OBSQTDisplay::CreateDisplay() QTToGSWindow(winId(), info.window); - display = obs_display_create(&info); + display = obs_display_create(&info, backgroundColor); emit DisplayCreated(this); } diff --git a/UI/qt-display.hpp b/UI/qt-display.hpp index 438100b..de4fdac 100644 --- a/UI/qt-display.hpp +++ b/UI/qt-display.hpp @@ -3,8 +3,13 @@ #include #include +#define GREY_COLOR_BACKGROUND 0xFF4C4C4C + class OBSQTDisplay : public QWidget { Q_OBJECT + Q_PROPERTY(QColor displayBackgroundColor MEMBER backgroundColor + READ GetDisplayBackgroundColor + WRITE SetDisplayBackgroundColor) OBSDisplay display; @@ -18,9 +23,16 @@ signals: void DisplayResized(); public: - OBSQTDisplay(QWidget *parent = 0, Qt::WindowFlags flags = 0); + OBSQTDisplay(QWidget *parent = nullptr, + Qt::WindowFlags flags = nullptr); virtual QPaintEngine *paintEngine() const override; inline obs_display_t *GetDisplay() const {return display;} + + uint32_t backgroundColor = GREY_COLOR_BACKGROUND; + + QColor GetDisplayBackgroundColor() const; + void SetDisplayBackgroundColor(const QColor &color); + void UpdateDisplayBackgroundColor(); }; diff --git a/UI/qt-wrappers.cpp b/UI/qt-wrappers.cpp index eac5ea0..5d8a9cb 100644 --- a/UI/qt-wrappers.cpp +++ b/UI/qt-wrappers.cpp @@ -19,6 +19,7 @@ #include "obs-app.hpp" #include +#include #include #include #include @@ -89,6 +90,33 @@ void OBSMessageBox::information( mb.exec(); } +void OBSMessageBox::warning( + QWidget *parent, + const QString &title, + const QString &text, + bool enableRichText) +{ + QMessageBox mb(QMessageBox::Warning, + title, text, QMessageBox::Ok, + parent); + if (enableRichText) + mb.setTextFormat(Qt::RichText); + mb.setButtonText(QMessageBox::Ok, QTStr("OK")); + mb.exec(); +} + +void OBSMessageBox::critical( + QWidget *parent, + const QString &title, + const QString &text) +{ + QMessageBox mb(QMessageBox::Critical, + title, text, QMessageBox::Ok, + parent); + mb.setButtonText(QMessageBox::Ok, QTStr("OK")); + mb.exec(); +} + void QTToGSWindow(WId windowId, gs_window &gswindow) { #ifdef _WIN32 @@ -203,3 +231,87 @@ void DeleteLayout(QLayout *layout) delete layout; } + +class QuickThread : public QThread { +public: + explicit inline QuickThread(std::function func_) + : func(func_) + {} + +private: + virtual void run() override + { + func(); + } + + std::function func; +}; + +QThread *CreateQThread(std::function func) +{ + return new QuickThread(func); +} + +volatile long insideEventLoop = 0; + +void ExecuteFuncSafeBlock(std::function func) +{ + QEventLoop eventLoop; + + auto wait = [&] () + { + func(); + QMetaObject::invokeMethod(&eventLoop, "quit", + Qt::QueuedConnection); + }; + + os_atomic_inc_long(&insideEventLoop); + QScopedPointer thread(CreateQThread(wait)); + thread->start(); + eventLoop.exec(); + thread->wait(); + os_atomic_dec_long(&insideEventLoop); +} + +void ExecuteFuncSafeBlockMsgBox( + std::function func, + const QString &title, + const QString &text) +{ + QMessageBox dlg; + dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowCloseButtonHint); + dlg.setWindowTitle(title); + dlg.setText(text); + dlg.setStandardButtons(0); + + auto wait = [&] () + { + func(); + QMetaObject::invokeMethod(&dlg, "accept", Qt::QueuedConnection); + }; + + os_atomic_inc_long(&insideEventLoop); + QScopedPointer thread(CreateQThread(wait)); + thread->start(); + dlg.exec(); + thread->wait(); + os_atomic_dec_long(&insideEventLoop); +} + +static bool enable_message_boxes = false; + +void EnableThreadedMessageBoxes(bool enable) +{ + enable_message_boxes = enable; +} + +void ExecThreadedWithoutBlocking( + std::function func, + const QString &title, + const QString &text) +{ + if (!enable_message_boxes) + ExecuteFuncSafeBlock(func); + else + ExecuteFuncSafeBlockMsgBox(func, title, text); +} diff --git a/UI/qt-wrappers.hpp b/UI/qt-wrappers.hpp index 5273f92..ab30109 100644 --- a/UI/qt-wrappers.hpp +++ b/UI/qt-wrappers.hpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -47,6 +48,15 @@ public: QWidget *parent, const QString &title, const QString &text); + static void warning( + QWidget *parent, + const QString &title, + const QString &text, + bool enableRichText = false); + static void critical( + QWidget *parent, + const QString &title, + const QString &text); }; void OBSErrorBox(QWidget *parent, const char *msg, ...); @@ -64,6 +74,22 @@ QDataStream &operator>>(QDataStream &in, OBSScene &scene); QDataStream &operator<<(QDataStream &out, const OBSSceneItem &si); QDataStream &operator>>(QDataStream &in, OBSSceneItem &si); +QThread *CreateQThread(std::function func); + +void ExecuteFuncSafeBlock(std::function func); +void ExecuteFuncSafeBlockMsgBox( + std::function func, + const QString &title, + const QString &text); + +/* allows executing without message boxes if starting up, otherwise with a + * message box */ +void EnableThreadedMessageBoxes(bool enable); +void ExecThreadedWithoutBlocking( + std::function func, + const QString &title, + const QString &text); + class SignalBlocker { QWidget *widget; bool blocked; diff --git a/UI/remote-text.cpp b/UI/remote-text.cpp index e4b5c39..41525c9 100644 --- a/UI/remote-text.cpp +++ b/UI/remote-text.cpp @@ -61,7 +61,11 @@ void RemoteTextThread::run() contentTypeString.c_str()); } + for (std::string &h : extraHeaders) + header = curl_slist_append(header, h.c_str()); + curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl.get(), CURLOPT_ACCEPT_ENCODING, ""); curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header); curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, @@ -71,6 +75,10 @@ void RemoteTextThread::run() curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str); + if (timeoutSec) + curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, + timeoutSec); + #if LIBCURL_VERSION_NUM >= 0x072400 // A lot of servers don't yet support ALPN curl_easy_setopt(curl.get(), CURLOPT_SSL_ENABLE_ALPN, 0); @@ -118,7 +126,8 @@ bool GetRemoteFile( const char *contentType, const char *postData, std::vector extraHeaders, - std::string *signature) + std::string *signature, + int timeoutSec) { vector header_in_list; char error_in[CURL_ERROR_SIZE]; @@ -151,6 +160,7 @@ bool GetRemoteFile( header = curl_slist_append(header, h.c_str()); curl_easy_setopt(curl.get(), CURLOPT_URL, url); + curl_easy_setopt(curl.get(), CURLOPT_ACCEPT_ENCODING, ""); curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header); curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, @@ -166,6 +176,10 @@ bool GetRemoteFile( &header_in_list); } + if (timeoutSec) + curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, + timeoutSec); + #if LIBCURL_VERSION_NUM >= 0x072400 // A lot of servers don't yet support ALPN curl_easy_setopt(curl.get(), CURLOPT_SSL_ENABLE_ALPN, 0); diff --git a/UI/remote-text.hpp b/UI/remote-text.hpp index 7758843..d5663e1 100644 --- a/UI/remote-text.hpp +++ b/UI/remote-text.hpp @@ -28,6 +28,10 @@ class RemoteTextThread : public QThread { std::string contentType; std::string postData; + std::vector extraHeaders; + + int timeoutSec = 0; + void run() override; signals: @@ -37,8 +41,25 @@ public: inline RemoteTextThread( std::string url_, std::string contentType_ = std::string(), - std::string postData_ = std::string()) - : url(url_), contentType(contentType_), postData(postData_) + std::string postData_ = std::string(), + int timeoutSec_ = 0) + : url (url_), + contentType (contentType_), + postData (postData_), + timeoutSec (timeoutSec_) + {} + + inline RemoteTextThread( + std::string url_, + std::vector &&extraHeaders_, + std::string contentType_ = std::string(), + std::string postData_ = std::string(), + int timeoutSec_ = 0) + : url (url_), + contentType (contentType_), + postData (postData_), + extraHeaders (std::move(extraHeaders_)), + timeoutSec (timeoutSec_) {} }; @@ -50,4 +71,5 @@ bool GetRemoteFile( const char *contentType = nullptr, const char *postData = nullptr, std::vector extraHeaders = std::vector(), - std::string *signature = nullptr); + std::string *signature = nullptr, + int timeoutSec = 0); diff --git a/UI/slider-ignorewheel.cpp b/UI/slider-ignorewheel.cpp new file mode 100644 index 0000000..f2708e9 --- /dev/null +++ b/UI/slider-ignorewheel.cpp @@ -0,0 +1,22 @@ +#include "slider-ignorewheel.hpp" + +SliderIgnoreScroll::SliderIgnoreScroll(QWidget *parent) : QSlider(parent) +{ + setFocusPolicy(Qt::StrongFocus); +} + +SliderIgnoreScroll::SliderIgnoreScroll(Qt::Orientation orientation, + QWidget *parent) + : QSlider(parent) +{ + setFocusPolicy(Qt::StrongFocus); + setOrientation(orientation); +} + +void SliderIgnoreScroll::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QSlider::wheelEvent(event); +} diff --git a/UI/slider-ignorewheel.hpp b/UI/slider-ignorewheel.hpp new file mode 100644 index 0000000..7778c90 --- /dev/null +++ b/UI/slider-ignorewheel.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +class SliderIgnoreScroll : public QSlider { + Q_OBJECT + +public: + SliderIgnoreScroll(QWidget *parent = nullptr); + SliderIgnoreScroll(Qt::Orientation orientation, QWidget *parent = nullptr); + +protected: + virtual void wheelEvent(QWheelEvent *event) override; +}; diff --git a/UI/source-label.hpp b/UI/source-label.hpp index 03b9be6..6bba24e 100644 --- a/UI/source-label.hpp +++ b/UI/source-label.hpp @@ -15,6 +15,8 @@ along with this program. If not, see . ******************************************************************************/ +#pragma once + #include #include diff --git a/UI/source-list-widget.cpp b/UI/source-list-widget.cpp deleted file mode 100644 index d537fbc..0000000 --- a/UI/source-list-widget.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include - -#include - -#include "qt-wrappers.hpp" -#include "source-list-widget.hpp" - -Q_DECLARE_METATYPE(OBSSceneItem); - -void SourceListWidget::mouseDoubleClickEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) - QListWidget::mouseDoubleClickEvent(event); -} - -void SourceListWidget::dropEvent(QDropEvent *event) -{ - QListWidget::dropEvent(event); - if (!event->isAccepted() || !count()) - return; - - auto GetSceneItem = [&](int i) - { - return item(i)->data(Qt::UserRole).value(); - }; - - std::vector newOrder; - newOrder.reserve(count()); - for (int i = count() - 1; i >= 0; i--) - newOrder.push_back(GetSceneItem(i)); - - auto UpdateOrderAtomically = [&](obs_scene_t *scene) - { - ignoreReorder = true; - obs_scene_reorder_items(scene, newOrder.data(), - newOrder.size()); - ignoreReorder = false; - }; - using UpdateOrderAtomically_t = decltype(UpdateOrderAtomically); - - auto scene = obs_sceneitem_get_scene(GetSceneItem(0)); - obs_scene_atomic_update(scene, [](void *data, obs_scene_t *scene) - { - (*static_cast(data))(scene); - }, static_cast(&UpdateOrderAtomically)); -} diff --git a/UI/source-list-widget.hpp b/UI/source-list-widget.hpp deleted file mode 100644 index d2215b0..0000000 --- a/UI/source-list-widget.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -class QMouseEvent; - -class SourceListWidget : public QListWidget { - Q_OBJECT - - bool ignoreReorder = false; -public: - inline SourceListWidget(QWidget *parent = nullptr) - : QListWidget(parent) - { - } - - bool IgnoreReorder() const { return ignoreReorder; } - -protected: - virtual void mouseDoubleClickEvent(QMouseEvent *event) override; - virtual void dropEvent(QDropEvent *event) override; -}; diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 1652988..e46ad54 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -5,6 +5,7 @@ #include "visibility-checkbox.hpp" #include "locked-checkbox.hpp" #include "expand-checkbox.hpp" +#include "platform.hpp" #include #include @@ -35,6 +36,7 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) sceneitem (sceneitem_) { setAttribute(Qt::WA_TranslucentBackground); + setMouseTracking(true); obs_source_t *source = obs_sceneitem_get_source(sceneitem); const char *name = obs_source_get_name(source); @@ -58,13 +60,15 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) vis = new VisibilityCheckBox(); vis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - vis->setMaximumSize(16, 16); + vis->setFixedSize(16, 16); vis->setChecked(obs_sceneitem_visible(sceneitem)); + vis->setStyleSheet("background: none"); lock = new LockedCheckBox(); lock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - lock->setMaximumSize(16, 16); + lock->setFixedSize(16, 16); lock->setChecked(obs_sceneitem_locked(sceneitem)); + lock->setStyleSheet("background: none"); label = new QLabel(QT_UTF8(name)); label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); @@ -77,10 +81,11 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) #endif boxLayout = new QHBoxLayout(); - boxLayout->setContentsMargins(1, 1, 2, 1); + boxLayout->setContentsMargins(1, 1, 1, 1); boxLayout->setSpacing(1); boxLayout->addWidget(label); boxLayout->addWidget(vis); + boxLayout->setSpacing(2); boxLayout->addWidget(lock); #ifdef __APPLE__ /* Hack: Fixes a bug where scrollbars would be above the lock icon */ @@ -129,6 +134,12 @@ void SourceTreeItem::DisconnectSignals() removeSignal.Disconnect(); } +void SourceTreeItem::Clear() +{ + DisconnectSignals(); + sceneitem = nullptr; +} + void SourceTreeItem::ReconnectSignals() { if (!sceneitem) @@ -150,10 +161,8 @@ void SourceTreeItem::ReconnectSignals() Q_ARG(OBSSceneItem, curItem)); curItem = nullptr; } - if (!curItem) { - this_->DisconnectSignals(); - this_->sceneitem = nullptr; - } + if (!curItem) + QMetaObject::invokeMethod(this_, "Clear"); }; auto itemVisible = [] (void *data, calldata_t *cd) @@ -244,11 +253,18 @@ void SourceTreeItem::mouseDoubleClickEvent(QMouseEvent *event) } } +bool SourceTreeItem::IsEditing() +{ + return editor != nullptr; +} + void SourceTreeItem::EnterEditMode() { setFocusPolicy(Qt::StrongFocus); boxLayout->removeWidget(label); editor = new QLineEdit(label->text()); + editor->setStyleSheet("background: none"); + editor->selectAll(); editor->installEventFilter(this); boxLayout->insertWidget(1, editor); setFocusProxy(editor); @@ -690,8 +706,14 @@ int SourceTreeModel::rowCount(const QModelIndex &parent) const return parent.isValid() ? 0 : items.count(); } -QVariant SourceTreeModel::data(const QModelIndex &, int) const +QVariant SourceTreeModel::data(const QModelIndex &index, int role) const { + if (role == Qt::AccessibleTextRole) { + OBSSceneItem item = items[index.row()]; + obs_source_t *source = obs_sceneitem_get_source(item); + return QVariant(QT_UTF8(obs_source_get_name(source))); + } + return QVariant(); } @@ -896,6 +918,12 @@ SourceTree::SourceTree(QWidget *parent_) : QListView(parent_) "*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}" \ "*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}" \ "*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}")); + + setMouseTracking(true); + + UpdateNoSourcesMessage(); + connect(App(), &OBSApp::StyleChanged, + this, &SourceTree::UpdateNoSourcesMessage); } void SourceTree::ResetWidgets() @@ -1249,6 +1277,24 @@ void SourceTree::dropEvent(QDropEvent *event) QListView::dropEvent(event); } +void SourceTree::mouseMoveEvent(QMouseEvent *event) +{ + QPoint pos = event->pos(); + SourceTreeItem *item = qobject_cast(childAt(pos)); + + OBSBasicPreview *preview = OBSBasicPreview::Get(); + preview->hoveredListItem = !!item ? item->sceneitem : nullptr; + + QListView::mouseMoveEvent(event); +} + +void SourceTree::leaveEvent(QEvent *event) +{ + OBSBasicPreview *preview = OBSBasicPreview::Get(); + preview->hoveredListItem = nullptr; + QListView::leaveEvent(event); +} + void SourceTree::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected) @@ -1279,10 +1325,14 @@ void SourceTree::Edit(int row) if (row < 0 || row >= stm->items.count()) return; - QWidget *widget = indexWidget(stm->createIndex(row, 0)); + QModelIndex index = stm->createIndex(row, 0); + QWidget *widget = indexWidget(index); SourceTreeItem *itemWidget = reinterpret_cast(widget); + if (itemWidget->IsEditing()) + return; + itemWidget->EnterEditMode(); - edit(stm->createIndex(row, 0)); + edit(index); } bool SourceTree::MultipleBaseSelected() const @@ -1388,3 +1438,56 @@ void SourceTree::AddGroup() { GetStm()->AddGroup(); } + +void SourceTree::UpdateNoSourcesMessage() +{ + std::string darkPath; + GetDataFilePath("themes/Dark/no_sources.svg", darkPath); + + QColor color = palette().text().color(); + bool lightTheme = (color.redF() < 0.5); + QString file = lightTheme + ? ":res/images/no_sources.svg" + : darkPath.c_str(); + iconNoSources.load(file); + + QTextOption opt(Qt::AlignHCenter); + opt.setWrapMode(QTextOption::WordWrap); + textNoSources.setTextOption(opt); + textNoSources.setText(QTStr("NoSources.Label").replace("\n", "
")); + + textPrepared = false; +} + +void SourceTree::paintEvent(QPaintEvent *event) +{ + SourceTreeModel *stm = GetStm(); + if (stm && !stm->items.count()) { + QPainter p(viewport()); + + if (!textPrepared) { + textNoSources.prepare(QTransform(), p.font()); + textPrepared = true; + } + + QRectF iconRect = iconNoSources.viewBoxF(); + + QSizeF iconSize = iconRect.size(); + QSizeF textSize = textNoSources.size(); + QSizeF thisSize = size(); + + qreal totalHeight = textSize.height() + iconSize.height(); + + qreal x = thisSize.width() / 2.0 - textSize.width() / 2.0; + qreal y = thisSize.height() / 2.0 - totalHeight / 2.0; + p.drawStaticText(x, y, textNoSources); + + x = thisSize.width() / 2.0 - iconSize.width() / 2.0; + y += textSize.height(); + iconRect.moveTo(x, y); + + iconNoSources.render(&p, iconRect); + } else { + QListView::paintEvent(event); + } +} diff --git a/UI/source-tree.hpp b/UI/source-tree.hpp index f77ae90..46577d2 100644 --- a/UI/source-tree.hpp +++ b/UI/source-tree.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include class QLabel; @@ -47,6 +49,7 @@ class SourceTreeItem : public QWidget { public: explicit SourceTreeItem(SourceTree *tree, OBSSceneItem sceneitem); + bool IsEditing(); private: QSpacerItem *spacer = nullptr; @@ -71,6 +74,8 @@ private: virtual void paintEvent(QPaintEvent* event) override; private slots: + void Clear(); + void EnterEditMode(); void ExitEditMode(bool save); @@ -130,6 +135,12 @@ class SourceTree : public QListView { friend class SourceTreeModel; friend class SourceTreeItem; + bool textPrepared = false; + QStaticText textNoSources; + QSvgRenderer iconNoSources; + + void UpdateNoSourcesMessage(); + void ResetWidgets(); void UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item); void UpdateWidgets(bool force = false); @@ -172,6 +183,9 @@ public slots: protected: virtual void mouseDoubleClickEvent(QMouseEvent *event) override; virtual void dropEvent(QDropEvent *event) override; + virtual void mouseMoveEvent(QMouseEvent *event) override; + virtual void leaveEvent(QEvent *event) override; + virtual void paintEvent(QPaintEvent *event) override; virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; }; diff --git a/UI/spinbox-ignorewheel.cpp b/UI/spinbox-ignorewheel.cpp new file mode 100644 index 0000000..0452b93 --- /dev/null +++ b/UI/spinbox-ignorewheel.cpp @@ -0,0 +1,14 @@ +#include "spinbox-ignorewheel.hpp" + +SpinBoxIgnoreScroll::SpinBoxIgnoreScroll(QWidget *parent) : QSpinBox(parent) +{ + setFocusPolicy(Qt::StrongFocus); +} + +void SpinBoxIgnoreScroll::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QSpinBox::wheelEvent(event); +} diff --git a/UI/spinbox-ignorewheel.hpp b/UI/spinbox-ignorewheel.hpp new file mode 100644 index 0000000..535197e --- /dev/null +++ b/UI/spinbox-ignorewheel.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +class SpinBoxIgnoreScroll : public QSpinBox { + Q_OBJECT + +public: + SpinBoxIgnoreScroll(QWidget *parent = nullptr); + +protected: + virtual void wheelEvent(QWheelEvent *event) override; +}; diff --git a/UI/ui-config.h.in b/UI/ui-config.h.in new file mode 100644 index 0000000..fe31138 --- /dev/null +++ b/UI/ui-config.h.in @@ -0,0 +1,31 @@ +#pragma once + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef ON +#define ON 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef OFF +#define OFF 0 +#endif + +#define TWITCH_ENABLED @TWITCH_ENABLED@ +#define TWITCH_CLIENTID "@TWITCH_CLIENTID@" +#define TWITCH_HASH 0x@TWITCH_HASH@ + +#define MIXER_ENABLED @MIXER_ENABLED@ +#define MIXER_CLIENTID "@MIXER_CLIENTID@" +#define MIXER_HASH 0x@MIXER_HASH@ + +#define RESTREAM_ENABLED @RESTREAM_ENABLED@ +#define RESTREAM_CLIENTID "@RESTREAM_CLIENTID@" +#define RESTREAM_HASH 0x@RESTREAM_HASH@ + +#define DEFAULT_THEME "Dark" diff --git a/UI/visibility-checkbox.cpp b/UI/visibility-checkbox.cpp deleted file mode 100644 index 5b73084..0000000 --- a/UI/visibility-checkbox.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include -#include "visibility-checkbox.hpp" - -#include - -VisibilityCheckBox::VisibilityCheckBox() : QCheckBox() -{ - checkedImage = - QPixmap::fromImage(QImage(":/res/images/visible_mask.png")); - uncheckedImage = - QPixmap::fromImage(QImage(":/res/images/invisible_mask.png")); - setMinimumSize(16, 16); - - setStyleSheet("outline: none;"); -} - -void VisibilityCheckBox::paintEvent(QPaintEvent *event) -{ - UNUSED_PARAMETER(event); - - QPixmap &pixmap = isChecked() ? checkedImage : uncheckedImage; - QImage image(pixmap.size(), QImage::Format_ARGB32); - - QPainter draw(&image); - draw.setCompositionMode(QPainter::CompositionMode_Source); - draw.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap); - draw.setCompositionMode(QPainter::CompositionMode_SourceIn); - draw.fillRect(QRectF(QPointF(0.0f, 0.0f), pixmap.size()), - palette().color(foregroundRole())); - - QPainter p(this); - p.drawPixmap(0, 0, image.width(), image.height(), - QPixmap::fromImage(image)); -} diff --git a/UI/visibility-checkbox.hpp b/UI/visibility-checkbox.hpp index 47c5d89..ff21df2 100644 --- a/UI/visibility-checkbox.hpp +++ b/UI/visibility-checkbox.hpp @@ -1,17 +1,7 @@ -#include -#include +#pragma once -class QPaintEvernt; +#include class VisibilityCheckBox : public QCheckBox { Q_OBJECT - - QPixmap checkedImage; - QPixmap uncheckedImage; - -public: - VisibilityCheckBox(); - -protected: - void paintEvent(QPaintEvent *event) override; }; diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 80fbe1f..91f1032 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -2,11 +2,11 @@ #include "qt-wrappers.hpp" #include "obs-app.hpp" #include "mute-checkbox.hpp" +#include "slider-ignorewheel.hpp" #include "slider-absoluteset-style.hpp" #include #include #include -#include #include #include #include @@ -14,6 +14,7 @@ using namespace std; #define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) +#define FADER_PRECISION 4096.0 QWeakPointer VolumeMeter::updateTimer; @@ -47,7 +48,8 @@ void VolControl::OBSVolumeMuted(void *data, calldata_t *calldata) void VolControl::VolumeChanged() { slider->blockSignals(true); - slider->setValue((int) (obs_fader_get_deflection(obs_fader) * 100.0f)); + slider->setValue((int) (obs_fader_get_deflection(obs_fader) * + FADER_PRECISION)); slider->blockSignals(false); updateText(); @@ -66,7 +68,7 @@ void VolControl::SetMuted(bool checked) void VolControl::SliderChanged(int vol) { - obs_fader_set_deflection(obs_fader, float(vol) * 0.01f); + obs_fader_set_deflection(obs_fader, float(vol) / FADER_PRECISION); updateText(); } @@ -116,13 +118,14 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) : source (std::move(source_)), levelTotal (0.0f), levelCount (0.0f), - obs_fader (obs_fader_create(OBS_FADER_CUBIC)), + obs_fader (obs_fader_create(OBS_FADER_LOG)), obs_volmeter (obs_volmeter_create(OBS_FADER_LOG)), vertical (vertical) { nameLabel = new QLabel(); volLabel = new QLabel(); mute = new MuteCheckBox(); + QString sourceName = obs_source_get_name(source); setObjectName(sourceName); @@ -153,7 +156,7 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) QHBoxLayout *meterLayout = new QHBoxLayout; volMeter = new VolumeMeter(nullptr, obs_volmeter, true); - slider = new QSlider(Qt::Vertical); + slider = new SliderIgnoreScroll(Qt::Vertical); nameLayout->setAlignment(Qt::AlignCenter); meterLayout->setAlignment(Qt::AlignCenter); @@ -188,6 +191,8 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) mainLayout->addItem(meterLayout); mainLayout->addItem(controlLayout); + volMeter->setFocusProxy(slider); + setMaximumWidth(110); } else { QHBoxLayout *volLayout = new QHBoxLayout; @@ -195,7 +200,7 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) QHBoxLayout *botLayout = new QHBoxLayout; volMeter = new VolumeMeter(nullptr, obs_volmeter, false); - slider = new QSlider(Qt::Horizontal); + slider = new SliderIgnoreScroll(Qt::Horizontal); textLayout->setContentsMargins(0, 0, 0, 0); textLayout->addWidget(nameLabel); @@ -217,6 +222,8 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) mainLayout->addItem(textLayout); mainLayout->addWidget(volMeter); mainLayout->addItem(botLayout); + + volMeter->setFocusProxy(slider); } setLayout(mainLayout); @@ -227,8 +234,9 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) nameLabel->setText(sourceName); nameLabel->setFont(font); volLabel->setFont(font); + slider->setMinimum(0); - slider->setMaximum(100); + slider->setMaximum(int(FADER_PRECISION)); bool muted = obs_source_muted(source); mute->setChecked(muted); @@ -498,6 +506,16 @@ void VolumeMeter::setPeakMeterType(enum obs_peak_meter_type peakMeterType) } } +void VolumeMeter::mousePressEvent(QMouseEvent *event) +{ + setFocus(Qt::MouseFocusReason); +} + +void VolumeMeter::wheelEvent(QWheelEvent *event) +{ + QApplication::sendEvent(focusProxy(), event); +} + VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter, bool vertical) : QWidget(parent), obs_volmeter(obs_volmeter), @@ -528,6 +546,8 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter, peakHoldDuration = 20.0; // 20 seconds inputPeakHoldDuration = 1.0; // 1 second + channels = (int)audio_output_get_channels(obs_get_audio()); + handleChannelCofigurationChange(); updateTimerRef = updateTimer.toStrongRef(); if (!updateTimerRef) { @@ -1007,16 +1027,22 @@ void VolumeMeter::paintEvent(QPaintEvent *event) for (int channelNr = 0; channelNr < displayNrAudioChannels; channelNr++) { + + int channelNrFixed = (displayNrAudioChannels == 1 && + channels > 2) + ? 2 + : channelNr; + if (vertical) paintVMeter(painter, channelNr * 4, 8, 3, height - 10, - displayMagnitude[channelNr], - displayPeak[channelNr], - displayPeakHold[channelNr]); + displayMagnitude[channelNrFixed], + displayPeak[channelNrFixed], + displayPeakHold[channelNrFixed]); else paintHMeter(painter, 5, channelNr * 4, width - 5, 3, - displayMagnitude[channelNr], - displayPeak[channelNr], - displayPeakHold[channelNr]); + displayMagnitude[channelNrFixed], + displayPeak[channelNrFixed], + displayPeakHold[channelNrFixed]); if (idle) continue; @@ -1026,10 +1052,10 @@ void VolumeMeter::paintEvent(QPaintEvent *event) // having too much visual impact. if (vertical) paintInputMeter(painter, channelNr * 4, 3, 3, 3, - displayInputPeakHold[channelNr]); + displayInputPeakHold[channelNrFixed]); else paintInputMeter(painter, 0, channelNr * 4, 3, 3, - displayInputPeakHold[channelNr]); + displayInputPeakHold[channelNrFixed]); } lastRedrawTime = ts; diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index fde0d12..e24ef5c 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -144,6 +144,7 @@ private: qreal inputPeakHoldDuration; uint64_t lastRedrawTime = 0; + int channels = 0; bool clipping = false; bool vertical; @@ -197,6 +198,8 @@ public: qreal getInputPeakHoldDuration() const; void setInputPeakHoldDuration(qreal v); void setPeakMeterType(enum obs_peak_meter_type peakMeterType); + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void wheelEvent(QWheelEvent *event) override; protected: void paintEvent(QPaintEvent *event) override; diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 65b8a1c..e7cfd82 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -28,11 +28,12 @@ set(updater_SOURCES http.cpp hash.cpp updater.rc + updater.manifest ) add_definitions(-DNOMINMAX -DUNICODE -D_UNICODE) if(MSVC) - add_compile_options("$<$:/MT>") + add_compile_options($,/MTd,/MT>) endif() add_executable(updater WIN32 diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index f3735cb..56d3812 100644 --- a/UI/win-update/updater/updater.cpp +++ b/UI/win-update/updater/updater.cpp @@ -576,10 +576,18 @@ static inline bool FileExists(const wchar_t *path) static bool NonCorePackageInstalled(const char *name) { - if (strcmp(name, "obs-browser") == 0) { - return FileExists(L"obs-plugins\\32bit\\obs-browser.dll"); - } else if (strcmp(name, "realsense") == 0) { - return FileExists(L"obs-plugins\\32bit\\win-ivcam.dll"); + if (is32bit) { + if (strcmp(name, "obs-browser") == 0) { + return FileExists(L"obs-plugins\\32bit\\obs-browser.dll"); + } else if (strcmp(name, "realsense") == 0) { + return FileExists(L"obs-plugins\\32bit\\win-ivcam.dll"); + } + } else { + if (strcmp(name, "obs-browser") == 0) { + return FileExists(L"obs-plugins\\64bit\\obs-browser.dll"); + } else if (strcmp(name, "realsense") == 0) { + return FileExists(L"obs-plugins\\64bit\\win-ivcam.dll"); + } } return false; @@ -1133,6 +1141,12 @@ static bool Update(wchar_t *cmdLine) SetDlgItemTextW(hwndMain, IDC_STATUS, L"Searching for available updates..."); + HWND hProgress = GetDlgItem(hwndMain, IDC_PROGRESS); + LONG_PTR style = GetWindowLongPtr(hProgress, GWL_STYLE); + SetWindowLongPtr(hProgress, GWL_STYLE, style | PBS_MARQUEE); + + SendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETMARQUEE, 1, 0); + /* ------------------------------------- * * Check if updating portable build */ @@ -1237,16 +1251,20 @@ static bool Update(wchar_t *cmdLine) for (size_t i = 0; i < packageCount; i++) { if (!AddPackageUpdateFiles(packages, i, tempPath)) { - Status(L"Failed to process update packages"); + Status(L"Update failed: Failed to process update packages"); return false; } } + SendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETMARQUEE, 0, 0); + SetWindowLongPtr(hProgress, GWL_STYLE, style); + /* ------------------------------------- * * Exit if updates already installed */ if (!updates.size()) { Status(L"All available updates are already installed."); + SetDlgItemText(hwndMain, IDC_BUTTON, L"Launch OBS"); return true; } @@ -1452,8 +1470,10 @@ static DWORD WINAPI UpdateThread(void *arg) if (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) Status(L"Update aborted."); - SendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETSTATE, - PBST_ERROR, 0); + HWND hProgress = GetDlgItem(hwndMain, IDC_PROGRESS); + LONG_PTR style = GetWindowLongPtr(hProgress, GWL_STYLE); + SetWindowLongPtr(hProgress, GWL_STYLE, style & ~PBS_MARQUEE); + SendMessage(hProgress, PBM_SETSTATE, PBST_ERROR, 0); SetDlgItemText(hwndMain, IDC_BUTTON, L"Exit"); EnableWindow(GetDlgItem(hwndMain, IDC_BUTTON), true); diff --git a/UI/win-update/updater/updater.manifest b/UI/win-update/updater/updater.manifest new file mode 100644 index 0000000..d486163 --- /dev/null +++ b/UI/win-update/updater/updater.manifest @@ -0,0 +1,25 @@ + + + + + + system + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UI/win-update/win-update-helpers.cpp b/UI/win-update/win-update-helpers.cpp index b969e32..98cffbc 100644 --- a/UI/win-update/win-update-helpers.cpp +++ b/UI/win-update/win-update-helpers.cpp @@ -21,7 +21,7 @@ std::string vstrprintf(const char *format, va_list args) return std::string(); std::string str; - int size = (int)vsnprintf(nullptr, 0, format, args); + int size = (int)vsnprintf(nullptr, 0, format, args) + 1; str.resize(size); vsnprintf(&str[0], size, format, args); return str; diff --git a/UI/win-update/win-update.cpp b/UI/win-update/win-update.cpp index 2bd77b8..34d2157 100644 --- a/UI/win-update/win-update.cpp +++ b/UI/win-update/win-update.cpp @@ -242,7 +242,7 @@ try { return true; } catch (string text) { - blog(LOG_WARNING, "%s: %s", __FUNCTION__, text.c_str()); + blog(LOG_DEBUG, "%s: %s", __FUNCTION__, text.c_str()); return false; } @@ -586,7 +586,7 @@ try { auto ActiveOrGameCaptureLocked = [this] () { - if (video_output_active(obs_get_video())) { + if (obs_video_active()) { if (manualUpdate) info(QTStr("Updater.Running.Title"), QTStr("Updater.Running.Text")); diff --git a/UI/window-basic-about.cpp b/UI/window-basic-about.cpp new file mode 100644 index 0000000..eb9ca7b --- /dev/null +++ b/UI/window-basic-about.cpp @@ -0,0 +1,169 @@ +#include "window-basic-about.hpp" +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" +#include "remote-text.hpp" +#include +#include +#include +#include + +using namespace json11; + +OBSAbout::OBSAbout(QWidget *parent) + : QDialog(parent), + ui(new Ui::OBSAbout) +{ + ui->setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QString bitness; + QString ver; + + if(sizeof(void*) == 4) + bitness = " (32 bit)"; + else if(sizeof(void*) == 8) + bitness = " (64 bit)"; + +#ifdef HAVE_OBSCONFIG_H + ver += OBS_VERSION; +#else + ver += LIBOBS_API_MAJOR_VER + "." + + LIBOBS_API_MINOR_VER + "." + + LIBOBS_API_PATCH_VER; +#endif + + ui->version->setText(ver + bitness); + + ui->contribute->setText(QTStr("About.Contribute")); + ui->donate->setText("  " + + QTStr("About.Donate") + ""); + ui->donate->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui->donate->setOpenExternalLinks(true); + + ui->getInvolved->setText("  " + + QTStr("About.GetInvolved") + ""); + ui->getInvolved->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui->getInvolved->setOpenExternalLinks(true); + + ui->about->setText("" + QTStr("About") + ""); + ui->authors->setText("" + QTStr("About.Authors") + ""); + ui->license->setText("" + QTStr("About.License") + ""); + + ui->name->setProperty("themeID", "aboutName"); + ui->version->setProperty("themeID", "aboutVersion"); + ui->about->setProperty("themeID", "aboutHLayout"); + ui->authors->setProperty("themeID", "aboutHLayout"); + ui->license->setProperty("themeID", "aboutHLayout"); + ui->info->setProperty("themeID", "aboutInfo"); + + connect(ui->about, SIGNAL(clicked()), this, SLOT(ShowAbout())); + connect(ui->authors, SIGNAL(clicked()), this, SLOT(ShowAuthors())); + connect(ui->license, SIGNAL(clicked()), this, SLOT(ShowLicense())); + + QPointer about(this); + + OBSBasic *main = OBSBasic::Get(); + if (main->patronJson.empty() && !main->patronJsonThread) { + RemoteTextThread *thread = new RemoteTextThread( + "https://obsproject.com/patreon/about-box.json", + "application/json"); + QObject::connect(thread, &RemoteTextThread::Result, + main, &OBSBasic::UpdatePatronJson); + QObject::connect(thread, SIGNAL(Result(const QString &, const QString &)), + this, SLOT(ShowAbout())); + main->patronJsonThread.reset(thread); + thread->start(); + } else { + ShowAbout(); + } +} + +void OBSAbout::ShowAbout() +{ + OBSBasic *main = OBSBasic::Get(); + + if (main->patronJson.empty()) + return; + + std::string error; + Json json = Json::parse(main->patronJson, error); + const Json::array &patrons = json.array_items(); + QString text; + + text += "

Top Patreon contributors:

"; + text += "

"; + bool first = true; + bool top = true; + + for (const Json &patron : patrons) { + std::string name = patron["name"].string_value(); + std::string link = patron["link"].string_value(); + int amount = patron["amount"].int_value(); + + if (top && amount < 10000) { + text += "

"; + top = false; + } else if (!first) { + text += "
"; + } + + if (!link.empty()) { + text += ""; + } + text += QT_UTF8(name.c_str()).toHtmlEscaped(); + if (!link.empty()) + text += ""; + + if (first) + first = false; + } + + ui->textBrowser->setHtml(text); +} + +void OBSAbout::ShowAuthors() +{ + std::string path; + QString error = "Error! File could not be read.\n\n \ + Go to: https://github.com/obsproject/obs-studio/blob/master/AUTHORS"; + + if (!GetDataFilePath("authors/AUTHORS", path)) { + ui->textBrowser->setPlainText(error); + return; + } + + ui->textBrowser->setPlainText(QString::fromStdString(path)); + + BPtr text = os_quick_read_utf8_file(path.c_str()); + + if (!text || !*text) { + ui->textBrowser->setPlainText(error); + return; + } + + ui->textBrowser->setPlainText(QT_UTF8(text)); +} + +void OBSAbout::ShowLicense() +{ + std::string path; + QString error = "Error! File could not be read.\n\n \ + Go to: https://github.com/obsproject/obs-studio/blob/master/COPYING"; + + if (!GetDataFilePath("license/gplv2.txt", path)) { + ui->textBrowser->setPlainText(error); + return; + } + + BPtr text = os_quick_read_utf8_file(path.c_str()); + + if (!text || !*text) { + ui->textBrowser->setPlainText(error); + return; + } + + ui->textBrowser->setPlainText(QT_UTF8(text)); +} diff --git a/UI/window-basic-about.hpp b/UI/window-basic-about.hpp new file mode 100644 index 0000000..bb507fe --- /dev/null +++ b/UI/window-basic-about.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#include "ui_OBSAbout.h" + +class OBSAbout : public QDialog { + Q_OBJECT + +public: + explicit OBSAbout(QWidget *parent = 0); + + std::unique_ptr ui; + +private slots: + void ShowAbout(); + void ShowAuthors(); + void ShowLicense(); +}; diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index e6b47f7..03b4233 100644 --- a/UI/window-basic-adv-audio.cpp +++ b/UI/window-basic-adv-audio.cpp @@ -37,7 +37,7 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) label = new QLabel(QTStr("Basic.AdvAudio.Mono")); label->setAlignment(Qt::AlignHCenter); mainLayout->addWidget(label, 0, idx++); - label = new QLabel(QTStr("Basic.AdvAudio.Panning")); + label = new QLabel(QTStr("Basic.AdvAudio.Balance")); label->setAlignment(Qt::AlignHCenter); mainLayout->addWidget(label, 0, idx++); label = new QLabel(QTStr("Basic.AdvAudio.SyncOffset")); @@ -89,6 +89,7 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) resize(1000, 340); setWindowTitle(QTStr("Basic.AdvAudio")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setSizeGripEnabled(true); setWindowModality(Qt::NonModal); setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp index e79cc58..d4ba22c 100644 --- a/UI/window-basic-auto-config-test.cpp +++ b/UI/window-basic-auto-config-test.cpp @@ -219,6 +219,10 @@ void AutoConfigTestPage::TestBandwidthThread() string_depad_key(key); key += "?bandwidthtest"; } + else if(wiz->serviceName == "Restream.io" || wiz->serviceName == "Restream.io - RTMP") { + string_depad_key(key); + key += "?test=true"; + } obs_data_set_string(service_settings, "service", wiz->serviceName.c_str()); @@ -246,8 +250,9 @@ void AutoConfigTestPage::TestBandwidthThread() GetServers(servers); /* just use the first server if it only has one alternate server, - * or if using Mixer due to its "auto" server */ - if (servers.size() < 3 || wiz->serviceName == "Mixer.com - FTL") { + * or if using Mixer or Restream due to their "auto" servers */ + if (servers.size() < 3 || wiz->serviceName == "Mixer.com - FTL" || + wiz->serviceName.substr(0, 11)=="Restream.io") { servers.resize(1); } else if (wiz->service == AutoConfig::Service::Twitch && @@ -791,8 +796,17 @@ void AutoConfigTestPage::FindIdealHardwareResolution() if (!force && rate > maxDataRate) return; - int minBitrate = EstimateMinBitrate(cx, cy, fps_num, fps_den) - * 114 / 100; + AutoConfig::Encoder encType = wiz->streamingEncoder; + bool nvenc = encType == AutoConfig::Encoder::NVENC; + + int minBitrate = EstimateMinBitrate(cx, cy, fps_num, fps_den); + + /* most hardware encoders don't have a good quality to bitrate + * ratio, so increase the minimum bitrate estimate for them. + * NVENC currently is the exception because of the improvements + * its made to its quality in recent generations. */ + if (!nvenc) minBitrate = minBitrate * 114 / 100; + if (wiz->type == AutoConfig::Type::Recording) force = true; if (force || wiz->idealBitrate >= minBitrate) @@ -849,9 +863,6 @@ void AutoConfigTestPage::TestStreamEncoderThread() } } - if (preferHardware && !softwareTested && wiz->hardwareEncodingAvailable) - FindIdealHardwareResolution(); - if (!softwareTested) { if (wiz->nvencAvailable) wiz->streamingEncoder = AutoConfig::Encoder::NVENC; @@ -863,6 +874,9 @@ void AutoConfigTestPage::TestStreamEncoderThread() wiz->streamingEncoder = AutoConfig::Encoder::x264; } + if (preferHardware && !softwareTested && wiz->hardwareEncodingAvailable) + FindIdealHardwareResolution(); + QMetaObject::invokeMethod(this, "NextStage"); } diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index 7ddf2af..b3cc2b3 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -12,6 +12,17 @@ #include "ui_AutoConfigVideoPage.h" #include "ui_AutoConfigStreamPage.h" +#ifdef BROWSER_AVAILABLE +#include +#include "auth-oauth.hpp" +#endif + +struct QCef; +struct QCefCookieManager; + +extern QCef *cef; +extern QCefCookieManager *panel_cookies; + #define wiz reinterpret_cast(wizard()) /* ------------------------------------------------------------------------- */ @@ -210,6 +221,11 @@ bool AutoConfigVideoPage::validatePage() /* ------------------------------------------------------------------------- */ +enum class ListOpt : int { + ShowAll = 1, + Custom +}; + AutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) : QWizardPage (parent), ui (new Ui_AutoConfigStreamPage) @@ -217,17 +233,28 @@ AutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) ui->setupUi(this); ui->bitrateLabel->setVisible(false); ui->bitrate->setVisible(false); + ui->connectAccount2->setVisible(false); + ui->disconnectAccount->setVisible(false); - ui->streamType->addItem(obs_service_get_display_name("rtmp_common")); - ui->streamType->addItem(obs_service_get_display_name("rtmp_custom")); + int vertSpacing = ui->topLayout->verticalSpacing(); + + QMargins m = ui->topLayout->contentsMargins(); + m.setBottom(vertSpacing / 2); + ui->topLayout->setContentsMargins(m); + + m = ui->loginPageLayout->contentsMargins(); + m.setTop(vertSpacing / 2); + ui->loginPageLayout->setContentsMargins(m); + + m = ui->streamkeyPageLayout->contentsMargins(); + m.setTop(vertSpacing / 2); + ui->streamkeyPageLayout->setContentsMargins(m); 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 &)), @@ -238,8 +265,6 @@ AutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) 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())); @@ -270,12 +295,17 @@ int AutoConfigStreamPage::nextId() const return AutoConfig::TestPage; } +inline bool AutoConfigStreamPage::IsCustom() const +{ + return ui->service->currentData().toInt() == (int)ListOpt::Custom; +} + bool AutoConfigStreamPage::validatePage() { OBSData service_settings = obs_data_create(); obs_data_release(service_settings); - wiz->customServer = ui->streamType->currentIndex() == 1; + wiz->customServer = IsCustom(); const char *serverType = wiz->customServer ? "rtmp_custom" @@ -358,9 +388,99 @@ void AutoConfigStreamPage::on_show_clicked() } } +void AutoConfigStreamPage::OnOAuthStreamKeyConnected() +{ +#ifdef BROWSER_AVAILABLE + OAuthStreamKey *a = reinterpret_cast(auth.get()); + + if (a) { + bool validKey = !a->key().empty(); + + if (validKey) + ui->key->setText(QT_UTF8(a->key().c_str())); + + ui->streamKeyWidget->setVisible(!validKey); + ui->streamKeyLabel->setVisible(!validKey); + ui->connectAccount2->setVisible(!validKey); + ui->disconnectAccount->setVisible(validKey); + } + + ui->stackedWidget->setCurrentIndex((int)Section::StreamKey); + UpdateCompleted(); +#endif +} + +void AutoConfigStreamPage::OnAuthConnected() +{ + std::string service = QT_TO_UTF8(ui->service->currentText()); + Auth::Type type = Auth::AuthType(service); + + if (type == Auth::Type::OAuth_StreamKey) { + OnOAuthStreamKeyConnected(); + } +} + +void AutoConfigStreamPage::on_connectAccount_clicked() +{ +#ifdef BROWSER_AVAILABLE + std::string service = QT_TO_UTF8(ui->service->currentText()); + + auth = OAuthStreamKey::Login(this, service); + if (!!auth) + OnAuthConnected(); +#endif +} + +#define DISCONNECT_COMFIRM_TITLE \ + "Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title" +#define DISCONNECT_COMFIRM_TEXT \ + "Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text" + +void AutoConfigStreamPage::on_disconnectAccount_clicked() +{ + QMessageBox::StandardButton button; + + button = OBSMessageBox::question(this, + QTStr(DISCONNECT_COMFIRM_TITLE), + QTStr(DISCONNECT_COMFIRM_TEXT)); + + if (button == QMessageBox::No) { + return; + } + + OBSBasic *main = OBSBasic::Get(); + + main->auth.reset(); + auth.reset(); + + std::string service = QT_TO_UTF8(ui->service->currentText()); + +#ifdef BROWSER_AVAILABLE + OAuth::DeleteCookies(service); +#endif + + ui->streamKeyWidget->setVisible(true); + ui->streamKeyLabel->setVisible(true); + ui->connectAccount2->setVisible(true); + ui->disconnectAccount->setVisible(false); + ui->key->setText(""); +} + +void AutoConfigStreamPage::on_useStreamKey_clicked() +{ + ui->stackedWidget->setCurrentIndex((int)Section::StreamKey); + UpdateCompleted(); +} + +static inline bool is_auth_service(const std::string &service) +{ + return Auth::AuthType(service) != Auth::Type::None; +} + void AutoConfigStreamPage::ServiceChanged() { - bool showMore = ui->service->currentData().toBool(); + bool showMore = + ui->service->currentData().toInt() == (int)ListOpt::ShowAll; if (showMore) return; @@ -368,23 +488,43 @@ void AutoConfigStreamPage::ServiceChanged() bool regionBased = service == "Twitch" || service == "Smashcast"; bool testBandwidth = ui->doBandwidthTest->isChecked(); - bool custom = ui->streamType->currentIndex() == 1; + bool custom = IsCustom(); + + ui->disconnectAccount->setVisible(false); + +#ifdef BROWSER_AVAILABLE + if (cef) { + if (lastService != service.c_str()) { + bool can_auth = is_auth_service(service); + int page = can_auth + ? (int)Section::Connect + : (int)Section::StreamKey; + + ui->stackedWidget->setCurrentIndex(page); + ui->streamKeyWidget->setVisible(true); + ui->streamKeyLabel->setVisible(true); + ui->connectAccount2->setVisible(can_auth); + auth.reset(); + + if (lastService.isEmpty()) + lastService = service.c_str(); + } + } else { + ui->connectAccount2->setVisible(false); + } +#else + ui->connectAccount2->setVisible(false); +#endif /* Test three closest servers if "Auto" is available for Twitch */ if (service == "Twitch" && wiz->twitchAuto) regionBased = false; - 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); + ui->streamkeyPageLayout->removeWidget(ui->serverLabel); + ui->streamkeyPageLayout->removeWidget(ui->serverStackedWidget); if (custom) { - ui->formLayout->insertRow(1, ui->serverLabel, + ui->streamkeyPageLayout->insertRow(1, ui->serverLabel, ui->serverStackedWidget); ui->region->setVisible(false); @@ -392,10 +532,8 @@ void AutoConfigStreamPage::ServiceChanged() 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->streamkeyPageLayout->insertRow(2, ui->serverLabel, ui->serverStackedWidget); ui->region->setVisible(regionBased && testBandwidth); @@ -409,12 +547,22 @@ void AutoConfigStreamPage::ServiceChanged() ui->bitrateLabel->setHidden(testBandwidth); ui->bitrate->setHidden(testBandwidth); +#ifdef BROWSER_AVAILABLE + OBSBasic *main = OBSBasic::Get(); + + if (!!main->auth && + service.find(main->auth->service()) != std::string::npos) { + auth = main->auth; + OnAuthConnected(); + } +#endif + UpdateCompleted(); } void AutoConfigStreamPage::UpdateKeyLink() { - bool custom = ui->streamType->currentIndex() == 1; + bool custom = IsCustom(); QString serviceName = ui->service->currentText(); bool isYoutube = false; @@ -478,18 +626,22 @@ void AutoConfigStreamPage::LoadServices(bool showAll) for (QString &name : names) ui->service->addItem(name); + if (!showAll) { + ui->service->addItem( + QTStr("Basic.AutoConfig.StreamPage.Service.ShowAll"), + QVariant((int)ListOpt::ShowAll)); + } + + ui->service->insertItem(0, + QTStr("Basic.AutoConfig.StreamPage.Service.Custom"), + QVariant((int)ListOpt::Custom)); + 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); @@ -498,7 +650,8 @@ void AutoConfigStreamPage::LoadServices(bool showAll) void AutoConfigStreamPage::UpdateServerList() { QString serviceName = ui->service->currentText(); - bool showMore = ui->service->currentData().toBool(); + bool showMore = + ui->service->currentData().toInt() == (int)ListOpt::ShowAll; if (showMore) { LoadServices(true); @@ -533,10 +686,11 @@ void AutoConfigStreamPage::UpdateServerList() void AutoConfigStreamPage::UpdateCompleted() { - if (ui->key->text().isEmpty()) { + if (ui->stackedWidget->currentIndex() == (int)Section::Connect || + (ui->key->text().isEmpty() && !auth)) { ready = false; } else { - bool custom = ui->streamType->currentIndex() == 1; + bool custom = IsCustom(); if (custom) { ready = !ui->customServer->text().isEmpty(); } else { @@ -555,6 +709,8 @@ void AutoConfigStreamPage::UpdateCompleted() AutoConfig::AutoConfig(QWidget *parent) : QWizard(parent) { + EnableThreadedMessageBoxes(true); + calldata_t cd = {0}; calldata_set_int(&cd, "seconds", 5); @@ -572,13 +728,14 @@ AutoConfig::AutoConfig(QWidget *parent) #ifdef _WIN32 setWizardStyle(QWizard::ModernStyle); #endif - AutoConfigStreamPage *streamPage = new AutoConfigStreamPage(); + streamPage = new AutoConfigStreamPage(); setPage(StartPage, new AutoConfigStartPage()); setPage(VideoPage, new AutoConfigVideoPage()); setPage(StreamPage, streamPage); setPage(TestPage, new AutoConfigTestPage()); setWindowTitle(QTStr("Basic.AutoConfig")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); obs_video_info ovi; obs_get_video_info(&ovi); @@ -636,6 +793,7 @@ AutoConfig::AutoConfig(QWidget *parent) streamPage->UpdateServerList(); streamPage->UpdateKeyLink(); + streamPage->lastService.clear(); if (!customServer) { QComboBox *serverList = streamPage->ui->server; @@ -646,7 +804,9 @@ AutoConfig::AutoConfig(QWidget *parent) serverList->setCurrentIndex(idx); } else { streamPage->ui->customServer->setText(server.c_str()); - streamPage->ui->streamType->setCurrentIndex(1); + int idx = streamPage->ui->service->findData( + QVariant((int)ListOpt::Custom)); + streamPage->ui->service->setCurrentIndex(idx); } if (!key.empty()) @@ -656,12 +816,17 @@ AutoConfig::AutoConfig(QWidget *parent) 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; + } else { + /* Newer generations of NVENC have a high enough quality to + * bitrate ratio that if NVENC is available, it makes sense to + * just always prefer hardware encoding by default */ + bool preferHardware = nvencAvailable || + os_get_physical_cores() <= 4; + streamPage->ui->preferHardware->setChecked(preferHardware); } setOptions(0); @@ -676,6 +841,7 @@ AutoConfig::~AutoConfig() { OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); main->EnableOutputs(true); + EnableThreadedMessageBoxes(false); } void AutoConfig::TestHardwareEncoding() @@ -788,6 +954,9 @@ void AutoConfig::SaveStreamSettings() main->SetService(newService); main->SaveService(); + main->auth = streamPage->auth; + if (!!main->auth) + main->auth->LoadUI(); /* ---------------------------------- */ /* save stream settings */ diff --git a/UI/window-basic-auto-config.hpp b/UI/window-basic-auto-config.hpp index 3478df2..9426a75 100644 --- a/UI/window-basic-auto-config.hpp +++ b/UI/window-basic-auto-config.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,9 @@ class Ui_AutoConfigVideoPage; class Ui_AutoConfigStreamPage; class Ui_AutoConfigTestPage; +class AutoConfigStreamPage; +class Auth; + class AutoConfig : public QWizard { Q_OBJECT @@ -60,6 +64,8 @@ class AutoConfig : public QWizard { static inline const char *GetEncoderId(Encoder enc); + AutoConfigStreamPage *streamPage = nullptr; + Service service = Service::Other; Quality recordingQuality = Quality::Stream; Encoder recordingEncoder = Encoder::Stream; @@ -155,11 +161,19 @@ class AutoConfigStreamPage : public QWizardPage { friend class AutoConfig; + enum class Section : int { + Connect, + StreamKey, + }; + + std::shared_ptr auth; + Ui_AutoConfigStreamPage *ui; QString lastService; bool ready = false; void LoadServices(bool showAll); + inline bool IsCustom() const; public: AutoConfigStreamPage(QWidget *parent = nullptr); @@ -169,8 +183,14 @@ public: virtual int nextId() const override; virtual bool validatePage() override; + void OnAuthConnected(); + void OnOAuthStreamKeyConnected(); + public slots: void on_show_clicked(); + void on_connectAccount_clicked(); + void on_disconnectAccount_clicked(); + void on_useStreamKey_clicked(); void ServiceChanged(); void UpdateKeyLink(); void UpdateServerList(); diff --git a/UI/window-basic-filters.cpp b/UI/window-basic-filters.cpp index 564deac..fa2ef99 100644 --- a/UI/window-basic-filters.cpp +++ b/UI/window-basic-filters.cpp @@ -71,6 +71,15 @@ OBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_) const char *name = obs_source_get_name(source); setWindowTitle(QTStr("Basic.Filters.Title").arg(QT_UTF8(name))); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + +#ifndef QT_NO_SHORTCUT + ui->actionRemoveFilter->setShortcut(QApplication::translate("OBSBasicFilters", "Del", nullptr)); +#endif // QT_NO_SHORTCUT + + addAction(ui->actionRemoveFilter); + addAction(ui->actionMoveUp); + addAction(ui->actionMoveDown); installEventFilter(CreateShortcutFilter()); @@ -425,7 +434,7 @@ void OBSBasicFilters::AddNewFilter(const char *id) return; if (name.empty()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); AddNewFilter(id); @@ -435,7 +444,7 @@ void OBSBasicFilters::AddNewFilter(const char *id) existing_filter = obs_source_get_filter_by_name(source, name.c_str()); if (existing_filter) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); obs_source_release(existing_filter); @@ -507,20 +516,20 @@ void OBSBasicFilters::OBSSourceReordered(void *param, calldata_t *data) UNUSED_PARAMETER(data); } -void OBSBasicFilters::SourceRemoved(void *data, calldata_t *params) +void OBSBasicFilters::SourceRemoved(void *param, calldata_t *data) { - UNUSED_PARAMETER(params); + UNUSED_PARAMETER(data); - QMetaObject::invokeMethod(static_cast(data), + QMetaObject::invokeMethod(static_cast(param), "close"); } -void OBSBasicFilters::SourceRenamed(void *data, calldata_t *params) +void OBSBasicFilters::SourceRenamed(void *param, calldata_t *data) { - const char *name = calldata_string(params, "new_name"); + const char *name = calldata_string(data, "new_name"); QString title = QTStr("Basic.Filters.Title").arg(QT_UTF8(name)); - QMetaObject::invokeMethod(static_cast(data), + QMetaObject::invokeMethod(static_cast(param), "setWindowTitle", Q_ARG(QString, title)); } @@ -659,6 +668,30 @@ void OBSBasicFilters::on_effectFilters_currentRowChanged(int row) UpdatePropertiesView(row, false); } +void OBSBasicFilters::on_actionRemoveFilter_triggered() +{ + if (ui->asyncFilters->hasFocus()) + on_removeAsyncFilter_clicked(); + else if (ui->effectFilters->hasFocus()) + on_removeEffectFilter_clicked(); +} + +void OBSBasicFilters::on_actionMoveUp_triggered() +{ + if (ui->asyncFilters->hasFocus()) + on_moveAsyncFilterUp_clicked(); + else if (ui->effectFilters->hasFocus()) + on_moveEffectFilterUp_clicked(); +} + +void OBSBasicFilters::on_actionMoveDown_triggered() +{ + if (ui->asyncFilters->hasFocus()) + on_moveAsyncFilterDown_clicked(); + else if (ui->effectFilters->hasFocus()) + on_moveEffectFilterDown_clicked(); +} + void OBSBasicFilters::CustomContextMenu(const QPoint &pos, bool async) { QListWidget *list = async ? ui->asyncFilters : ui->effectFilters; diff --git a/UI/window-basic-filters.hpp b/UI/window-basic-filters.hpp index 9fdbce3..5ab28a0 100644 --- a/UI/window-basic-filters.hpp +++ b/UI/window-basic-filters.hpp @@ -100,6 +100,10 @@ private slots: void on_effectFilters_customContextMenuRequested(const QPoint &pos); void on_effectFilters_GotFocus(); + void on_actionRemoveFilter_triggered(); + void on_actionMoveUp_triggered(); + void on_actionMoveDown_triggered(); + void AsyncFilterNameEdited(QWidget *editor, QAbstractItemDelegate::EndEditHint endHint); void EffectFilterNameEdited(QWidget *editor, diff --git a/UI/window-basic-main-browser.cpp b/UI/window-basic-main-browser.cpp new file mode 100644 index 0000000..c29fe07 --- /dev/null +++ b/UI/window-basic-main-browser.cpp @@ -0,0 +1,164 @@ +/****************************************************************************** + Copyright (C) 2018 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 +#include +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" + +#include + +#ifdef BROWSER_AVAILABLE +#include +#endif + +struct QCef; +struct QCefCookieManager; + +extern QCef *cef; +extern QCefCookieManager *panel_cookies; + +static std::string GenId() +{ + std::random_device rd; + std::mt19937_64 e2(rd()); + std::uniform_int_distribution dist(0, 0xFFFFFFFFFFFFFFFF); + + uint64_t id = dist(e2); + + char id_str[20]; + snprintf(id_str, sizeof(id_str), "%16llX", (unsigned long long)id); + return std::string(id_str); +} + +void CheckExistingCookieId() +{ + OBSBasic *main = OBSBasic::Get(); + if (config_has_user_value(main->Config(), "Panels", "CookieId")) + return; + + config_set_string(main->Config(), "Panels", "CookieId", GenId().c_str()); +} + +#ifdef BROWSER_AVAILABLE +static void InitPanelCookieManager() +{ + if (!cef) + return; + if (panel_cookies) + return; + + CheckExistingCookieId(); + + OBSBasic *main = OBSBasic::Get(); + const char *cookie_id = config_get_string(main->Config(), + "Panels", "CookieId"); + + std::string sub_path; + sub_path += "obs_profile_cookies/"; + sub_path += cookie_id; + + panel_cookies = cef->create_cookie_manager(sub_path); +} +#endif + +void DestroyPanelCookieManager() +{ +#ifdef BROWSER_AVAILABLE + if (panel_cookies) { + panel_cookies->FlushStore(); + delete panel_cookies; + panel_cookies = nullptr; + } +#endif +} + +void DeleteCookies() +{ +#ifdef BROWSER_AVAILABLE + if (panel_cookies) { + panel_cookies->DeleteCookies("", ""); + } +#endif +} + +void DuplicateCurrentCookieProfile(ConfigFile &config) +{ +#ifdef BROWSER_AVAILABLE + if (cef) { + OBSBasic *main = OBSBasic::Get(); + std::string cookie_id = config_get_string(main->Config(), + "Panels", "CookieId"); + + std::string src_path; + src_path += "obs_profile_cookies/"; + src_path += cookie_id; + + std::string new_id = GenId(); + + std::string dst_path; + dst_path += "obs_profile_cookies/"; + dst_path += new_id; + + BPtr src_path_full = cef->get_cookie_path(src_path); + BPtr dst_path_full = cef->get_cookie_path(dst_path); + + QDir srcDir(src_path_full.Get()); + QDir dstDir(dst_path_full.Get()); + + if (srcDir.exists()) { + if (!dstDir.exists()) + dstDir.mkdir(dst_path_full.Get()); + + QStringList files = srcDir.entryList(QDir::Files); + for (const QString &file : files) { + QString src = QString(src_path_full); + QString dst = QString(dst_path_full); + src += QDir::separator() + file; + dst += QDir::separator() + file; + QFile::copy(src, dst); + } + } + + config_set_string(config, "Panels", "CookieId", + cookie_id.c_str()); + config_set_string(main->Config(), "Panels", "CookieId", + new_id.c_str()); + } +#else + UNUSED_PARAMETER(config); +#endif +} + +void OBSBasic::InitBrowserPanelSafeBlock() +{ +#ifdef BROWSER_AVAILABLE + if (!cef) + return; + if (cef->init_browser()) { + InitPanelCookieManager(); + return; + } + + ExecThreadedWithoutBlocking( + [] {cef->wait_for_browser_init();}, + QTStr("BrowserPanelInit.Title"), + QTStr("BrowserPanelInit.Text")); + InitPanelCookieManager(); +#endif +} diff --git a/UI/window-basic-main-dropfiles.cpp b/UI/window-basic-main-dropfiles.cpp index cd5c481..50dd3d3 100644 --- a/UI/window-basic-main-dropfiles.cpp +++ b/UI/window-basic-main-dropfiles.cpp @@ -148,7 +148,7 @@ void OBSBasic::dropEvent(QDropEvent *event) if (mimeData->hasUrls()) { QList urls = mimeData->urls(); - for (int i = 0; i < urls.size() && i < 5; i++) { + for (int i = 0; i < urls.size(); i++) { QString file = urls.at(i).toLocalFile(); QFileInfo fileInfo(file); diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 2f9bfa8..6b3e46f 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -8,6 +8,12 @@ using namespace std; +extern bool EncoderAvailable(const char *encoder); + +volatile bool streaming_active = false; +volatile bool recording_active = false; +volatile bool replaybuf_active = false; + static void OBSStreamStarting(void *data, calldata_t *params) { BasicOutputHandler *output = static_cast(data); @@ -39,6 +45,7 @@ static void OBSStartStreaming(void *data, calldata_t *params) { BasicOutputHandler *output = static_cast(data); output->streamingActive = true; + os_atomic_set_bool(&streaming_active, true); QMetaObject::invokeMethod(output->main, "StreamingStart"); UNUSED_PARAMETER(params); @@ -54,8 +61,10 @@ static void OBSStopStreaming(void *data, calldata_t *params) output->streamingActive = false; output->delayActive = false; + os_atomic_set_bool(&streaming_active, false); QMetaObject::invokeMethod(output->main, - "StreamingStop", Q_ARG(int, code), Q_ARG(QString, arg_last_error)); + "StreamingStop", Q_ARG(int, code), + Q_ARG(QString, arg_last_error)); } static void OBSStartRecording(void *data, calldata_t *params) @@ -63,6 +72,7 @@ static void OBSStartRecording(void *data, calldata_t *params) BasicOutputHandler *output = static_cast(data); output->recordingActive = true; + os_atomic_set_bool(&recording_active, true); QMetaObject::invokeMethod(output->main, "RecordingStart"); UNUSED_PARAMETER(params); @@ -72,10 +82,15 @@ static void OBSStopRecording(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->recordingActive = false; + os_atomic_set_bool(&recording_active, false); QMetaObject::invokeMethod(output->main, - "RecordingStop", Q_ARG(int, code)); + "RecordingStop", Q_ARG(int, code), + Q_ARG(QString, arg_last_error)); UNUSED_PARAMETER(params); } @@ -93,6 +108,7 @@ static void OBSStartReplayBuffer(void *data, calldata_t *params) BasicOutputHandler *output = static_cast(data); output->replayBufferActive = true; + os_atomic_set_bool(&replaybuf_active, true); QMetaObject::invokeMethod(output->main, "ReplayBufferStart"); UNUSED_PARAMETER(params); @@ -104,6 +120,7 @@ static void OBSStopReplayBuffer(void *data, calldata_t *params) int code = (int)calldata_int(params, "code"); output->replayBufferActive = false; + os_atomic_set_bool(&replaybuf_active, false); QMetaObject::invokeMethod(output->main, "ReplayBufferStop", Q_ARG(int, code)); @@ -242,6 +259,8 @@ void SimpleOutput::LoadRecordingPreset_Lossless() obs_data_set_string(settings, "video_encoder", "utvideo"); obs_data_set_string(settings, "audio_encoder", "pcm_s16le"); + int aMixes = 1; + obs_output_set_mixers(fileOutput, aMixes); obs_output_update(fileOutput, settings); obs_data_release(settings); } @@ -300,7 +319,10 @@ void SimpleOutput::LoadRecordingPreset() } else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) { LoadRecordingPreset_h264("amd_amf_h264"); } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) { - LoadRecordingPreset_h264("ffmpeg_nvenc"); + const char *id = EncoderAvailable("jim_nvenc") + ? "jim_nvenc" + : "ffmpeg_nvenc"; + LoadRecordingPreset_h264(id); } usingRecordingPreset = true; @@ -315,14 +337,22 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) { const char *encoder = config_get_string(main->Config(), "SimpleOutput", "StreamEncoder"); - if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) + + if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) { LoadStreamingPreset_h264("obs_qsv11"); - else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) + + } else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) { LoadStreamingPreset_h264("amd_amf_h264"); - else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) - LoadStreamingPreset_h264("ffmpeg_nvenc"); - else + + } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) { + const char *id = EncoderAvailable("jim_nvenc") + ? "jim_nvenc" + : "ffmpeg_nvenc"; + LoadStreamingPreset_h264(id); + + } else { LoadStreamingPreset_h264("obs_x264"); + } if (!CreateAACEncoder(aacStreaming, aacStreamEncID, GetAudioBitrate(), "simple_aac", 0)) @@ -649,6 +679,10 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) if (!Active()) SetupOutputs(); + Auth *auth = main->GetAuth(); + if (auth) + auth->OnStreamConfig(); + /* --------------------- */ const char *type = obs_service_get_output_type(service); @@ -685,28 +719,39 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) obs_output_get_signal_handler(streamOutput), "stop", OBSStopStreaming, this); - const char *codec = - obs_output_get_supported_audio_codecs(streamOutput); - if (!codec) { - return false; - } + bool isEncoded = obs_output_get_flags(streamOutput) + & OBS_OUTPUT_ENCODED; - if (strcmp(codec, "aac") != 0) { - const char *id = FindAudioEncoderFromCodec(codec); - int audioBitrate = GetAudioBitrate(); - obs_data_t *settings = obs_data_create(); - obs_data_set_int(settings, "bitrate", audioBitrate); - - aacStreaming = obs_audio_encoder_create(id, - "alt_audio_enc", nullptr, 0, nullptr); - obs_encoder_release(aacStreaming); - if (!aacStreaming) + if (isEncoded) { + const char *codec = + obs_output_get_supported_audio_codecs( + streamOutput); + if (!codec) { + blog(LOG_WARNING, "Failed to load audio codec"); return false; + } - obs_encoder_update(aacStreaming, settings); - obs_encoder_set_audio(aacStreaming, obs_get_audio()); + if (strcmp(codec, "aac") != 0) { + const char *id = FindAudioEncoderFromCodec( + codec); + int audioBitrate = GetAudioBitrate(); + obs_data_t *settings = obs_data_create(); + obs_data_set_int(settings, "bitrate", + audioBitrate); - obs_data_release(settings); + aacStreaming = obs_audio_encoder_create(id, + "alt_audio_enc", nullptr, 0, + nullptr); + obs_encoder_release(aacStreaming); + if (!aacStreaming) + return false; + + obs_encoder_update(aacStreaming, settings); + obs_encoder_set_audio(aacStreaming, + obs_get_audio()); + + obs_data_release(settings); + } } outputType = type; @@ -760,12 +805,15 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) } const char *error = obs_output_get_last_error(streamOutput); - bool has_last_error = error && *error; + bool hasLastError = error && *error; + if (hasLastError) + lastError = error; + else + lastError = string(); - blog(LOG_WARNING, "Stream output type '%s' failed to start!%s%s", - type, - has_last_error ? " Last Error: " : "", - has_last_error ? error : ""); + blog(LOG_WARNING, "Stream output type '%s' failed to start!%s%s", type, + hasLastError ? " Last Error: " : "", + hasLastError ? error : ""); return false; } @@ -848,7 +896,7 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer) if (!dir) { if (main->isVisible()) - OBSMessageBox::information(main, + OBSMessageBox::warning(main, QTStr("Output.BadPath.Title"), QTStr("Output.BadPath.Text")); else @@ -1207,9 +1255,14 @@ inline void AdvancedOutput::SetupStreaming() "Rescale"); const char *rescaleRes = config_get_string(main->Config(), "AdvOut", "RescaleRes"); + uint32_t caps = obs_encoder_get_caps(h264Streaming); unsigned int cx = 0; unsigned int cy = 0; + if ((caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0) { + rescale = false; + } + if (rescale && rescaleRes && *rescaleRes) { if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) { cx = 0; @@ -1243,6 +1296,11 @@ inline void AdvancedOutput::SetupRecording() obs_output_set_video_encoder(replayBuffer, h264Streaming); } else { + uint32_t caps = obs_encoder_get_caps(h264Recording); + if ((caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0) { + rescale = false; + } + if (rescale && rescaleRes && *rescaleRes) { if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) { cx = 0; @@ -1302,8 +1360,8 @@ inline void AdvancedOutput::SetupFFmpeg() "FFVCustom"); int aBitrate = config_get_int(main->Config(), "AdvOut", "FFABitrate"); - int aTrack = config_get_int(main->Config(), "AdvOut", - "FFAudioTrack"); + int aMixes = config_get_int(main->Config(), "AdvOut", + "FFAudioMixes"); const char *aEncoder = config_get_string(main->Config(), "AdvOut", "FFAEncoder"); int aEncoderId = config_get_int(main->Config(), "AdvOut", @@ -1337,7 +1395,7 @@ inline void AdvancedOutput::SetupFFmpeg() } } - obs_output_set_mixer(fileOutput, aTrack - 1); + obs_output_set_mixers(fileOutput, aMixes); obs_output_set_media(fileOutput, obs_get_video(), obs_get_audio()); obs_output_update(fileOutput, settings); @@ -1424,6 +1482,10 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) if (!Active()) SetupOutputs(); + Auth *auth = main->GetAuth(); + if (auth) + auth->OnStreamConfig(); + /* --------------------- */ int trackIndex = config_get_int(main->Config(), "AdvOut", @@ -1463,30 +1525,42 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) obs_output_get_signal_handler(streamOutput), "stop", OBSStopStreaming, this); - const char *codec = - obs_output_get_supported_audio_codecs(streamOutput); - if (!codec) { - return false; - } + bool isEncoded = obs_output_get_flags(streamOutput) + & OBS_OUTPUT_ENCODED; - if (strcmp(codec, "aac") == 0) { - streamAudioEnc = aacTrack[trackIndex - 1]; - } else { - const char *id = FindAudioEncoderFromCodec(codec); - int audioBitrate = GetAudioBitrate(trackIndex - 1); - obs_data_t *settings = obs_data_create(); - obs_data_set_int(settings, "bitrate", audioBitrate); - - streamAudioEnc = obs_audio_encoder_create(id, - "alt_audio_enc", nullptr, - trackIndex - 1, nullptr); - if (!streamAudioEnc) + if (isEncoded) { + const char *codec = + obs_output_get_supported_audio_codecs( + streamOutput); + if (!codec) { + blog(LOG_WARNING, "Failed to load audio codec"); return false; + } - obs_encoder_update(streamAudioEnc, settings); - obs_encoder_set_audio(streamAudioEnc, obs_get_audio()); + if (strcmp(codec, "aac") == 0) { + streamAudioEnc = aacTrack[trackIndex - 1]; + } else { + obs_data_t *settings = obs_data_create(); + const char *id = + FindAudioEncoderFromCodec(codec); + int audioBitrate = + GetAudioBitrate(trackIndex - 1); + + obs_data_set_int(settings, "bitrate", + audioBitrate); + streamAudioEnc = obs_audio_encoder_create(id, + "alt_audio_enc", nullptr, + trackIndex - 1, nullptr); - obs_data_release(settings); + if (!streamAudioEnc) + return false; + + obs_encoder_update(streamAudioEnc, settings); + obs_encoder_set_audio(streamAudioEnc, + obs_get_audio()); + + obs_data_release(settings); + } } outputType = type; @@ -1538,12 +1612,15 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) } const char *error = obs_output_get_last_error(streamOutput); - bool has_last_error = error && *error; + bool hasLastError = error && *error; + if (hasLastError) + lastError = error; + else + lastError = string(); - blog(LOG_WARNING, "Stream output type '%s' failed to start!%s%s", - type, - has_last_error ? " Last Error: " : "", - has_last_error ? error : ""); + blog(LOG_WARNING, "Stream output type '%s' failed to start!%s%s", type, + hasLastError ? " Last Error: " : "", + hasLastError ? error : ""); return false; } @@ -1586,7 +1663,7 @@ bool AdvancedOutput::StartRecording() if (!dir) { if (main->isVisible()) - OBSMessageBox::information(main, + OBSMessageBox::warning(main, QTStr("Output.BadPath.Title"), QTStr("Output.BadPath.Text")); else @@ -1686,7 +1763,7 @@ bool AdvancedOutput::StartReplayBuffer() if (!dir) { if (main->isVisible()) - OBSMessageBox::information(main, + OBSMessageBox::warning(main, QTStr("Output.BadPath.Title"), QTStr("Output.BadPath.Text")); else diff --git a/UI/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp index 4c4842d..c053d0b 100644 --- a/UI/window-basic-main-outputs.hpp +++ b/UI/window-basic-main-outputs.hpp @@ -15,6 +15,7 @@ struct BasicOutputHandler { OBSBasic *main; std::string outputType; + std::string lastError; OBSSignal startRecording; OBSSignal stopRecording; diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index f5db4cd..aa512d5 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -25,6 +25,11 @@ #include "window-namedialog.hpp" #include "qt-wrappers.hpp" +extern void DestroyPanelCookieManager(); +extern void DuplicateCurrentCookieProfile(ConfigFile &config); +extern void CheckExistingCookieId(); +extern void DeleteCookies(); + void EnumProfiles(std::function &&cb) { char path[512]; @@ -102,13 +107,13 @@ static bool GetProfileName(QWidget *parent, std::string &name, return false; } if (name.empty()) { - OBSMessageBox::information(parent, + OBSMessageBox::warning(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; } if (ProfileExists(name.c_str())) { - OBSMessageBox::information(parent, + OBSMessageBox::warning(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); continue; @@ -143,7 +148,7 @@ static bool GetProfileName(QWidget *parent, std::string &name, static bool CopyProfile(const char *fromPartial, const char *to) { os_glob_t *glob; - char path[512]; + char path[514]; char dir[512]; int ret; @@ -182,7 +187,7 @@ static bool CopyProfile(const char *fromPartial, const char *to) } bool OBSBasic::AddProfile(bool create_new, const char *title, const char *text, - const char *init_text) + const char *init_text, bool rename) { std::string newName; std::string newDir; @@ -228,10 +233,20 @@ bool OBSBasic::AddProfile(bool create_new, const char *title, const char *text, config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", newDir.c_str()); + Auth::Save(); + if (create_new) { + auth.reset(); + DestroyPanelCookieManager(); + } else if (!rename) { + DuplicateCurrentCookieProfile(config); + } + config_set_string(config, "General", "Name", newName.c_str()); + basicConfig.SaveSafe("tmp"); config.SaveSafe("tmp"); config.Swap(basicConfig); InitBasicConfigDefaults(); + InitBasicConfigDefaults2(); RefreshProfiles(); if (create_new) @@ -380,7 +395,7 @@ void OBSBasic::on_actionRenameProfile_triggered() /* Duplicate and delete in case there are any issues in the process */ bool success = AddProfile(false, Str("RenameProfile.Title"), - Str("AddProfile.Text"), curName.c_str()); + Str("AddProfile.Text"), curName.c_str(), true); if (success) { DeleteProfile(curName.c_str(), curDir.c_str()); RefreshProfiles(); @@ -446,8 +461,14 @@ void OBSBasic::on_actionRemoveProfile_triggered() config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", newDir); + Auth::Save(); + auth.reset(); + DeleteCookies(); + DestroyPanelCookieManager(); + config.Swap(basicConfig); InitBasicConfigDefaults(); + InitBasicConfigDefaults2(); ResetProfileData(); DeleteProfile(oldName.c_str(), oldDir.c_str()); RefreshProfiles(); @@ -459,6 +480,8 @@ void OBSBasic::on_actionRemoveProfile_triggered() UpdateTitleBar(); + Auth::Load(); + if (api) { api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); @@ -503,7 +526,7 @@ void OBSBasic::on_actionImportProfile_triggered() profileDir + "/recordEncoder.json"); RefreshProfiles(); } else { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("Basic.MainMenu.Profile.Import"), QTStr("Basic.MainMenu.Profile.Exists")); } @@ -603,13 +626,20 @@ void OBSBasic::ChangeProfile() config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", newDir); + Auth::Save(); + auth.reset(); + DestroyPanelCookieManager(); + config.Swap(basicConfig); InitBasicConfigDefaults(); + InitBasicConfigDefaults2(); ResetProfileData(); RefreshProfiles(); config_save_safe(App()->GlobalConfig(), "tmp", nullptr); UpdateTitleBar(); + Auth::Load(); + CheckForSimpleModeX264Fallback(); blog(LOG_INFO, "Switched to profile '%s' (%s)", diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index 9759a27..84da0f3 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()) { - OBSMessageBox::information(parent, + OBSMessageBox::warning(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; } if (SceneCollectionExists(name.c_str())) { - OBSMessageBox::information(parent, + OBSMessageBox::warning(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); continue; @@ -439,6 +439,8 @@ void OBSBasic::on_actionImportSceneCollection_triggered() void OBSBasic::on_actionExportSceneCollection_triggered() { + SaveProjectNow(); + char path[512]; QString home = QDir::homePath(); diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 952f5d4..daec936 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -254,6 +254,12 @@ void OBSBasic::TransitionStopped() OBSSource scene = OBSGetStrongRef(swapScene); if (scene) SetCurrentScene(scene); + + // Make sure we re-enable the transition button + if (transitionButton) + transitionButton->setEnabled(true); + + EnableQuickTransitionWidgets(); } if (api) { @@ -294,7 +300,7 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force, bool direct, return; OBSWeakSource lastProgramScene; - + if (usingPreviewProgram) { lastProgramScene = programScene; programScene = OBSGetWeakRef(source); @@ -321,6 +327,12 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force, bool direct, OBSSource transition = obs_get_output_source(0); obs_source_release(transition); + bool stillTransitioning = obs_transition_get_time(transition) < 1.0f; + + // If actively transitioning, block new transitions from starting + if (usingPreviewProgram && stillTransitioning) + goto cleanup; + if (force) { obs_transition_set(transition, source); if (api) @@ -355,6 +367,15 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force, bool direct, TransitionFullyStopped(); } + // If transition has begun, disable Transition button + if (usingPreviewProgram && stillTransitioning) { + if (transitionButton) + transitionButton->setEnabled(false); + + DisableQuickTransitionWidgets(); + } + +cleanup: if (usingPreviewProgram && sceneDuplicationMode) obs_scene_release(scene); } @@ -432,7 +453,7 @@ void OBSBasic::AddTransition() if (accepted) { if (name.empty()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); AddTransition(); @@ -441,7 +462,7 @@ void OBSBasic::AddTransition() source = FindTransition(name.c_str()); if (source) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -538,7 +559,7 @@ void OBSBasic::RenameTransition() if (accepted) { if (name.empty()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); RenameTransition(); @@ -547,7 +568,7 @@ void OBSBasic::RenameTransition() source = FindTransition(name.c_str()); if (source) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -675,6 +696,7 @@ void OBSBasic::SetCurrentScene(OBSSource scene, bool force, bool direct) void OBSBasic::CreateProgramDisplay() { program = new OBSQTDisplay(); + program->setContextMenuPolicy(Qt::CustomContextMenu); connect(program.data(), &QWidget::customContextMenuRequested, this, &OBSBasic::on_program_customContextMenuRequested); @@ -725,7 +747,7 @@ void OBSBasic::CreateProgramOptions() QHBoxLayout *mainButtonLayout = new QHBoxLayout(); mainButtonLayout->setSpacing(2); - QPushButton *transitionButton = new QPushButton(QTStr("Transition")); + transitionButton = new QPushButton(QTStr("Transition")); QHBoxLayout *quickTransitions = new QHBoxLayout(); quickTransitions->setSpacing(2); @@ -809,7 +831,7 @@ void OBSBasic::CreateProgramOptions() menu.exec(QCursor::pos()); }; - connect(transitionButton, &QAbstractButton::clicked, + connect(transitionButton.data(), &QAbstractButton::clicked, this, &OBSBasic::TransitionClicked); connect(addQuickTransition, &QAbstractButton::clicked, onAdd); connect(configTransitions, &QAbstractButton::clicked, onConfig); @@ -1124,11 +1146,55 @@ void OBSBasic::RefreshQuickTransitions() AddQuickTransitionId(qt.id); } +void OBSBasic::DisableQuickTransitionWidgets() +{ + 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; + + widget->setEnabled(false); + } +} + +void OBSBasic::EnableQuickTransitionWidgets() +{ + 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; + + widget->setEnabled(true); + } +} + void OBSBasic::SetPreviewProgramMode(bool enabled) { if (IsPreviewProgramMode() == enabled) return; + ui->previewLabel->setHidden(!enabled); + ui->modeSwitch->setChecked(enabled); os_atomic_set_bool(&previewProgramMode, enabled); @@ -1167,10 +1233,31 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) RefreshQuickTransitions(); + programLabel = new QLabel(QTStr("StudioMode.Program")); + programLabel->setSizePolicy(QSizePolicy::Preferred, + QSizePolicy::Preferred); + programLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + programLabel->setProperty("themeID", "previewProgramLabels"); + + programWidget = new QWidget(); + programLayout = new QVBoxLayout(); + + programLayout->setContentsMargins(0, 0, 0, 0); + programLayout->setSpacing(0); + + programLayout->addWidget(programLabel); + programLayout->addWidget(program); + + bool labels = config_get_bool(GetGlobalConfig(), + "BasicWindow", "StudioModeLabels"); + + programLabel->setHidden(!labels); + + programWidget->setLayout(programLayout); + ui->previewLayout->addWidget(programOptions); - ui->previewLayout->addWidget(program); + ui->previewLayout->addWidget(programWidget); ui->previewLayout->setAlignment(programOptions, Qt::AlignCenter); - program->show(); if (api) api->on_event(OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED); @@ -1188,6 +1275,8 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) delete programOptions; delete program; + delete programLabel; + delete programWidget; if (lastScene) { OBSSource actualLastScene = OBSGetStrongRef(lastScene); @@ -1219,6 +1308,8 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) void OBSBasic::RenderProgram(void *data, uint32_t cx, uint32_t cy) { + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "RenderProgram"); + OBSBasic *window = static_cast(data); obs_video_info ovi; @@ -1247,6 +1338,8 @@ void OBSBasic::RenderProgram(void *data, uint32_t cx, uint32_t cy) gs_projection_pop(); gs_viewport_pop(); + GS_DEBUG_MARKER_END(); + UNUSED_PARAMETER(cx); UNUSED_PARAMETER(cy); } diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index b2cebfe..33a17d8 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -17,7 +17,7 @@ along with this program. If not, see . ******************************************************************************/ -#include +#include #include #include #include @@ -25,10 +25,8 @@ #include #include #include -#include #include #include -#include #include #include @@ -36,7 +34,6 @@ #include #include #include -#include #include "obs-app.hpp" #include "platform.hpp" @@ -49,7 +46,6 @@ #include "window-basic-main.hpp" #include "window-basic-stats.hpp" #include "window-basic-main-outputs.hpp" -#include "window-basic-properties.hpp" #include "window-log-reply.hpp" #include "window-projector.hpp" #include "window-remux.hpp" @@ -57,7 +53,8 @@ #include "display-helpers.hpp" #include "volume-control.hpp" #include "remote-text.hpp" -#include "source-tree.hpp" +#include +#include #ifdef _WIN32 #include "win-update/win-update.hpp" @@ -72,19 +69,25 @@ #include #include -#ifdef _WIN32 -#include -#endif - #include using namespace json11; using namespace std; -#ifdef _WIN32 -static CREATE_BROWSER_WIDGET_PROC create_browser_widget = nullptr; +#ifdef BROWSER_AVAILABLE +#include #endif +#include "ui-config.h" + +struct QCef; +struct QCefCookieManager; + +QCef *cef = nullptr; +QCefCookieManager *panel_cookies = nullptr; + +void DestroyPanelCookieManager(); + namespace { template @@ -95,6 +98,8 @@ struct SignalContainer { } +extern volatile long insideEventLoop; + Q_DECLARE_METATYPE(OBSScene); Q_DECLARE_METATYPE(OBSSceneItem); Q_DECLARE_METATYPE(OBSSource); @@ -167,12 +172,47 @@ static int CountVideoSources() return count; } +void assignDockToggle(QDockWidget *dock, QAction *action) +{ + auto handleWindowToggle = [action] (bool vis) + { + action->blockSignals(true); + action->setChecked(vis); + action->blockSignals(false); + }; + auto handleMenuToggle = [dock] (bool check) + { + dock->blockSignals(true); + dock->setVisible(check); + dock->blockSignals(false); + }; + + dock->connect(dock->toggleViewAction(), &QAction::toggled, + handleWindowToggle); + dock->connect(action, &QAction::toggled, + handleMenuToggle); +} + +extern void RegisterTwitchAuth(); +extern void RegisterMixerAuth(); +extern void RegisterRestreamAuth(); + OBSBasic::OBSBasic(QWidget *parent) : OBSMainWindow (parent), ui (new Ui::OBSBasic) { setAttribute(Qt::WA_NativeWindow); +#if TWITCH_ENABLED + RegisterTwitchAuth(); +#endif +#if MIXER_ENABLED + RegisterMixerAuth(); +#endif +#if RESTREAM_ENABLED + RegisterRestreamAuth(); +#endif + setAcceptDrops(true); api = InitializeAPIInterface(this); @@ -182,6 +222,15 @@ OBSBasic::OBSBasic(QWidget *parent) startingDockLayout = saveState(); + statsDock = new OBSDock(); + statsDock->setObjectName(QStringLiteral("statsDock")); + statsDock->setFeatures(QDockWidget::AllDockWidgetFeatures); + statsDock->setWindowTitle(QTStr("Basic.Stats")); + addDockWidget(Qt::BottomDockWidgetArea, statsDock); + statsDock->setVisible(false); + statsDock->setFloating(true); + statsDock->resize(700, 200); + copyActionsDynamicProperties(); char styleSheetPath[512]; @@ -209,6 +258,8 @@ OBSBasic::OBSBasic(QWidget *parent) ui->scenes->setAttribute(Qt::WA_MacShowFocusRect, false); ui->sources->setAttribute(Qt::WA_MacShowFocusRect, false); + ui->scenes->setItemDelegate(new SceneRenameDelegate(ui->scenes)); + auto displayResize = [this]() { struct obs_video_info ovi; @@ -219,7 +270,9 @@ OBSBasic::OBSBasic(QWidget *parent) connect(windowHandle(), &QWindow::screenChanged, displayResize); connect(ui->preview, &OBSQTDisplay::DisplayResized, displayResize); - installEventFilter(CreateShortcutFilter()); + delete shortcutFilter; + shortcutFilter = CreateShortcutFilter(); + installEventFilter(shortcutFilter); stringstream name; name << "OBS " << App()->GetVersionString(); @@ -241,12 +294,29 @@ OBSBasic::OBSBasic(QWidget *parent) ui->statusbar, SLOT(UpdateCPUUsage())); cpuUsageTimer->start(3000); + QAction *renameScene = new QAction(ui->scenesDock); + renameScene->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(renameScene, SIGNAL(triggered()), this, SLOT(EditSceneName())); + ui->scenesDock->addAction(renameScene); + + QAction *renameSource = new QAction(ui->sourcesDock); + renameSource->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(renameSource, SIGNAL(triggered()), this, + SLOT(EditSceneItemName())); + ui->sourcesDock->addAction(renameSource); + #ifdef __APPLE__ + renameScene->setShortcut({Qt::Key_Return}); + renameSource->setShortcut({Qt::Key_Return}); + ui->actionRemoveSource->setShortcuts({Qt::Key_Backspace}); ui->actionRemoveScene->setShortcuts({Qt::Key_Backspace}); ui->action_Settings->setMenuRole(QAction::PreferencesRole); ui->actionE_xit->setMenuRole(QAction::QuitRole); +#else + renameScene->setShortcut({Qt::Key_F2}); + renameSource->setShortcut({Qt::Key_F2}); #endif auto addNudge = [this](const QKeySequence &seq, const char *s) @@ -263,32 +333,12 @@ OBSBasic::OBSBasic(QWidget *parent) addNudge(Qt::Key_Left, SLOT(NudgeLeft())); addNudge(Qt::Key_Right, SLOT(NudgeRight())); - auto assignDockToggle = [] (QDockWidget *dock, QAction *action) - { - auto handleWindowToggle = [action] (bool vis) - { - action->blockSignals(true); - action->setChecked(vis); - action->blockSignals(false); - }; - auto handleMenuToggle = [dock] (bool check) - { - dock->blockSignals(true); - dock->setVisible(check); - dock->blockSignals(false); - }; - - dock->connect(dock->toggleViewAction(), &QAction::toggled, - handleWindowToggle); - dock->connect(action, &QAction::toggled, - handleMenuToggle); - }; - assignDockToggle(ui->scenesDock, ui->toggleScenes); assignDockToggle(ui->sourcesDock, ui->toggleSources); assignDockToggle(ui->mixerDock, ui->toggleMixer); assignDockToggle(ui->transitionsDock, ui->toggleTransitions); assignDockToggle(ui->controlsDock, ui->toggleControls); + assignDockToggle(statsDock, ui->toggleStats); //hide all docking panes ui->toggleScenes->setChecked(false); @@ -296,6 +346,9 @@ OBSBasic::OBSBasic(QWidget *parent) ui->toggleMixer->setChecked(false); ui->toggleTransitions->setChecked(false); ui->toggleControls->setChecked(false); + ui->toggleStats->setChecked(false); + + QPoint curPos; //restore parent window geometry const char *geometry = config_get_string(App()->GlobalConfig(), @@ -313,7 +366,29 @@ OBSBasic::OBSBasic(QWidget *parent) Qt::AlignCenter, size(), rect)); } + + curPos = pos(); + } else { + QRect desktopRect = QGuiApplication::primaryScreen()->geometry(); + QSize adjSize = desktopRect.size() / 2 - size() / 2; + curPos = QPoint(adjSize.width(), adjSize.height()); } + + QPoint curSize(width(), height()); + QPoint statsDockSize(statsDock->width(), statsDock->height()); + QPoint statsDockPos = curSize / 2 - statsDockSize / 2; + QPoint newPos = curPos + statsDockPos; + statsDock->move(newPos); + + ui->previewLabel->setProperty("themeID", "previewProgramLabels"); + + bool labels = config_get_bool(GetGlobalConfig(), + "BasicWindow", "StudioModeLabels"); + + if (!previewProgramMode) + ui->previewLabel->setHidden(true); + else + ui->previewLabel->setHidden(!labels); } static void SaveAudioDevice(const char *name, int channel, obs_data_t *parent, @@ -349,6 +424,7 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, SaveAudioDevice(AUX_AUDIO_1, 3, saveData, audioSources); SaveAudioDevice(AUX_AUDIO_2, 4, saveData, audioSources); SaveAudioDevice(AUX_AUDIO_3, 5, saveData, audioSources); + SaveAudioDevice(AUX_AUDIO_4, 6, saveData, audioSources); /* -------------------------------- */ /* save non-group sources */ @@ -677,6 +753,11 @@ void OBSBasic::LoadSceneListOrder(obs_data_array_t *array) void OBSBasic::LoadSavedProjectors(obs_data_array_t *array) { + for (SavedProjectorInfo *info : savedProjectorsArray) { + delete info; + } + savedProjectorsArray.clear(); + size_t num = obs_data_array_count(array); for (size_t i = 0; i < num; i++) { @@ -708,13 +789,18 @@ static void LogFilter(obs_source_t*, obs_source_t *filter, void *v_val) blog(LOG_INFO, "%s- filter: '%s' (%s)", indent.c_str(), name, id); } -static bool LogSceneItem(obs_scene_t*, obs_sceneitem_t *item, void*) +static bool LogSceneItem(obs_scene_t*, obs_sceneitem_t *item, void *v_val) { obs_source_t *source = obs_sceneitem_get_source(item); const char *name = obs_source_get_name(source); const char *id = obs_source_get_id(source); + int indent_count = (int)(intptr_t)v_val; + string indent; - blog(LOG_INFO, " - source: '%s' (%s)", name, id); + for (int i = 0; i < indent_count; i++) + indent += " "; + + blog(LOG_INFO, "%s- source: '%s' (%s)", indent.c_str(), name, id); obs_monitoring_type monitoring_type = obs_source_get_monitoring_type(source); @@ -725,10 +811,12 @@ static bool LogSceneItem(obs_scene_t*, obs_sceneitem_t *item, void*) ? "monitor only" : "monitor and output"; - blog(LOG_INFO, " - monitoring: %s", type); + blog(LOG_INFO, " %s- monitoring: %s", indent.c_str(), type); } - - obs_source_enum_filters(source, LogFilter, (void*)(intptr_t)2); + int child_indent = 1 + indent_count; + obs_source_enum_filters(source, LogFilter, (void*)(intptr_t)child_indent); + if (obs_sceneitem_is_group(item)) + obs_sceneitem_group_enum_items(item, LogSceneItem, (void*)(intptr_t)child_indent); return true; } @@ -745,7 +833,7 @@ void OBSBasic::LogScenes() const char *name = obs_source_get_name(source); blog(LOG_INFO, "- scene '%s':", name); - obs_scene_enum_items(scene, LogSceneItem, nullptr); + obs_scene_enum_items(scene, LogSceneItem, (void*)(intptr_t)1); obs_source_enum_filters(source, LogFilter, (void*)(intptr_t)1); } @@ -814,6 +902,7 @@ void OBSBasic::Load(const char *file) LoadAudioDevice(AUX_AUDIO_1, 3, data); LoadAudioDevice(AUX_AUDIO_2, 4, data); LoadAudioDevice(AUX_AUDIO_3, 5, data); + LoadAudioDevice(AUX_AUDIO_4, 6, data); if (!sources) { sources = groups; @@ -952,6 +1041,9 @@ retryScene: opt_start_replaybuffer = false; } + copyString = nullptr; + copyFiltersString = nullptr; + LogScenes(); disableSaving--; @@ -1050,6 +1142,8 @@ static const double scaled_vals[] = 0.0 }; +extern void CheckExistingCookieId(); + bool OBSBasic::InitBasicConfigDefaults() { QList screens = QGuiApplication::screens(); @@ -1076,6 +1170,21 @@ bool OBSBasic::InitBasicConfigDefaults() cy = 1080; } + bool changed = false; + + /* ----------------------------------------------------- */ + /* move over old FFmpeg track settings */ + if (config_has_user_value(basicConfig, "AdvOut", "FFAudioTrack") && + !config_has_user_value(basicConfig, "AdvOut", "Pre22.1Settings")) { + + int track = (int)config_get_int(basicConfig, "AdvOut", + "FFAudioTrack"); + config_set_int(basicConfig, "AdvOut", "FFAudioMixes", + 1LL << (track - 1)); + config_set_bool(basicConfig, "AdvOut", "Pre22.1Settings", true); + changed = true; + } + /* ----------------------------------------------------- */ /* move over mixer values in advanced if older config */ if (config_has_user_value(basicConfig, "AdvOut", "RecTrackIndex") && @@ -1086,11 +1195,16 @@ bool OBSBasic::InitBasicConfigDefaults() track = 1ULL << (track - 1); config_set_uint(basicConfig, "AdvOut", "RecTracks", track); config_remove_value(basicConfig, "AdvOut", "RecTrackIndex"); - config_save_safe(basicConfig, "tmp", nullptr); + changed = true; } /* ----------------------------------------------------- */ + if (changed) + config_save_safe(basicConfig, "tmp", nullptr); + + /* ----------------------------------------------------- */ + config_set_default_string(basicConfig, "Output", "Mode", "Simple"); config_set_default_string(basicConfig, "SimpleOutput", "FilePath", @@ -1099,8 +1213,6 @@ bool OBSBasic::InitBasicConfigDefaults() "flv"); config_set_default_uint (basicConfig, "SimpleOutput", "VBitrate", 2500); - config_set_default_string(basicConfig, "SimpleOutput", "StreamEncoder", - SIMPLE_ENCODER_X264); config_set_default_uint (basicConfig, "SimpleOutput", "ABitrate", 160); config_set_default_bool (basicConfig, "SimpleOutput", "UseAdvanced", false); @@ -1108,10 +1220,10 @@ bool OBSBasic::InitBasicConfigDefaults() true); config_set_default_string(basicConfig, "SimpleOutput", "Preset", "veryfast"); + config_set_default_string(basicConfig, "SimpleOutput", "NVENCPreset", + "hq"); config_set_default_string(basicConfig, "SimpleOutput", "RecQuality", "Stream"); - config_set_default_string(basicConfig, "SimpleOutput", "RecEncoder", - SIMPLE_ENCODER_X264); config_set_default_bool(basicConfig, "SimpleOutput", "RecRB", false); config_set_default_int(basicConfig, "SimpleOutput", "RecRBTime", 20); config_set_default_int(basicConfig, "SimpleOutput", "RecRBSize", 512); @@ -1147,7 +1259,7 @@ bool OBSBasic::InitBasicConfigDefaults() config_set_default_bool (basicConfig, "AdvOut", "FFIgnoreCompat", false); config_set_default_uint (basicConfig, "AdvOut", "FFABitrate", 160); - config_set_default_uint (basicConfig, "AdvOut", "FFAudioTrack", 1); + config_set_default_uint (basicConfig, "AdvOut", "FFAudioMixes", 1); config_set_default_uint (basicConfig, "AdvOut", "Track1Bitrate", 160); config_set_default_uint (basicConfig, "AdvOut", "Track2Bitrate", 160); @@ -1235,9 +1347,25 @@ bool OBSBasic::InitBasicConfigDefaults() VOLUME_METER_DECAY_FAST); config_set_default_uint (basicConfig, "Audio", "PeakMeterType", 0); + CheckExistingCookieId(); + return true; } +extern bool EncoderAvailable(const char *encoder); + +void OBSBasic::InitBasicConfigDefaults2() +{ + bool oldEncDefaults = config_get_bool(App()->GlobalConfig(), + "General", "Pre23Defaults"); + bool useNV = EncoderAvailable("ffmpeg_nvenc") && !oldEncDefaults; + + config_set_default_string(basicConfig, "SimpleOutput", "StreamEncoder", + useNV ? SIMPLE_ENCODER_NVENC : SIMPLE_ENCODER_X264); + config_set_default_string(basicConfig, "SimpleOutput", "RecEncoder", + useNV ? SIMPLE_ENCODER_NVENC : SIMPLE_ENCODER_X264); +} + bool OBSBasic::InitBasicConfig() { ProfileScope("OBSBasic::InitBasicConfig"); @@ -1465,10 +1593,12 @@ void OBSBasic::OBSInit() blog(LOG_INFO, "---------------------------------"); obs_post_load_modules(); -#ifdef _WIN32 - create_browser_widget = obs_browser_init_panel(); +#ifdef BROWSER_AVAILABLE + cef = obs_browser_init_panel(); #endif + InitBasicConfigDefaults2(); + CheckForSimpleModeX264Fallback(); blog(LOG_INFO, STARTUP_SEPARATOR); @@ -1510,14 +1640,12 @@ void OBSBasic::OBSInit() SET_VISIBILITY("ShowStatusBar", toggleStatusBar); #undef SET_VISIBILITY -#ifndef __APPLE__ { ProfileScope("OBSBasic::Load"); disableSaving--; Load(savePath); disableSaving++; } -#endif TimedCheckForUpdates(); loaded = true; @@ -1539,9 +1667,7 @@ void OBSBasic::OBSInit() } #endif -#ifndef __APPLE__ RefreshSceneCollections(); -#endif RefreshProfiles(); disableSaving--; @@ -1573,6 +1699,10 @@ void OBSBasic::OBSInit() show(); #endif + /* setup stats dock */ + OBSBasicStats *statsDlg = new OBSBasicStats(statsDock, false); + statsDock->setWidget(statsDlg); + const char *dockStateStr = config_get_string(App()->GlobalConfig(), "BasicWindow", "DockState"); if (!dockStateStr) { @@ -1584,8 +1714,19 @@ void OBSBasic::OBSInit() on_resetUI_triggered(); } - config_set_default_bool(App()->GlobalConfig(), "BasicWindow", - "DocksLocked", true); + bool pre23Defaults = config_get_bool(App()->GlobalConfig(), + "General", "Pre23Defaults"); + if (pre23Defaults) { + bool resetDockLock23 = config_get_bool(App()->GlobalConfig(), + "General", "ResetDockLock23"); + if (!resetDockLock23) { + config_set_bool(App()->GlobalConfig(), + "General", "ResetDockLock23", true); + config_remove_value(App()->GlobalConfig(), + "BasicWindow", "DocksLocked"); + config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + } + } bool docksLocked = config_get_bool(App()->GlobalConfig(), "BasicWindow", "DocksLocked"); @@ -1621,7 +1762,9 @@ void OBSBasic::OBSInit() msg); if (button == QMessageBox::Yes) { - on_autoConfigure_triggered(); + QMetaObject::invokeMethod(this, + "on_autoConfigure_triggered", + Qt::QueuedConnection); } else { msg = QTStr("Basic.FirstStartup.RunWizard.NoClicked"); OBSMessageBox::information(this, @@ -1644,6 +1787,8 @@ void OBSBasic::OBSInit() multiviewProjectorMenu = new QMenu(QTStr("MultiviewProjector")); ui->viewMenu->addMenu(multiviewProjectorMenu); + AddProjectorMenuMonitors(multiviewProjectorMenu, this, + SLOT(OpenMultiviewProjector())); connect(ui->viewMenu->menuAction(), &QAction::hovered, this, &OBSBasic::UpdateMultiviewProjectorMenu); ui->viewMenu->addAction(QTStr("MultiviewWindowed"), @@ -1660,28 +1805,12 @@ void OBSBasic::OBSInit() ui->actionCheckForUpdates = nullptr; #endif -#ifdef __APPLE__ - /* This is an incredibly unpleasant hack for macOS to isolate CEF - * initialization until after all tasks related to Qt startup and main - * window initialization have completed. There is a macOS-specific bug - * within either CEF and/or Qt that can cause a crash if both Qt and - * CEF are loading at the same time. - * - * CEF will typically load fine after about two iterations from this - * point, and all Qt tasks are typically fully completed after about - * four or five iterations, but to be "ultra" safe, an arbitrarily - * large number such as 10 is used. This hack is extremely unpleasant, - * but is worth doing instead of being forced to isolate the entire - * browser plugin in to a separate process as before. - * - * Again, this hack is specific to macOS only. Fortunately, on other - * operating systems, such issues do not occur. */ - QMetaObject::invokeMethod(this, "DeferredLoad", - Qt::QueuedConnection, - Q_ARG(QString, QT_UTF8(savePath)), - Q_ARG(int, 10)); -#else OnFirstLoad(); + +#ifdef __APPLE__ + QMetaObject::invokeMethod(this, "DeferredSysTrayLoad", + Qt::QueuedConnection, + Q_ARG(int, 10)); #endif } @@ -1690,9 +1819,9 @@ void OBSBasic::OnFirstLoad() if (api) api->on_event(OBS_FRONTEND_EVENT_FINISHED_LOADING); -#ifdef _WIN32 +#if defined(BROWSER_AVAILABLE) && defined(_WIN32) /* Attempt to load init screen if available */ - if (create_browser_widget) { + if (cef) { WhatsNewInfoThread *wnit = new WhatsNewInfoThread(); if (wnit) { connect(wnit, &WhatsNewInfoThread::Result, @@ -1704,22 +1833,19 @@ void OBSBasic::OnFirstLoad() } } #endif + + Auth::Load(); } -void OBSBasic::DeferredLoad(const QString &file, int requeueCount) +void OBSBasic::DeferredSysTrayLoad(int requeueCount) { if (--requeueCount > 0) { - QMetaObject::invokeMethod(this, "DeferredLoad", + QMetaObject::invokeMethod(this, "DeferredSysTrayLoad", Qt::QueuedConnection, - Q_ARG(QString, file), Q_ARG(int, requeueCount)); return; } - Load(QT_TO_UTF8(file)); - RefreshSceneCollections(); - OnFirstLoad(); - /* Minimizng to tray on initial startup does not work on mac * unless it is done in the deferred load */ SystemTray(true); @@ -1728,6 +1854,7 @@ void OBSBasic::DeferredLoad(const QString &file, int requeueCount) /* shows a "what's new" page on startup of new versions using CEF */ void OBSBasic::ReceivedIntroJson(const QString &text) { +#ifdef BROWSER_AVAILABLE #ifdef _WIN32 std::string err; Json json = Json::parse(QT_TO_UTF8(text), err); @@ -1781,7 +1908,7 @@ void OBSBasic::ReceivedIntroJson(const QString &text) #if OBS_RELEASE_CANDIDATE > 0 if (lastVersion < OBS_RELEASE_CANDIDATE_VER) { #else - if (lastVersion < LIBOBS_API_VER) { + if ((lastVersion & ~0xFFFF) < (LIBOBS_API_VER & ~0xFFFF)) { #endif config_set_int(App()->GlobalConfig(), "General", "InfoIncrement", -1); @@ -1798,32 +1925,49 @@ void OBSBasic::ReceivedIntroJson(const QString &text) config_set_int(App()->GlobalConfig(), "General", "InfoIncrement", info_increment); - QDialog dlg(this); - dlg.setWindowTitle("What's New"); - dlg.resize(700, 600); + /* Don't show What's New dialog for new users */ +#if !defined(OBS_RELEASE_CANDIDATE) || OBS_RELEASE_CANDIDATE == 0 + if (!lastVersion) { + return; + } +#endif + cef->init_browser(); + ExecuteFuncSafeBlock([] {cef->wait_for_browser_init();}); - QCefWidget *cefWidget = create_browser_widget(nullptr, info_url); + QDialog *dlg = new QDialog(this); + dlg->setAttribute(Qt::WA_DeleteOnClose, true); + dlg->setWindowTitle("What's New"); + dlg->resize(700, 600); + + Qt::WindowFlags flags = dlg->windowFlags(); + Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; + dlg->setWindowFlags(flags & (~helpFlag)); + + QCefWidget *cefWidget = cef->create_widget(nullptr, info_url); if (!cefWidget) { return; } connect(cefWidget, SIGNAL(titleChanged(const QString &)), - &dlg, SLOT(setWindowTitle(const QString &))); + dlg, SLOT(setWindowTitle(const QString &))); QPushButton *close = new QPushButton(QTStr("Close")); connect(close, &QAbstractButton::clicked, - &dlg, &QDialog::accept); + dlg, &QDialog::accept); QHBoxLayout *bottomLayout = new QHBoxLayout(); bottomLayout->addStretch(); bottomLayout->addWidget(close); bottomLayout->addStretch(); - QVBoxLayout *topLayout = new QVBoxLayout(&dlg); + QVBoxLayout *topLayout = new QVBoxLayout(dlg); topLayout->addWidget(cefWidget); topLayout->addLayout(bottomLayout); - dlg.exec(); + dlg->show(); +#else + UNUSED_PARAMETER(text); +#endif #else UNUSED_PARAMETER(text); #endif @@ -1879,6 +2023,7 @@ void OBSBasic::InitHotkeys() t.apple_keypad_decimal = Str("Hotkeys.AppleKeypadDecimal"); t.apple_keypad_equal = Str("Hotkeys.AppleKeypadEqual"); t.mouse_num = Str("Hotkeys.MouseButton"); + t.escape = Str("Hotkeys.Escape"); obs_hotkeys_set_translations(&t); obs_hotkeys_set_audio_hotkeys_translations(Str("Mute"), Str("Unmute"), @@ -2014,6 +2159,19 @@ void OBSBasic::CreateHotkeys() this, this); LoadHotkeyPair(replayBufHotkeys, "OBSBasic.StartReplayBuffer", "OBSBasic.StopReplayBuffer"); + + togglePreviewHotkeys = obs_hotkey_pair_register_frontend( + "OBSBasic.EnablePreview", + Str("Basic.Main.PreviewConextMenu.Enable"), + "OBSBasic.DisablePreview", + Str("Basic.Main.Preview.Disable"), + MAKE_CALLBACK(!basic.previewEnabled, + basic.EnablePreview, "Enabling preview"), + MAKE_CALLBACK(basic.previewEnabled, + basic.DisablePreview, "Disabling preview"), + this, this); + LoadHotkeyPair(togglePreviewHotkeys, + "OBSBasic.EnablePreview", "OBSBasic.DisablePreview"); #undef MAKE_CALLBACK auto togglePreviewProgram = [] (void *data, obs_hotkey_id, @@ -2051,6 +2209,7 @@ void OBSBasic::ClearHotkeys() obs_hotkey_pair_unregister(streamingHotkeys); obs_hotkey_pair_unregister(recordingHotkeys); obs_hotkey_pair_unregister(replayBufHotkeys); + obs_hotkey_pair_unregister(togglePreviewHotkeys); obs_hotkey_unregister(forceStreamingStopHotkey); obs_hotkey_unregister(togglePreviewProgramHotkey); obs_hotkey_unregister(transitionHotkey); @@ -2062,6 +2221,19 @@ OBSBasic::~OBSBasic() updateCheckThread->wait(); delete multiviewProjectorMenu; + delete previewProjector; + delete studioProgramProjector; + delete previewProjectorSource; + delete previewProjectorMain; + delete sourceProjector; + delete sceneProjectorMenu; + delete scaleFilteringMenu; + delete colorMenu; + delete colorWidgetAction; + delete colorSelect; + delete deinterlaceMenu; + delete perSceneTransitionMenu; + delete shortcutFilter; delete trayMenu; delete programOptions; delete program; @@ -2096,6 +2268,9 @@ OBSBasic::~OBSBasic() if (advAudioWindow) delete advAudioWindow; + if (about) + delete about; + obs_display_remove_draw_callback(ui->preview->GetDisplay(), OBSBasic::RenderMain, this); @@ -2152,6 +2327,12 @@ OBSBasic::~OBSBasic() } } #endif + +#ifdef BROWSER_AVAILABLE + DestroyPanelCookieManager(); + delete cef; + cef = nullptr; +#endif } void OBSBasic::SaveProjectNow() @@ -2587,7 +2768,7 @@ void OBSBasic::MixerRenameSource() return; if (name.empty()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; @@ -2597,7 +2778,7 @@ void OBSBasic::MixerRenameSource() obs_source_release(sourceTest); if (sourceTest) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); continue; @@ -2618,6 +2799,9 @@ void OBSBasic::VolControlContextMenu() QAction unhideAllAction(QTStr("UnhideAll"), this); QAction mixerRenameAction(QTStr("Rename"), this); + QAction copyFiltersAction(QTStr("Copy.Filters"), this); + QAction pasteFiltersAction(QTStr("Paste.Filters"), this); + QAction filtersAction(QTStr("Filters"), this); QAction propertiesAction(QTStr("Properties"), this); QAction advPropAction(QTStr("Basic.MainMenu.Edit.AdvAudio"), this); @@ -2639,6 +2823,13 @@ void OBSBasic::VolControlContextMenu() this, &OBSBasic::MixerRenameSource, Qt::DirectConnection); + connect(©FiltersAction, &QAction::triggered, + this, &OBSBasic::AudioMixerCopyFilters, + Qt::DirectConnection); + connect(&pasteFiltersAction, &QAction::triggered, + this, &OBSBasic::AudioMixerPasteFilters, + Qt::DirectConnection); + connect(&filtersAction, &QAction::triggered, this, &OBSBasic::GetAudioSourceFilters, Qt::DirectConnection); @@ -2662,6 +2853,11 @@ void OBSBasic::VolControlContextMenu() mixerRenameAction.setProperty("volControl", QVariant::fromValue(vol)); + copyFiltersAction.setProperty("volControl", + QVariant::fromValue(vol)); + pasteFiltersAction.setProperty("volControl", + QVariant::fromValue(vol)); + filtersAction.setProperty("volControl", QVariant::fromValue(vol)); propertiesAction.setProperty("volControl", @@ -2669,11 +2865,19 @@ void OBSBasic::VolControlContextMenu() /* ------------------- */ + if (copyFiltersString == nullptr) + pasteFiltersAction.setEnabled(false); + else + pasteFiltersAction.setEnabled(true); + QMenu popup; popup.addAction(&unhideAllAction); popup.addAction(&hideAction); popup.addAction(&mixerRenameAction); popup.addSeparator(); + popup.addAction(©FiltersAction); + popup.addAction(&pasteFiltersAction); + popup.addSeparator(); popup.addAction(&toggleControlLayoutAction); popup.addSeparator(); popup.addAction(&filtersAction); @@ -2939,7 +3143,7 @@ void OBSBasic::DuplicateSelectedScene() return; if (name.empty()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); continue; @@ -2947,7 +3151,7 @@ void OBSBasic::DuplicateSelectedScene() obs_source_t *source = obs_get_source_by_name(name.c_str()); if (source) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -3107,6 +3311,8 @@ void OBSBasic::DrawBackdrop(float cx, float cy) if (!box) return; + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawBackdrop"); + gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID); gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color"); gs_technique_t *tech = gs_effect_get_technique(solid, "Solid"); @@ -3129,10 +3335,14 @@ void OBSBasic::DrawBackdrop(float cx, float cy) gs_technique_end(tech); gs_load_vertexbuffer(nullptr); + + GS_DEBUG_MARKER_END(); } void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) { + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "RenderMain"); + OBSBasic *window = static_cast(data); obs_video_info ovi; @@ -3144,6 +3354,18 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) gs_viewport_push(); gs_projection_push(); + obs_display_t *display = window->ui->preview->GetDisplay(); + uint32_t width, height; + obs_display_size(display, &width, &height); + float right = float(width) - window->previewX; + float bottom = float(height) - window->previewY; + + gs_ortho(-window->previewX, right, + -window->previewY, bottom, + -100.0f, 100.0f); + + window->ui->preview->DrawOverflow(); + /* --------------------------------------- */ gs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height), @@ -3165,9 +3387,6 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) /* --------------------------------------- */ - QSize previewSize = GetPixelSize(window->ui->preview); - float right = float(previewSize.width()) - window->previewX; - float bottom = float(previewSize.height()) - window->previewY; gs_ortho(-window->previewX, right, -window->previewY, bottom, @@ -3181,6 +3400,8 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) gs_projection_pop(); gs_viewport_pop(); + GS_DEBUG_MARKER_END(); + UNUSED_PARAMETER(cx); UNUSED_PARAMETER(cy); } @@ -3203,6 +3424,11 @@ void OBSBasic::SetService(obs_service_t *newService) service = newService; } +int OBSBasic::GetTransitionDuration() +{ + return ui->transitionDuration->value(); +} + bool OBSBasic::StreamingActive() const { if (!outputHandler) @@ -3266,10 +3492,19 @@ void OBSBasic::ResetUI() bool studioPortraitLayout = config_get_bool(GetGlobalConfig(), "BasicWindow", "StudioPortraitLayout"); + bool labels = config_get_bool(GetGlobalConfig(), + "BasicWindow", "StudioModeLabels"); + if (studioPortraitLayout) ui->previewLayout->setDirection(QBoxLayout::TopToBottom); else ui->previewLayout->setDirection(QBoxLayout::LeftToRight); + + if (previewProgramMode) + ui->previewLabel->setHidden(!labels); + + if (programLabel) + programLabel->setHidden(!labels); } int OBSBasic::ResetVideo() @@ -3543,15 +3778,20 @@ void OBSBasic::ClearSceneData() void OBSBasic::closeEvent(QCloseEvent *event) { + /* Do not close window if inside of a temporary event loop because we + * could be inside of an Auth::LoadUI call. Keep trying once per + * second until we've exit any known sub-loops. */ + if (os_atomic_load_long(&insideEventLoop) != 0) { + QTimer::singleShot(1000, this, SLOT(close())); + event->ignore(); + return; + } + if (isVisible()) config_set_string(App()->GlobalConfig(), "BasicWindow", "geometry", saveGeometry().toBase64().constData()); - config_set_string(App()->GlobalConfig(), - "BasicWindow", "DockState", - saveState().toBase64().constData()); - if (outputHandler && outputHandler->Active()) { SetShowing(true); @@ -3580,7 +3820,13 @@ void OBSBasic::closeEvent(QCloseEvent *event) signalHandlers.clear(); + Auth::Save(); SaveProjectNow(); + auth.reset(); + + config_set_string(App()->GlobalConfig(), + "BasicWindow", "DockState", + saveState().toBase64().constData()); if (api) api->on_event(OBS_FRONTEND_EVENT_EXIT); @@ -3640,9 +3886,28 @@ void OBSBasic::on_actionRemux_triggered() void OBSBasic::on_action_Settings_triggered() { + static bool settings_already_executing = false; + + /* Do not load settings window if inside of a temporary event loop + * because we could be inside of an Auth::LoadUI call. Keep trying + * once per second until we've exit any known sub-loops. */ + if (os_atomic_load_long(&insideEventLoop) != 0) { + QTimer::singleShot(1000, this, + SLOT(on_action_Settings_triggered())); + return; + } + + if (settings_already_executing) { + return; + } + + settings_already_executing = true; + OBSBasicSettings settings(this); settings.exec(); SystemTray(false); + + settings_already_executing = false; } void OBSBasic::on_actionAdvAudioProperties_triggered() @@ -3712,7 +3977,7 @@ static void AddProjectorMenuMonitors(QMenu *parent, QObject *target, QRect screenGeometry = screens[i]->geometry(); QString str = QString("%1 %2: %3x%4 @ %5,%6"). arg(QTStr("Display"), - QString::number(i), + QString::number(i + 1), QString::number(screenGeometry.width()), QString::number(screenGeometry.height()), QString::number(screenGeometry.x()), @@ -3726,7 +3991,6 @@ static void AddProjectorMenuMonitors(QMenu *parent, QObject *target, void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) { QListWidgetItem *item = ui->scenes->itemAt(pos); - QPointer sceneProjectorMenu; QMenu popup(this); QMenu order(QTStr("Basic.MainMenu.Edit.Order"), this); @@ -3734,9 +3998,19 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) this, SLOT(on_actionAddScene_triggered())); if (item) { + QAction *pasteFilters = new QAction( + QTStr("Paste.Filters"), this); + pasteFilters->setEnabled(copyFiltersString); + connect(pasteFilters, SIGNAL(triggered()), this, + SLOT(ScenePasteFilters())); + popup.addSeparator(); popup.addAction(QTStr("Duplicate"), this, SLOT(DuplicateSelectedScene())); + popup.addAction(QTStr("Copy.Filters"), + this, SLOT(SceneCopyFilters())); + popup.addAction(pasteFilters); + popup.addSeparator(); popup.addAction(QTStr("Rename"), this, SLOT(EditSceneName())); popup.addAction(QTStr("Remove"), @@ -3756,6 +4030,7 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) popup.addSeparator(); + delete sceneProjectorMenu; sceneProjectorMenu = new QMenu(QTStr("SceneProjector")); AddProjectorMenuMonitors(sceneProjectorMenu, this, SLOT(OpenSceneProjector())); @@ -3772,8 +4047,9 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) popup.addSeparator(); - QMenu *transitionMenu = CreatePerSceneTransitionMenu(); - popup.addMenu(transitionMenu); + delete perSceneTransitionMenu; + perSceneTransitionMenu = CreatePerSceneTransitionMenu(); + popup.addMenu(perSceneTransitionMenu); /* ---------------------- */ @@ -3828,7 +4104,7 @@ void OBSBasic::on_actionAddScene_triggered() if (accepted) { if (name.empty()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); on_actionAddScene_triggered(); @@ -3837,7 +4113,7 @@ void OBSBasic::on_actionAddScene_triggered() obs_source_t *source = obs_get_source_by_name(name.c_str()); if (source) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text")); @@ -3933,9 +4209,8 @@ void OBSBasic::SetDeinterlacingOrder() obs_source_set_deinterlace_field_order(source, order); } -QMenu *OBSBasic::AddDeinterlacingMenu(obs_source_t *source) +QMenu *OBSBasic::AddDeinterlacingMenu(QMenu *menu, obs_source_t *source) { - QMenu *menu = new QMenu(QTStr("Deinterlacing")); obs_deinterlace_mode deinterlaceMode = obs_source_get_deinterlace_mode(source); obs_deinterlace_field_order deinterlaceOrder = @@ -3985,9 +4260,8 @@ void OBSBasic::SetScaleFilter() obs_sceneitem_set_scale_filter(sceneItem, mode); } -QMenu *OBSBasic::AddScaleFilteringMenu(obs_sceneitem_t *item) +QMenu *OBSBasic::AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item) { - QMenu *menu = new QMenu(QTStr("ScaleFiltering")); obs_scale_type scaleFilter = obs_sceneitem_get_scale_filter(item); QAction *action; @@ -4003,14 +4277,15 @@ QMenu *OBSBasic::AddScaleFilteringMenu(obs_sceneitem_t *item) ADD_MODE("ScaleFiltering.Bilinear", OBS_SCALE_BILINEAR); ADD_MODE("ScaleFiltering.Bicubic", OBS_SCALE_BICUBIC); ADD_MODE("ScaleFiltering.Lanczos", OBS_SCALE_LANCZOS); + ADD_MODE("ScaleFiltering.Area", OBS_SCALE_AREA); #undef ADD_MODE return menu; } -QMenu *OBSBasic::AddBackgroundColorMenu(obs_sceneitem_t *item) +QMenu *OBSBasic::AddBackgroundColorMenu(QMenu *menu, QWidgetAction *widgetAction, + ColorSelect *select, obs_sceneitem_t *item) { - QMenu *menu = new QMenu(QTStr("ChangeBG")); QAction *action; menu->setStyleSheet(QString( @@ -4043,8 +4318,6 @@ QMenu *OBSBasic::AddBackgroundColorMenu(obs_sceneitem_t *item) menu->addSeparator(); - QWidgetAction *widgetAction = new QWidgetAction(menu); - ColorSelect *select = new ColorSelect(menu); widgetAction->setDefaultWidget(select); for (int i = 1; i < 9; i++) { @@ -4065,11 +4338,23 @@ QMenu *OBSBasic::AddBackgroundColorMenu(obs_sceneitem_t *item) return menu; } +ColorSelect::ColorSelect(QWidget *parent) + : QWidget(parent), + ui(new Ui::ColorSelect) +{ + ui->setupUi(this); +} + void OBSBasic::CreateSourcePopupMenu(int idx, bool preview) { QMenu popup(this); - QPointer previewProjector; - QPointer sourceProjector; + delete previewProjectorSource; + delete sourceProjector; + delete scaleFilteringMenu; + delete colorMenu; + delete colorWidgetAction; + delete colorSelect; + delete deinterlaceMenu; if (preview) { QAction *action = popup.addAction( @@ -4084,11 +4369,11 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview) popup.addAction(ui->actionLockPreview); popup.addMenu(ui->scalingMenu); - previewProjector = new QMenu(QTStr("PreviewProjector")); - AddProjectorMenuMonitors(previewProjector, this, + previewProjectorSource = new QMenu(QTStr("PreviewProjector")); + AddProjectorMenuMonitors(previewProjectorSource, this, SLOT(OpenPreviewProjector())); - popup.addMenu(previewProjector); + popup.addMenu(previewProjectorSource); QAction *previewWindow = popup.addAction( QTStr("PreviewWindow"), @@ -4141,7 +4426,11 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview) OBS_SOURCE_AUDIO; QAction *action; - popup.addMenu(AddBackgroundColorMenu(sceneItem)); + colorMenu = new QMenu(QTStr("ChangeBG")); + colorWidgetAction = new QWidgetAction(colorMenu); + colorSelect = new ColorSelect(colorMenu); + popup.addMenu(AddBackgroundColorMenu(colorMenu, + colorWidgetAction, colorSelect, sceneItem)); popup.addAction(QTStr("Rename"), this, SLOT(EditSceneItemName())); popup.addAction(QTStr("Remove"), this, @@ -4171,11 +4460,25 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview) } if (isAsyncVideo) { - popup.addMenu(AddDeinterlacingMenu(source)); + deinterlaceMenu = new QMenu(QTStr("Deinterlacing")); + popup.addMenu(AddDeinterlacingMenu(deinterlaceMenu, source)); popup.addSeparator(); } - popup.addMenu(AddScaleFilteringMenu(sceneItem)); + QAction *resizeOutput = popup.addAction( + QTStr("ResizeOutputSizeOfSource"), this, + SLOT(ResizeOutputSizeOfSource())); + + int width = obs_source_get_width(source); + int height = obs_source_get_height(source); + + resizeOutput->setEnabled(!obs_video_active()); + + if (width == 0 || height == 0) + resizeOutput->setEnabled(false); + + scaleFilteringMenu = new QMenu(QTStr("ScaleFiltering")); + popup.addMenu(AddScaleFilteringMenu(scaleFilteringMenu, sceneItem)); popup.addSeparator(); popup.addMenu(sourceProjector); @@ -4561,7 +4864,7 @@ void OBSBasic::logUploadFinished(const QString &text, const QString &error) ui->menuLogFiles->setEnabled(true); if (text.isEmpty()) { - OBSMessageBox::information(this, + OBSMessageBox::critical(this, QTStr("LogReturnDialog.ErrorUploadingLog"), error); return; @@ -4590,11 +4893,11 @@ static void RenameListItem(OBSBasic *parent, QListWidget *listWidget, listItem->setText(QT_UTF8(prevName)); if (foundSource) { - OBSMessageBox::information(parent, + OBSMessageBox::warning(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text")); } else if (name.empty()) { - OBSMessageBox::information(parent, + OBSMessageBox::warning(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); } @@ -4675,6 +4978,9 @@ void OBSBasic::StartStreaming() } if (!outputHandler->StartStreaming(service)) { + QString message = !outputHandler->lastError.empty() + ? QTStr(outputHandler->lastError.c_str()) + : QTStr("Output.StartFailedGeneric"); ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); ui->streamButton->setChecked(false); @@ -4684,9 +4990,8 @@ void OBSBasic::StartStreaming() sysTrayStream->setEnabled(true); } - QMessageBox::critical(this, - QTStr("Output.StartStreamFailed"), - QTStr("Output.StartFailedGeneric")); + QMessageBox::critical(this, QTStr("Output.StartStreamFailed"), + message); return; } @@ -4731,7 +5036,8 @@ inline void OBSBasic::OnActivate() UpdateProcessPriority(); if (trayIcon) - trayIcon->setIcon(QIcon(":/res/images/tray_active.png")); + trayIcon->setIcon(QIcon::fromTheme("obs-tray-active", + QIcon(":/res/images/tray_active.png"))); } } @@ -4744,7 +5050,8 @@ inline void OBSBasic::OnDeactivate() ClearProcessPriority(); if (trayIcon) - trayIcon->setIcon(QIcon(":/res/images/obs.png")); + trayIcon->setIcon(QIcon::fromTheme("obs-tray", + QIcon(":/res/images/obs.png"))); } } @@ -4844,6 +5151,9 @@ void OBSBasic::StreamDelayStopping(int sec) ui->streamButton->setMenu(startStreamMenu); ui->statusbar->StreamDelayStopping(sec); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING); } void OBSBasic::StreamingStart() @@ -4880,9 +5190,10 @@ void OBSBasic::StreamStopping() void OBSBasic::StreamingStop(int code, QString last_error) { - const char *errorDescription; + const char *errorDescription = ""; DStr errorMessage; bool use_last_error = false; + bool encode_error = false; switch (code) { case OBS_OUTPUT_BAD_PATH: @@ -4898,6 +5209,10 @@ void OBSBasic::StreamingStop(int code, QString last_error) errorDescription = Str("Output.ConnectFail.InvalidStream"); break; + case OBS_OUTPUT_ENCODE_ERROR: + encode_error = true; + break; + default: case OBS_OUTPUT_ERROR: use_last_error = true; @@ -4936,10 +5251,16 @@ void OBSBasic::StreamingStop(int code, QString last_error) blog(LOG_INFO, STREAMING_STOP); - if (code != OBS_OUTPUT_SUCCESS && isVisible()) { + if (encode_error) { + OBSMessageBox::information(this, + QTStr("Output.StreamEncodeError.Title"), + QTStr("Output.StreamEncodeError.Msg")); + + } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { OBSMessageBox::information(this, QTStr("Output.ConnectFail.Title"), QT_UTF8(errorMessage)); + } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { SysTrayNotify(QT_UTF8(errorDescription), QSystemTrayIcon::Warning); } @@ -4951,6 +5272,47 @@ void OBSBasic::StreamingStop(int code, QString last_error) } } +void OBSBasic::AutoRemux() +{ + const char *mode = config_get_string(basicConfig, "Output", "Mode"); + bool advanced = astrcmpi(mode, "Advanced") == 0; + + const char *path = !advanced + ? config_get_string(basicConfig, "SimpleOutput", "FilePath") + : config_get_string(basicConfig, "AdvOut", "RecFilePath"); + + /* do not save if using FFmpeg output in advanced output mode */ + if (advanced) { + const char *type = config_get_string(basicConfig, "AdvOut", + "RecType"); + if (astrcmpi(type, "FFmpeg") == 0) { + return; + } + } + + QString input; + input += path; + input += "/"; + input += remuxFilename.c_str(); + + QFileInfo fi(remuxFilename.c_str()); + + /* do not remux if lossless */ + if (fi.suffix().compare("avi", Qt::CaseInsensitive) == 0) { + return; + } + + QString output; + output += path; + output += "/"; + output += fi.completeBaseName(); + output += ".mp4"; + + OBSRemux *remux = new OBSRemux(path, this, true); + remux->show(); + remux->AutoRemux(input, output); +} + void OBSBasic::StartRecording() { if (outputHandler->RecordingActive()) @@ -5007,7 +5369,7 @@ void OBSBasic::RecordingStart() blog(LOG_INFO, RECORDING_START); } -void OBSBasic::RecordingStop(int code) +void OBSBasic::RecordingStop(int code, QString last_error) { ui->statusbar->RecordingStopped(); ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); @@ -5019,19 +5381,37 @@ void OBSBasic::RecordingStop(int code) blog(LOG_INFO, RECORDING_STOP); if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { - OBSMessageBox::information(this, + OBSMessageBox::critical(this, QTStr("Output.RecordFail.Title"), QTStr("Output.RecordFail.Unsupported")); + } else if (code == OBS_OUTPUT_ENCODE_ERROR && isVisible()) { + OBSMessageBox::warning(this, + QTStr("Output.RecordError.Title"), + QTStr("Output.RecordError.EncodeErrorMsg")); + } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("Output.RecordNoSpace.Title"), QTStr("Output.RecordNoSpace.Msg")); } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { - OBSMessageBox::information(this, + + const char *errorDescription; + DStr errorMessage; + bool use_last_error = true; + + errorDescription = Str("Output.RecordError.Msg"); + + 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); + + OBSMessageBox::critical(this, QTStr("Output.RecordError.Title"), - QTStr("Output.RecordError.Msg")); + QT_UTF8(errorMessage)); } else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) { SysTrayNotify(QTStr("Output.RecordFail.Unsupported"), @@ -5049,6 +5429,9 @@ void OBSBasic::RecordingStop(int code) if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPED); + if (remuxAfterRecord) + AutoRemux(); + OnDeactivate(); } @@ -5081,6 +5464,7 @@ void OBSBasic::StartReplayBuffer() OBSMessageBox::information(this, RP_NO_HOTKEY_TITLE, RP_NO_HOTKEY_TEXT); + replayBufferButton->setChecked(false); return; } @@ -5126,6 +5510,7 @@ void OBSBasic::ReplayBufferStart() return; replayBufferButton->setText(QTStr("Basic.Main.StopReplayBuffer")); + replayBufferButton->setChecked(true); if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText(replayBufferButton->text()); @@ -5159,6 +5544,7 @@ void OBSBasic::ReplayBufferStop(int code) return; replayBufferButton->setText(QTStr("Basic.Main.StartReplayBuffer")); + replayBufferButton->setChecked(false); if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText(replayBufferButton->text()); @@ -5166,17 +5552,17 @@ void OBSBasic::ReplayBufferStop(int code) blog(LOG_INFO, REPLAY_BUFFER_STOP); if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { - OBSMessageBox::information(this, + OBSMessageBox::critical(this, QTStr("Output.RecordFail.Title"), QTStr("Output.RecordFail.Unsupported")); } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("Output.RecordNoSpace.Title"), QTStr("Output.RecordNoSpace.Msg")); } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { - OBSMessageBox::information(this, + OBSMessageBox::critical(this, QTStr("Output.RecordError.Title"), QTStr("Output.RecordError.Msg")); @@ -5207,14 +5593,16 @@ bool OBSBasic::NoSourcesConfirmation() msg += "\n\n"; msg += QTStr("NoSources.Text.AddSource"); - QMessageBox messageBox(QMessageBox::Question, - QTStr("NoSources.title"), - msg, - QMessageBox::Yes | QMessageBox::No, - this); - messageBox.setDefaultButton(QMessageBox::No); + QMessageBox messageBox(this); + messageBox.setWindowTitle(QTStr("NoSources.Title")); + messageBox.setText(msg); + QAbstractButton *Yes = messageBox.addButton(QTStr("Yes"), + QMessageBox::YesRole); + messageBox.addButton(QTStr("No"), QMessageBox::NoRole); + messageBox.setIcon(QMessageBox::Question); + messageBox.exec(); - if (QMessageBox::No == messageBox.exec()) + if (messageBox.clickedButton() != Yes) return false; } @@ -5249,7 +5637,21 @@ void OBSBasic::on_streamButton_clicked() bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", "WarnBeforeStartingStream"); - if (confirm && isVisible()) { + obs_data_t *settings = obs_service_get_settings(service); + bool bwtest = obs_data_get_bool(settings, "bwtest"); + obs_data_release(settings); + + if (bwtest && isVisible()) { + QMessageBox::StandardButton button = + OBSMessageBox::question(this, + QTStr("ConfirmBWTest.Title"), + QTStr("ConfirmBWTest.Text")); + + if (button == QMessageBox::No) { + ui->streamButton->setChecked(false); + return; + } + } else if (confirm && isVisible()) { QMessageBox::StandardButton button = OBSMessageBox::question(this, QTStr("ConfirmStart.Title"), @@ -5361,7 +5763,7 @@ void OBSBasic::on_previewDisabledLabel_customContextMenuRequested( const QPoint &pos) { QMenu popup(this); - QPointer previewProjector; + delete previewProjectorMain; QAction *action = popup.addAction( QTStr("Basic.Main.PreviewConextMenu.Enable"), @@ -5369,15 +5771,15 @@ void OBSBasic::on_previewDisabledLabel_customContextMenuRequested( action->setCheckable(true); action->setChecked(obs_display_enabled(ui->preview->GetDisplay())); - previewProjector = new QMenu(QTStr("PreviewProjector")); - AddProjectorMenuMonitors(previewProjector, this, + previewProjectorMain = new QMenu(QTStr("PreviewProjector")); + AddProjectorMenuMonitors(previewProjectorMain, this, SLOT(OpenPreviewProjector())); QAction *previewWindow = popup.addAction( QTStr("PreviewWindow"), this, SLOT(OpenPreviewWindow())); - popup.addMenu(previewProjector); + popup.addMenu(previewProjectorMain); popup.addAction(previewWindow); popup.exec(QCursor::pos()); @@ -5421,7 +5823,7 @@ void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const } else if (strcmp(val, "24 NTSC") == 0) { num = 24000; den = 1001; - } else if (strcmp(val, "25") == 0) { + } else if (strcmp(val, "25 PAL") == 0) { num = 25; den = 1; } else if (strcmp(val, "29.97") == 0) { @@ -5430,6 +5832,9 @@ void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const } else if (strcmp(val, "48") == 0) { num = 48; den = 1; + } else if (strcmp(val, "50 PAL") == 0) { + num = 50; + den = 1; } else if (strcmp(val, "59.94") == 0) { num = 60000; den = 1001; @@ -5740,20 +6145,39 @@ void OBSBasic::on_actionStretchToScreen_triggered() &boundsType); } -static bool center_to_scene(obs_scene_t *, obs_sceneitem_t *item, void *) +enum class CenterType { + Scene, + Vertical, + Horizontal +}; + +static bool center_to_scene(obs_scene_t *, obs_sceneitem_t *item, void *param) { + CenterType centerType = *reinterpret_cast(param); + vec3 tl, br, itemCenter, screenCenter, offset; obs_video_info ovi; + obs_transform_info oti; if (obs_sceneitem_is_group(item)) - obs_sceneitem_group_enum_items(item, center_to_scene, nullptr); + obs_sceneitem_group_enum_items(item, center_to_scene, + ¢erType); if (!obs_sceneitem_selected(item)) return true; obs_get_video_info(&ovi); + obs_sceneitem_get_info(item, &oti); + + if (centerType == CenterType::Scene) + vec3_set(&screenCenter, float(ovi.base_width), + float(ovi.base_height), 0.0f); + else if (centerType == CenterType::Vertical) + vec3_set(&screenCenter, float(oti.bounds.x), + float(ovi.base_height), 0.0f); + else if (centerType == CenterType::Horizontal) + vec3_set(&screenCenter, float(ovi.base_width), + float(oti.bounds.y), 0.0f); - vec3_set(&screenCenter, float(ovi.base_width), - float(ovi.base_height), 0.0f); vec3_mulf(&screenCenter, &screenCenter, 0.5f); GetItemBox(item, tl, br); @@ -5765,13 +6189,31 @@ static bool center_to_scene(obs_scene_t *, obs_sceneitem_t *item, void *) vec3_sub(&offset, &screenCenter, &itemCenter); vec3_add(&tl, &tl, &offset); + if (centerType == CenterType::Vertical) + tl.x = oti.pos.x; + else if (centerType == CenterType::Horizontal) + tl.y = oti.pos.y; + SetItemTL(item, tl); return true; }; void OBSBasic::on_actionCenterToScreen_triggered() { - obs_scene_enum_items(GetCurrentScene(), center_to_scene, nullptr); + CenterType centerType = CenterType::Scene; + obs_scene_enum_items(GetCurrentScene(), center_to_scene, ¢erType); +} + +void OBSBasic::on_actionVerticalCenter_triggered() +{ + CenterType centerType = CenterType::Vertical; + obs_scene_enum_items(GetCurrentScene(), center_to_scene, ¢erType); +} + +void OBSBasic::on_actionHorizontalCenter_triggered() +{ + CenterType centerType = CenterType::Horizontal; + obs_scene_enum_items(GetCurrentScene(), center_to_scene, ¢erType); } void OBSBasic::EnablePreviewDisplay(bool enable) @@ -5787,6 +6229,24 @@ void OBSBasic::TogglePreview() EnablePreviewDisplay(previewEnabled); } +void OBSBasic::EnablePreview() +{ + if (previewProgramMode) + return; + + previewEnabled = true; + EnablePreviewDisplay(true); +} + +void OBSBasic::DisablePreview() +{ + if (previewProgramMode) + return; + + previewEnabled = false; + EnablePreviewDisplay(false); +} + static bool nudge_callback(obs_scene_t*, obs_sceneitem_t *item, void *param) { if (obs_sceneitem_locked(item)) @@ -6001,7 +6461,7 @@ void OBSBasic::OpenSavedProjectors() } } - if (projector && !info->geometry.empty()) { + if (projector && !info->geometry.empty() && info->monitor < 0) { QByteArray byteArray = QByteArray::fromBase64( QByteArray(info->geometry.c_str())); projector->restoreGeometry(byteArray); @@ -6075,6 +6535,36 @@ int OBSBasic::GetProfilePath(char *path, size_t size, const char *file) const void OBSBasic::on_resetUI_triggered() { + /* prune deleted extra docks */ + for (int i = extraDocks.size() - 1; i >= 0 ; i--) { + if (!extraDocks[i]) { + extraDocks.removeAt(i); + } + } + + if (extraDocks.size()) { + QMessageBox::StandardButton button = QMessageBox::question( + this, + QTStr("ResetUIWarning.Title"), + QTStr("ResetUIWarning.Text")); + + if (button == QMessageBox::No) + return; + } + + /* undock/hide/center extra docks */ + for (int i = extraDocks.size() - 1; i >= 0 ; i--) { + if (extraDocks[i]) { + extraDocks[i]->setVisible(true); + extraDocks[i]->setFloating(true); + extraDocks[i]->move( + frameGeometry().topLeft() + + rect().center() - + extraDocks[i]->rect().center()); + extraDocks[i]->setVisible(false); + } + } + restoreState(startingDockLayout); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) @@ -6109,6 +6599,8 @@ void OBSBasic::on_resetUI_triggered() ui->mixerDock->setVisible(true); ui->transitionsDock->setVisible(true); ui->controlsDock->setVisible(true); + statsDock->setVisible(false); + statsDock->setFloating(true); resizeDocks(docks, {cy, cy, cy, cy, cy}, Qt::Vertical); resizeDocks(docks, sizes, Qt::Horizontal); @@ -6121,11 +6613,23 @@ void OBSBasic::on_lockUI_toggled(bool lock) ? QDockWidget::NoDockWidgetFeatures : QDockWidget::AllDockWidgetFeatures; - ui->scenesDock->setFeatures(features); - ui->sourcesDock->setFeatures(features); - ui->mixerDock->setFeatures(features); - ui->transitionsDock->setFeatures(features); - ui->controlsDock->setFeatures(features); + QDockWidget::DockWidgetFeatures mainFeatures = features; + mainFeatures &= ~QDockWidget::QDockWidget::DockWidgetClosable; + + ui->scenesDock->setFeatures(mainFeatures); + ui->sourcesDock->setFeatures(mainFeatures); + ui->mixerDock->setFeatures(mainFeatures); + ui->transitionsDock->setFeatures(mainFeatures); + ui->controlsDock->setFeatures(mainFeatures); + statsDock->setFeatures(features); + + for (int i = extraDocks.size() - 1; i >= 0 ; i--) { + if (!extraDocks[i]) { + extraDocks.removeAt(i); + } else { + extraDocks[i]->setFeatures(features); + } + } } void OBSBasic::on_toggleListboxToolbars_toggled(bool visible) @@ -6283,8 +6787,8 @@ void OBSBasic::ToggleShowHide() void OBSBasic::SystemTrayInit() { - trayIcon.reset(new QSystemTrayIcon(QIcon(":/res/images/obs.png"), - this)); + trayIcon.reset(new QSystemTrayIcon(QIcon::fromTheme("obs-tray", + QIcon(":/res/images/obs.png")), this)); trayIcon->setToolTip("OBS Studio"); showHide = new QAction(QTStr("Basic.SystemTray.Show"), @@ -6298,6 +6802,23 @@ void OBSBasic::SystemTrayInit() exit = new QAction(QTStr("Exit"), trayIcon.data()); + trayMenu = new QMenu; + previewProjector = new QMenu(QTStr("PreviewProjector")); + studioProgramProjector = new QMenu(QTStr("StudioProgramProjector")); + AddProjectorMenuMonitors(previewProjector, this, + SLOT(OpenPreviewProjector())); + AddProjectorMenuMonitors(studioProgramProjector, this, + SLOT(OpenStudioProgramProjector())); + trayMenu->addAction(showHide); + trayMenu->addMenu(previewProjector); + trayMenu->addMenu(studioProgramProjector); + trayMenu->addAction(sysTrayStream); + trayMenu->addAction(sysTrayRecord); + trayMenu->addAction(sysTrayReplayBuffer); + trayMenu->addAction(exit); + trayIcon->setContextMenu(trayMenu); + trayIcon->show(); + if (outputHandler && !outputHandler->replayBuffer) sysTrayReplayBuffer->setEnabled(false); @@ -6315,39 +6836,26 @@ void OBSBasic::SystemTrayInit() this, &OBSBasic::ReplayBufferClicked); connect(exit, SIGNAL(triggered()), this, SLOT(close())); - - trayMenu = new QMenu; } void OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason) { - if (reason == QSystemTrayIcon::Trigger) { - ToggleShowHide(); - } else if (reason == QSystemTrayIcon::Context) { - QMenu *previewProjector = new QMenu(QTStr("PreviewProjector")); - AddProjectorMenuMonitors(previewProjector, this, - SLOT(OpenPreviewProjector())); - QMenu *studioProgramProjector = new QMenu( - QTStr("StudioProgramProjector")); - AddProjectorMenuMonitors(studioProgramProjector, this, - SLOT(OpenStudioProgramProjector())); + // Refresh projector list + previewProjector->clear(); + studioProgramProjector->clear(); + AddProjectorMenuMonitors(previewProjector, this, + SLOT(OpenPreviewProjector())); + AddProjectorMenuMonitors(studioProgramProjector, this, + SLOT(OpenStudioProgramProjector())); - trayMenu->clear(); - trayMenu->addAction(showHide); - trayMenu->addMenu(previewProjector); - trayMenu->addMenu(studioProgramProjector); - trayMenu->addAction(sysTrayStream); - trayMenu->addAction(sysTrayRecord); - trayMenu->addAction(sysTrayReplayBuffer); - trayMenu->addAction(exit); - trayMenu->popup(QCursor::pos()); - } + if (reason == QSystemTrayIcon::Trigger) + ToggleShowHide(); } void OBSBasic::SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n) { - if (QSystemTrayIcon::supportsMessages()) { + if (trayIcon && QSystemTrayIcon::supportsMessages()) { QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::MessageIcon(n); trayIcon->showMessage("OBS Studio", text, icon, 10000); @@ -6358,6 +6866,8 @@ void OBSBasic::SystemTray(bool firstStarted) { if (!QSystemTrayIcon::isSystemTrayAvailable()) return; + if (!trayIcon && !firstStarted) + return; bool sysTrayWhenStarted = config_get_bool(GetGlobalConfig(), "BasicWindow", "SysTrayWhenStarted"); @@ -6440,6 +6950,48 @@ void OBSBasic::on_actionPasteDup_triggered() on_actionPasteTransform_triggered(); } +void OBSBasic::AudioMixerCopyFilters() +{ + QAction *action = reinterpret_cast(sender()); + VolControl *vol = action->property("volControl").value(); + obs_source_t *source = vol->GetSource(); + + copyFiltersString = obs_source_get_name(source); +} + +void OBSBasic::AudioMixerPasteFilters() +{ + QAction *action = reinterpret_cast(sender()); + VolControl *vol = action->property("volControl").value(); + obs_source_t *dstSource = vol->GetSource(); + + OBSSource source = obs_get_source_by_name(copyFiltersString); + obs_source_release(source); + + if (source == dstSource) + return; + + obs_source_copy_filters(dstSource, source); +} + +void OBSBasic::SceneCopyFilters() +{ + copyFiltersString = obs_source_get_name(GetCurrentSceneSource()); +} + +void OBSBasic::ScenePasteFilters() +{ + OBSSource source = obs_get_source_by_name(copyFiltersString); + obs_source_release(source); + + OBSSource dstSource = GetCurrentSceneSource(); + + if (source == dstSource) + return; + + obs_source_copy_filters(dstSource, source); +} + void OBSBasic::on_actionCopyFilters_triggered() { OBSSceneItem item = GetCurrentSceneItem(); @@ -6664,9 +7216,132 @@ void OBSBasic::on_stats_triggered() stats = statsDlg; } -ColorSelect::ColorSelect(QWidget *parent) - : QWidget(parent), - ui(new Ui::ColorSelect) +void OBSBasic::on_actionShowAbout_triggered() { - ui->setupUi(this); + if (about) + about->close(); + + about = new OBSAbout(this); + about->show(); + + about->setAttribute(Qt::WA_DeleteOnClose, true); +} + +void OBSBasic::ResizeOutputSizeOfSource() +{ + if (ui->streamButton->isChecked() || ui->recordButton->isChecked() || + (replayBufferButton && replayBufferButton->isChecked())) + return; + + QMessageBox resize_output(this); + resize_output.setText(QTStr("ResizeOutputSizeOfSource.Text") + + "\n\n" + QTStr("ResizeOutputSizeOfSource.Continue")); + QAbstractButton *Yes = resize_output.addButton(QTStr("Yes"), + QMessageBox::YesRole); + resize_output.addButton(QTStr("No"), QMessageBox::NoRole); + resize_output.setIcon(QMessageBox::Warning); + resize_output.setWindowTitle(QTStr("ResizeOutputSizeOfSource")); + resize_output.exec(); + + if (resize_output.clickedButton() != Yes) + return; + + OBSSource source = obs_sceneitem_get_source(GetCurrentSceneItem()); + + int width = obs_source_get_width(source); + int height = obs_source_get_height(source); + + config_set_uint(basicConfig, "Video", "BaseCX", width); + config_set_uint(basicConfig, "Video", "BaseCY", height); + config_set_uint(basicConfig, "Video", "OutputCX", width); + config_set_uint(basicConfig, "Video", "OutputCY", height); + + ResetVideo(); + on_actionFitToScreen_triggered(); +} + +QAction *OBSBasic::AddDockWidget(QDockWidget *dock) +{ + QAction *action = ui->viewMenuDocks->addAction(dock->windowTitle()); + action->setCheckable(true); + assignDockToggle(dock, action); + extraDocks.push_back(dock); + + bool lock = ui->lockUI->isChecked(); + QDockWidget::DockWidgetFeatures features = lock + ? QDockWidget::NoDockWidgetFeatures + : QDockWidget::AllDockWidgetFeatures; + + dock->setFeatures(features); + + /* prune deleted docks */ + for (int i = extraDocks.size() - 1; i >= 0 ; i--) { + if (!extraDocks[i]) { + extraDocks.removeAt(i); + } + } + + return action; +} + +OBSBasic *OBSBasic::Get() +{ + return reinterpret_cast(App()->GetMainWindow()); +} + +bool OBSBasic::StreamingActive() +{ + if (!outputHandler) + return false; + return outputHandler->StreamingActive(); +} + +bool OBSBasic::RecordingActive() +{ + if (!outputHandler) + return false; + return outputHandler->RecordingActive(); +} + +bool OBSBasic::ReplayBufferActive() +{ + if (!outputHandler) + return false; + return outputHandler->ReplayBufferActive(); +} + +SceneRenameDelegate::SceneRenameDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +void SceneRenameDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + QStyledItemDelegate::setEditorData(editor, index); + QLineEdit *lineEdit = qobject_cast(editor); + if (lineEdit) + lineEdit->selectAll(); +} + +bool SceneRenameDelegate::eventFilter(QObject *editor, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) { + QLineEdit *lineEdit = qobject_cast(editor); + if (lineEdit) + lineEdit->undo(); + } + } + + return QStyledItemDelegate::eventFilter(editor, event); +} + +void OBSBasic::UpdatePatronJson(const QString &text, const QString &error) +{ + if (!error.isEmpty()) + return; + + patronJson = QT_TO_UTF8(text); } diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 24e0036..8641f53 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include #include #include @@ -30,6 +32,8 @@ #include "window-basic-adv-audio.hpp" #include "window-basic-filters.hpp" #include "window-projector.hpp" +#include "window-basic-about.hpp" +#include "auth-base.hpp" #include @@ -52,6 +56,7 @@ class OBSBasicStats; #define AUX_AUDIO_1 Str("AuxAudioDevice1") #define AUX_AUDIO_2 Str("AuxAudioDevice2") #define AUX_AUDIO_3 Str("AuxAudioDevice3") +#define AUX_AUDIO_4 Str("AuxAudioDevice4") #define SIMPLE_ENCODER_X264 "x264" #define SIMPLE_ENCODER_X264_LOWCPU "x264_lowcpu" @@ -97,13 +102,26 @@ private: std::shared_ptr renamedSignal; }; +class ColorSelect : public QWidget { + +public: + explicit ColorSelect(QWidget *parent = 0); + +private: + std::unique_ptr ui; +}; + class OBSBasic : public OBSMainWindow { Q_OBJECT + friend class OBSAbout; friend class OBSBasicPreview; friend class OBSBasicStatusBar; friend class OBSBasicSourceSelect; friend class OBSBasicSettings; + friend class Auth; + friend class AutoConfig; + friend class AutoConfigStreamPage; friend struct OBSStudioAPI; enum class MoveDir { @@ -124,10 +142,14 @@ class OBSBasic : public OBSMainWindow { private: obs_frontend_callbacks *api = nullptr; + std::shared_ptr auth; + std::vector volumes; std::vector signalHandlers; + QList> extraDocks; + bool loaded = false; long disableSaving = 1; bool projectChanged = false; @@ -135,7 +157,7 @@ private: bool fullscreenInterface = false; const char *copyString; - const char *copyFiltersString; + const char *copyFiltersString = nullptr; bool copyVisible = true; QScopedPointer updateCheckThread; @@ -147,6 +169,8 @@ private: QPointer transformWindow; QPointer advAudioWindow; QPointer filters; + QPointer statsDock; + QPointer about; QPointer cpuUsageTimer; os_cpu_usage_info_t *cpuUsageInfo = nullptr; @@ -182,6 +206,7 @@ private: QPointer startStreamMenu; + QPointer transitionButton; QPointer replayBufferButton; QScopedPointer trayIcon; @@ -191,8 +216,28 @@ private: QPointer showHide; QPointer exit; QPointer trayMenu; + QPointer previewProjector; + QPointer studioProgramProjector; + QPointer multiviewProjectorMenu; + QPointer previewProjectorSource; + QPointer previewProjectorMain; + QPointer sceneProjectorMenu; + QPointer sourceProjector; + QPointer scaleFilteringMenu; + QPointer colorMenu; + QPointer colorWidgetAction; + QPointer colorSelect; + QPointer deinterlaceMenu; + QPointer perSceneTransitionMenu; + QPointer shortcutFilter; + + QPointer programWidget; + QPointer programLayout; + QPointer programLabel; + + QScopedPointer patronJsonThread; + std::string patronJson; - QPointer multiviewProjectorMenu; void UpdateMultiviewProjectorMenu(); void DrawBackdrop(float cx, float cy); @@ -218,6 +263,7 @@ private: bool InitService(); bool InitBasicConfigDefaults(); + void InitBasicConfigDefaults2(); bool InitBasicConfig(); void InitOBSCallbacks(); @@ -271,7 +317,7 @@ private: void LoadProfile(); void ResetProfileData(); bool AddProfile(bool create_new, const char *title, const char *text, - const char *init_text = nullptr); + const char *init_text = nullptr, bool rename = false); void DeleteProfile(const char *profile_name, const char *profile_dir); void RefreshProfiles(); void ChangeProfile(); @@ -282,7 +328,7 @@ private: int GetTopSelectedSourceItem(); obs_hotkey_pair_id streamingHotkeys, recordingHotkeys, - replayBufHotkeys; + replayBufHotkeys, togglePreviewHotkeys; obs_hotkey_id forceStreamingStopHotkey; void InitDefaultTransitions(); @@ -304,6 +350,8 @@ private: obs_data_array_t *SaveQuickTransitions(); void ClearQuickTransitionWidgets(); void RefreshQuickTransitions(); + void DisableQuickTransitionWidgets(); + void EnableQuickTransitionWidgets(); void CreateDefaultQuickTransitions(); QMenu *CreatePerSceneTransitionMenu(); @@ -394,7 +442,7 @@ public slots: void RecordingStart(); void RecordStopping(); - void RecordingStop(int code); + void RecordingStop(int code, QString last_error); void StartReplayBuffer(); void StopReplayBuffer(); @@ -419,6 +467,8 @@ public slots: bool create_new, const QString &name = QString()); + void UpdatePatronJson(const QString &text, const QString &error); + private slots: void AddSceneItem(OBSSceneItem item); void AddScene(OBSSource source); @@ -477,6 +527,17 @@ private slots: SourceTreeItem *GetItemWidgetFromSceneItem(obs_sceneitem_t *sceneItem); + void on_actionShowAbout_triggered(); + + void AudioMixerCopyFilters(); + void AudioMixerPasteFilters(); + + void EnablePreview(); + void DisablePreview(); + + void SceneCopyFilters(); + void ScenePasteFilters(); + private: /* OBS Callbacks */ static void SceneReordered(void *data, calldata_t *params); @@ -499,6 +560,8 @@ private: static void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed); + void AutoRemux(); + public: OBSSource GetProgramSource(); OBSScene GetCurrentScene(); @@ -514,6 +577,8 @@ public: obs_service_t *GetService(); void SetService(obs_service_t *service); + int GetTransitionDuration(); + inline bool IsPreviewProgramMode() const { return os_atomic_load_bool(&previewProgramMode); @@ -555,6 +620,8 @@ public: void SaveService(); bool LoadService(); + inline Auth *GetAuth() {return auth.get();} + inline void EnableOutputs(bool enable) { if (enable) { @@ -565,9 +632,10 @@ public: } } - QMenu *AddDeinterlacingMenu(obs_source_t *source); - QMenu *AddScaleFilteringMenu(obs_sceneitem_t *item); - QMenu *AddBackgroundColorMenu(obs_sceneitem_t *item); + QMenu *AddDeinterlacingMenu(QMenu *menu, obs_source_t *source); + QMenu *AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item); + QMenu *AddBackgroundColorMenu(QMenu *menu, QWidgetAction *widgetAction, + ColorSelect *select, obs_sceneitem_t *item); void CreateSourcePopupMenu(int idx, bool preview); void UpdateTitleBar(); @@ -582,6 +650,10 @@ public: void CreatePropertiesWindow(obs_source_t *source); void CreateFiltersWindow(obs_source_t *source); + QAction *AddDockWidget(QDockWidget *dock); + + static OBSBasic *Get(); + protected: virtual void closeEvent(QCloseEvent *event) override; virtual void changeEvent(QEvent *event) override; @@ -615,6 +687,8 @@ private slots: void on_actionFitToScreen_triggered(); void on_actionStretchToScreen_triggered(); void on_actionCenterToScreen_triggered(); + void on_actionVerticalCenter_triggered(); + void on_actionHorizontalCenter_triggered(); void on_scenes_currentItemChanged(QListWidgetItem *current, QListWidgetItem *prev); @@ -730,13 +804,19 @@ private slots: void OpenMultiviewWindow(); void OpenSceneWindow(); - void DeferredLoad(const QString &file, int requeueCount); + void DeferredSysTrayLoad(int requeueCount); void StackedMixerAreaContextMenuRequested(); + void ResizeOutputSizeOfSource(); + public slots: void on_actionResetTransform_triggered(); + bool StreamingActive(); + bool RecordingActive(); + bool ReplayBufferActive(); + public: explicit OBSBasic(QWidget *parent = 0); virtual ~OBSBasic(); @@ -748,15 +828,20 @@ public: virtual int GetProfilePath(char *path, size_t size, const char *file) const override; + static void InitBrowserPanelSafeBlock(); + private: std::unique_ptr ui; }; -class ColorSelect : public QWidget { +class SceneRenameDelegate : public QStyledItemDelegate { + Q_OBJECT public: - explicit ColorSelect(QWidget *parent = 0); + SceneRenameDelegate(QObject *parent); + virtual void setEditorData(QWidget *editor, const QModelIndex &index) + const override; -private: - std::unique_ptr ui; +protected: + virtual bool eventFilter(QObject *editor, QEvent *event) override; }; diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index bc4b2b9..2d9fa59 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -3,14 +3,19 @@ #include #include +#include #include #include #include "window-basic-preview.hpp" #include "window-basic-main.hpp" #include "obs-app.hpp" +#include "platform.hpp" #define HANDLE_RADIUS 4.0f #define HANDLE_SEL_RADIUS (HANDLE_RADIUS * 1.5f) +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +#define SUPPORTS_FRACTIONAL_SCALING +#endif /* TODO: make C++ math classes and clean up code here later */ @@ -21,10 +26,23 @@ OBSBasicPreview::OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags) setMouseTracking(true); } +OBSBasicPreview::~OBSBasicPreview() +{ + if (overflow) { + obs_enter_graphics(); + gs_texture_destroy(overflow); + obs_leave_graphics(); + } +} + vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event) { OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); +#ifdef SUPPORTS_FRACTIONAL_SCALING + float pixelRatio = main->devicePixelRatioF(); +#else float pixelRatio = main->devicePixelRatio(); +#endif float scale = pixelRatio / main->previewScale; vec2 pos; vec2_set(&pos, @@ -369,7 +387,11 @@ void OBSBasicPreview::GetStretchHandleData(const vec2 &pos) if (!scene) return; +#ifdef SUPPORTS_FRACTIONAL_SCALING + float scale = main->previewScale / main->devicePixelRatioF(); +#else float scale = main->previewScale / main->devicePixelRatio(); +#endif vec2 scaled_pos = pos; vec2_divf(&scaled_pos, &scaled_pos, scale); HandleFindData data(scaled_pos, scale); @@ -491,7 +513,11 @@ void OBSBasicPreview::mousePressEvent(QMouseEvent *event) } OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); +#ifdef SUPPORTS_FRACTIONAL_SCALING + float pixelRatio = main->devicePixelRatioF(); +#else float pixelRatio = main->devicePixelRatio(); +#endif float x = float(event->x()) - main->previewX / pixelRatio; float y = float(event->y()) - main->previewY / pixelRatio; Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers(); @@ -588,6 +614,9 @@ void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event) mouseDown = false; mouseMoved = false; cropping = false; + + OBSSceneItem item = GetItemAtPos(pos, true); + hoveredPreviewItem = item; } } @@ -1134,6 +1163,8 @@ void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event) return; if (mouseDown) { + hoveredPreviewItem = nullptr; + vec2 pos = GetMouseEventPos(event); if (!mouseMoved && !mouseOverItems && @@ -1170,10 +1201,22 @@ void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event) } mouseMoved = true; + } else { + vec2 pos = GetMouseEventPos(event); + OBSSceneItem item = GetItemAtPos(pos, true); + + hoveredPreviewItem = item; } } -static void DrawCircleAtPos(float x, float y) +void OBSBasicPreview::leaveEvent(QEvent *event) +{ + hoveredPreviewItem = nullptr; + + UNUSED_PARAMETER(event); +} + +static void DrawSquareAtPos(float x, float y) { struct vec3 pos; vec3_set(&pos, x, y, 0.0f); @@ -1185,11 +1228,68 @@ static void DrawCircleAtPos(float x, float y) gs_matrix_push(); gs_matrix_identity(); gs_matrix_translate(&pos); - gs_matrix_scale3f(HANDLE_RADIUS, HANDLE_RADIUS, 1.0f); - gs_draw(GS_LINESTRIP, 0, 0); + + gs_matrix_translate3f(-HANDLE_RADIUS, -HANDLE_RADIUS, 0.0f); + gs_matrix_scale3f(HANDLE_RADIUS*2, HANDLE_RADIUS*2, 1.0f); + gs_draw(GS_TRISTRIP, 0, 0); gs_matrix_pop(); } +static void DrawLine(float x1, float y1, float x2, float y2, float thickness, + vec2 scale) +{ + float ySide = (y1 == y2) ? (y1 < 0.5f ? 1.0f : -1.0f) : 0.0f; + float xSide = (x1 == x2) ? (x1 < 0.5f ? 1.0f : -1.0f) : 0.0f; + + gs_render_start(true); + + gs_vertex2f(x1, y1); + gs_vertex2f(x1 + (xSide * (thickness / scale.x)), + y1 + (ySide * (thickness / scale.y))); + gs_vertex2f(x2 + (xSide * (thickness / scale.x)), + y2 + (ySide * (thickness / scale.y))); + gs_vertex2f(x2, y2); + gs_vertex2f(x1, y1); + + gs_vertbuffer_t *line = gs_render_save(); + + gs_load_vertexbuffer(line); + gs_draw(GS_TRISTRIP, 0, 0); + gs_vertexbuffer_destroy(line); +} + +static void DrawRect(float thickness, vec2 scale) +{ + gs_render_start(true); + + gs_vertex2f(0.0f, 0.0f); + gs_vertex2f(0.0f + (thickness / scale.x), 0.0f); + gs_vertex2f(0.0f + (thickness / scale.x), 1.0f); + gs_vertex2f(0.0f, 1.0f); + gs_vertex2f(0.0f, 0.0f); + gs_vertex2f(0.0f, 1.0f); + gs_vertex2f(0.0f, 1.0f - (thickness / scale.y)); + gs_vertex2f(1.0f, 1.0f - (thickness / scale.y)); + gs_vertex2f(1.0f, 1.0f); + gs_vertex2f(0.0f, 1.0f); + gs_vertex2f(1.0f, 1.0f); + gs_vertex2f(1.0f - (thickness / scale.x), 1.0f); + gs_vertex2f(1.0f - (thickness / scale.x), 0.0f); + gs_vertex2f(1.0f, 0.0f); + gs_vertex2f(1.0f, 1.0f); + gs_vertex2f(1.0f, 0.0f); + gs_vertex2f(1.0f, 0.0f + (thickness / scale.y)); + gs_vertex2f(0.0f, 0.0f + (thickness / scale.y)); + gs_vertex2f(0.0f, 0.0f); + gs_vertex2f(1.0f, 0.0f); + + gs_vertbuffer_t *rect = gs_render_save(); + + gs_load_vertexbuffer(rect); + gs_draw(GS_TRISTRIP, 0, 0); + gs_vertexbuffer_destroy(rect); +} + static inline bool crop_enabled(const obs_sceneitem_crop *crop) { return crop->left > 0 || @@ -1198,6 +1298,96 @@ static inline bool crop_enabled(const obs_sceneitem_crop *crop) crop->bottom > 0; } +bool OBSBasicPreview::DrawSelectedOverflow(obs_scene_t *scene, + obs_sceneitem_t *item, void *param) +{ + if (obs_sceneitem_locked(item)) + return true; + + if (!SceneItemHasVideo(item)) + return true; + + bool select = config_get_bool(GetGlobalConfig(), "BasicWindow", + "OverflowSelectionHidden"); + + if (!select && !obs_sceneitem_visible(item)) + return true; + + if (obs_sceneitem_is_group(item)) { + matrix4 mat; + obs_sceneitem_get_draw_transform(item, &mat); + + gs_matrix_push(); + gs_matrix_mul(&mat); + obs_sceneitem_group_enum_items(item, DrawSelectedOverflow, param); + gs_matrix_pop(); + } + + bool always = config_get_bool(GetGlobalConfig(), "BasicWindow", + "OverflowAlwaysVisible"); + + if (!always && !obs_sceneitem_selected(item)) + return true; + + OBSBasicPreview *prev = reinterpret_cast(param); + + matrix4 boxTransform; + matrix4 invBoxTransform; + obs_sceneitem_get_box_transform(item, &boxTransform); + matrix4_inv(&invBoxTransform, &boxTransform); + + vec3 bounds[] = { + {{{0.f, 0.f, 0.f}}}, + {{{1.f, 0.f, 0.f}}}, + {{{0.f, 1.f, 0.f}}}, + {{{1.f, 1.f, 0.f}}}, + }; + + bool visible = std::all_of(std::begin(bounds), std::end(bounds), + [&](const vec3 &b) + { + vec3 pos; + vec3_transform(&pos, &b, &boxTransform); + vec3_transform(&pos, &pos, &invBoxTransform); + return CloseFloat(pos.x, b.x) && CloseFloat(pos.y, b.y); + }); + + if (!visible) + return true; + + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSelectedOverflow"); + + obs_transform_info info; + obs_sceneitem_get_info(item, &info); + + gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_REPEAT); + gs_eparam_t *image = gs_effect_get_param_by_name(solid, "image"); + gs_eparam_t *scale = gs_effect_get_param_by_name(solid, "scale"); + + vec2 s; + vec2_set(&s, boxTransform.x.x / 96, boxTransform.y.y / 96); + + gs_effect_set_vec2(scale, &s); + gs_effect_set_texture(image, prev->overflow); + + gs_matrix_push(); + gs_matrix_mul(&boxTransform); + + obs_sceneitem_crop crop; + obs_sceneitem_get_crop(item, &crop); + + while (gs_effect_loop(solid, "Draw")) { + gs_draw_sprite(prev->overflow, 0, 1, 1); + } + + gs_matrix_pop(); + + GS_DEBUG_MARKER_END(); + + UNUSED_PARAMETER(scene); + return true; +} + bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene, obs_sceneitem_t *item, void *param) { @@ -1217,10 +1407,15 @@ bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene, gs_matrix_pop(); } - if (!obs_sceneitem_selected(item)) - return true; + OBSBasicPreview *prev = reinterpret_cast(param); + OBSBasic *main = OBSBasic::Get(); - OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + bool hovered = prev->hoveredPreviewItem == item || + prev->hoveredListItem == item; + bool selected = obs_sceneitem_selected(item); + + if (!selected && !hovered) + return true; matrix4 boxTransform; matrix4 invBoxTransform; @@ -1234,6 +1429,14 @@ bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene, {{{1.f, 1.f, 0.f}}}, }; + vec4 red; + vec4 green; + vec4 blue; + + vec4_set(&red, 1.0f, 0.0f, 0.0f, 1.0f); + vec4_set(&green, 0.0f, 1.0f, 0.0f, 1.0f); + vec4_set(&blue, 0.0f, 0.5f, 1.0f, 1.0f); + bool visible = std::all_of(std::begin(bounds), std::end(bounds), [&](const vec3 &b) { @@ -1246,62 +1449,115 @@ bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene, if (!visible) return true; + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSelectedItem"); + + matrix4 curTransform; + vec2 boxScale; + gs_matrix_get(&curTransform); + obs_sceneitem_get_box_scale(item, &boxScale); + boxScale.x *= curTransform.x.x; + boxScale.y *= curTransform.y.y; + obs_transform_info info; obs_sceneitem_get_info(item, &info); - gs_load_vertexbuffer(main->circle); - gs_matrix_push(); gs_matrix_mul(&boxTransform); - DrawCircleAtPos(0.0f, 0.0f); - DrawCircleAtPos(0.0f, 1.0f); - DrawCircleAtPos(1.0f, 0.0f); - DrawCircleAtPos(1.0f, 1.0f); - DrawCircleAtPos(0.5f, 0.0f); - DrawCircleAtPos(0.0f, 0.5f); - DrawCircleAtPos(0.5f, 1.0f); - DrawCircleAtPos(1.0f, 0.5f); - obs_sceneitem_crop crop; obs_sceneitem_get_crop(item, &crop); + gs_effect_t *eff = gs_get_effect(); + gs_eparam_t *colParam = gs_effect_get_param_by_name(eff, "color"); + if (info.bounds_type == OBS_BOUNDS_NONE && crop_enabled(&crop)) { - vec4 color; - gs_effect_t *eff = gs_get_effect(); - gs_eparam_t *param = gs_effect_get_param_by_name(eff, "color"); +#define DRAW_SIDE(side, x1, y1, x2, y2) \ + if (hovered && !selected) \ + gs_effect_set_vec4(colParam, &blue); \ + else if (crop.side > 0) \ + gs_effect_set_vec4(colParam, &green); \ + DrawLine(x1, y1, x2, y2, HANDLE_RADIUS / 2, boxScale); \ + gs_effect_set_vec4(colParam, &red); -#define DRAW_SIDE(side, vb) \ - if (crop.side > 0) \ - vec4_set(&color, 0.0f, 1.0f, 0.0f, 1.0f); \ - else \ - vec4_set(&color, 1.0f, 0.0f, 0.0f, 1.0f); \ - gs_effect_set_vec4(param, &color); \ - gs_load_vertexbuffer(main->vb); \ - gs_draw(GS_LINESTRIP, 0, 0); - - DRAW_SIDE(left, boxLeft); - DRAW_SIDE(top, boxTop); - DRAW_SIDE(right, boxRight); - DRAW_SIDE(bottom, boxBottom); + DRAW_SIDE(left, 0.0f, 0.0f, 0.0f, 1.0f); + DRAW_SIDE(top, 0.0f, 0.0f, 1.0f, 0.0f); + DRAW_SIDE(right, 1.0f, 0.0f, 1.0f, 1.0f); + DRAW_SIDE(bottom, 0.0f, 1.0f, 1.0f, 1.0f); #undef DRAW_SIDE } else { - gs_load_vertexbuffer(main->box); - gs_draw(GS_LINESTRIP, 0, 0); + if (!selected) { + gs_effect_set_vec4(colParam, &blue); + DrawRect(HANDLE_RADIUS / 2, boxScale); + } else { + DrawRect(HANDLE_RADIUS / 2, boxScale); + } + } + + gs_load_vertexbuffer(main->box); + gs_effect_set_vec4(colParam, &red); + + if (selected) { + DrawSquareAtPos(0.0f, 0.0f); + DrawSquareAtPos(0.0f, 1.0f); + DrawSquareAtPos(1.0f, 0.0f); + DrawSquareAtPos(1.0f, 1.0f); + DrawSquareAtPos(0.5f, 0.0f); + DrawSquareAtPos(0.0f, 0.5f); + DrawSquareAtPos(0.5f, 1.0f); + DrawSquareAtPos(1.0f, 0.5f); } gs_matrix_pop(); + GS_DEBUG_MARKER_END(); + UNUSED_PARAMETER(scene); UNUSED_PARAMETER(param); return true; } +void OBSBasicPreview::DrawOverflow() +{ + if (locked) + return; + + bool hidden = config_get_bool(GetGlobalConfig(), "BasicWindow", + "OverflowHidden"); + + if (hidden) + return; + + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawOverflow"); + + if (!overflow) { + std::string path; + GetDataFilePath("images/overflow.png", path); + overflow = gs_texture_create_from_file(path.c_str()); + } + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + OBSScene scene = main->GetCurrentScene(); + + if (scene) { + gs_matrix_push(); + gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f); + obs_scene_enum_items(scene, DrawSelectedOverflow, this); + gs_matrix_pop(); + } + + gs_load_vertexbuffer(nullptr); + + GS_DEBUG_MARKER_END(); +} + void OBSBasicPreview::DrawSceneEditing() { if (locked) return; + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSceneEditing"); + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID); @@ -1327,6 +1583,8 @@ void OBSBasicPreview::DrawSceneEditing() gs_technique_end_pass(tech); gs_technique_end(tech); + + GS_DEBUG_MARKER_END(); } void OBSBasicPreview::ResetScrollingOffset() @@ -1345,3 +1603,8 @@ void OBSBasicPreview::SetScalingAmount(float newScalingAmountVal) { scrollingOffset.y *= newScalingAmountVal / scalingAmount; scalingAmount = newScalingAmountVal; } + +OBSBasicPreview *OBSBasicPreview::Get() +{ + return OBSBasic::Get()->ui->preview; +} diff --git a/UI/window-basic-preview.hpp b/UI/window-basic-preview.hpp index c177dd9..e285456 100644 --- a/UI/window-basic-preview.hpp +++ b/UI/window-basic-preview.hpp @@ -31,6 +31,8 @@ enum class ItemHandle : uint32_t { class OBSBasicPreview : public OBSQTDisplay { Q_OBJECT + friend class SourceTree; + private: obs_sceneitem_crop startCrop; vec2 startItemPos; @@ -43,6 +45,8 @@ private: matrix4 itemToScreen; matrix4 invGroupTransform; + gs_texture_t *overflow = nullptr; + vec2 startPos; vec2 lastMoveOffset; vec2 scrollingFrom; @@ -57,7 +61,12 @@ private: int32_t scalingLevel = 0; float scalingAmount = 1.0f; + obs_sceneitem_t *hoveredPreviewItem = nullptr; + obs_sceneitem_t *hoveredListItem = nullptr; + static vec2 GetMouseEventPos(QMouseEvent *event); + static bool DrawSelectedOverflow(obs_scene_t *scene, + obs_sceneitem_t *item, void *param); static bool DrawSelectedItem(obs_scene_t *scene, obs_sceneitem_t *item, void *param); @@ -84,6 +93,9 @@ private: public: OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags = 0); + ~OBSBasicPreview(); + + static OBSBasicPreview *Get(); virtual void keyPressEvent(QKeyEvent *event) override; virtual void keyReleaseEvent(QKeyEvent *event) override; @@ -93,7 +105,9 @@ public: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; + virtual void leaveEvent(QEvent *event) override; + void DrawOverflow(); void DrawSceneEditing(); inline void SetLocked(bool newLockedVal) {locked = newLockedVal;} diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp index a03f187..3864abd 100644 --- a/UI/window-basic-properties.cpp +++ b/UI/window-basic-properties.cpp @@ -29,6 +29,8 @@ using namespace std; +static void CreateTransitionScene(OBSSource scene, char *text, uint32_t color); + OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) : QDialog (parent), preview (new OBSQTDisplay(this)), @@ -49,6 +51,8 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) int cy = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow", "cy"); + enum obs_source_type type = obs_source_get_type(source); + buttonBox->setObjectName(QStringLiteral("buttonBox")); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | @@ -64,6 +68,8 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) else resize(720, 580); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QMetaObject::connectSlotsByName(this); /* The OBSData constructor increments the reference once */ @@ -93,6 +99,13 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) setLayout(new QVBoxLayout(this)); layout()->addWidget(windowSplitter); + + if (type == OBS_SOURCE_TYPE_TRANSITION) { + AddPreviewButton(); + connect(view, SIGNAL(PropertiesRefreshed()), + this, SLOT(AddPreviewButton())); + } + layout()->addWidget(buttonBox); layout()->setAlignment(buttonBox, Qt::AlignBottom); @@ -114,7 +127,11 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) obs_display_add_draw_callback(preview->GetDisplay(), OBSBasicProperties::DrawPreview, this); }; - enum obs_source_type type = obs_source_get_type(source); + auto addTransitionDrawCallback = [this] () + { + obs_display_add_draw_callback(preview->GetDisplay(), + OBSBasicProperties::DrawTransitionPreview, this); + }; uint32_t caps = obs_source_get_output_flags(source); bool drawable_type = type == OBS_SOURCE_TYPE_INPUT || type == OBS_SOURCE_TYPE_SCENE; @@ -124,6 +141,59 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) preview->show(); connect(preview.data(), &OBSQTDisplay::DisplayCreated, addDrawCallback); + + } else if (type == OBS_SOURCE_TYPE_TRANSITION) { + sourceA = obs_source_create_private("scene", "sourceA", + nullptr); + sourceB = obs_source_create_private("scene", "sourceB", + nullptr); + + obs_source_release(sourceA); + obs_source_release(sourceB); + + uint32_t colorA = 0xFFB26F52; + uint32_t colorB = 0xFF6FB252; + + CreateTransitionScene(sourceA, "A", colorA); + CreateTransitionScene(sourceB, "B", colorB); + + /** + * The cloned source is made from scratch, rather than using + * obs_source_duplicate, as the stinger transition would not + * play correctly otherwise. + */ + + obs_data_t *settings = obs_source_get_settings(source); + + sourceClone = obs_source_create_private( + obs_source_get_id(source), "clone", settings); + obs_source_release(sourceClone); + + obs_source_inc_active(sourceClone); + obs_transition_set(sourceClone, sourceA); + + obs_data_release(settings); + + auto updateCallback = [=]() + { + obs_data_t *settings = obs_source_get_settings(source); + obs_source_update(sourceClone, settings); + + obs_transition_clear(sourceClone); + obs_transition_set(sourceClone, sourceA); + obs_transition_force_stop(sourceClone); + + obs_data_release(settings); + + direction = true; + }; + + connect(view, &OBSPropertiesView::Changed, updateCallback); + + preview->show(); + connect(preview.data(), &OBSQTDisplay::DisplayCreated, + addTransitionDrawCallback); + } else { preview->hide(); } @@ -131,10 +201,119 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) OBSBasicProperties::~OBSBasicProperties() { + if (sourceClone) { + obs_source_dec_active(sourceClone); + } obs_source_dec_showing(source); main->SaveProject(); } +void OBSBasicProperties::AddPreviewButton() +{ + QPushButton *playButton = new QPushButton( + QTStr("PreviewTransition"), this); + VScrollArea *area = view; + area->widget()->layout()->addWidget(playButton); + + playButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + auto play = [=] () + { + OBSSource start; + OBSSource end; + + if (direction) { + start = sourceA; + end = sourceB; + } else { + start = sourceB; + end = sourceA; + } + + obs_transition_set(sourceClone, start); + obs_transition_start(sourceClone, + OBS_TRANSITION_MODE_AUTO, + main->GetTransitionDuration(), end); + direction = !direction; + + start = nullptr; + end = nullptr; + }; + + connect(playButton, &QPushButton::clicked, play); +} + +static obs_source_t *CreateLabel(const char *name, size_t h) +{ + obs_data_t *settings = obs_data_create(); + obs_data_t *font = obs_data_create(); + + std::string text; + text += " "; + text += name; + text += " "; + +#if defined(_WIN32) + obs_data_set_string(font, "face", "Arial"); +#elif defined(__APPLE__) + obs_data_set_string(font, "face", "Helvetica"); +#else + obs_data_set_string(font, "face", "Monospace"); +#endif + obs_data_set_int(font, "flags", 1); // Bold text + obs_data_set_int(font, "size", min(int(h), 300)); + + obs_data_set_obj(settings, "font", font); + obs_data_set_string(settings, "text", text.c_str()); + obs_data_set_bool(settings, "outline", false); + +#ifdef _WIN32 + const char *text_source_id = "text_gdiplus"; +#else + const char *text_source_id = "text_ft2_source"; +#endif + + obs_source_t *txtSource = obs_source_create_private(text_source_id, + name, settings); + + obs_data_release(font); + obs_data_release(settings); + + return txtSource; +} + +static void CreateTransitionScene(OBSSource scene, char *text, uint32_t color) +{ + obs_data_t *settings = obs_data_create(); + obs_data_set_int(settings, "width", obs_source_get_width(scene)); + obs_data_set_int(settings, "height", obs_source_get_height(scene)); + obs_data_set_int(settings, "color", color); + + obs_source_t *colorBG = obs_source_create_private("color_source", + "background", settings); + + obs_scene_add(obs_scene_from_source(scene), colorBG); + + obs_source_t *label = CreateLabel(text, obs_source_get_height(scene)); + obs_sceneitem_t *item = obs_scene_add(obs_scene_from_source(scene), + label); + + vec2 size; + vec2_set(&size, obs_source_get_width(scene), +#ifdef _WIN32 + obs_source_get_height(scene)); +#else + obs_source_get_height(scene) * 0.8); +#endif + + obs_sceneitem_set_bounds(item, &size); + obs_sceneitem_set_bounds_type(item, OBS_BOUNDS_SCALE_INNER); + + obs_data_release(settings); + obs_source_release(colorBG); + obs_source_release(label); +} + void OBSBasicProperties::SourceRemoved(void *data, calldata_t *params) { QMetaObject::invokeMethod(static_cast(data), @@ -189,7 +368,7 @@ void OBSBasicProperties::on_buttonBox_clicked(QAbstractButton *button) if (!view->DeferUpdate()) obs_source_update(source, nullptr); - view->RefreshProperties(); + view->ReloadProperties(); } } @@ -224,6 +403,38 @@ void OBSBasicProperties::DrawPreview(void *data, uint32_t cx, uint32_t cy) gs_viewport_pop(); } +void OBSBasicProperties::DrawTransitionPreview(void *data, uint32_t cx, + uint32_t cy) +{ + OBSBasicProperties *window = static_cast(data); + + if (!window->source) + return; + + uint32_t sourceCX = max(obs_source_get_width(window->source), 1u); + uint32_t sourceCY = max(obs_source_get_height(window->source), 1u); + + int x, y; + int newCX, newCY; + float scale; + + GetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale); + + newCX = int(scale * float(sourceCX)); + newCY = int(scale * float(sourceCY)); + + gs_viewport_push(); + gs_projection_push(); + gs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), + -100.0f, 100.0f); + gs_set_viewport(x, y, newCX, newCY); + + obs_source_video_render(window->sourceClone); + + gs_projection_pop(); + gs_viewport_pop(); +} + void OBSBasicProperties::Cleanup() { config_set_int(App()->GlobalConfig(), "PropertiesWindow", "cx", @@ -233,6 +444,8 @@ void OBSBasicProperties::Cleanup() obs_display_remove_draw_callback(preview->GetDisplay(), OBSBasicProperties::DrawPreview, this); + obs_display_remove_draw_callback(preview->GetDisplay(), + OBSBasicProperties::DrawTransitionPreview, this); } void OBSBasicProperties::reject() diff --git a/UI/window-basic-properties.hpp b/UI/window-basic-properties.hpp index b21a2cf..69a9771 100644 --- a/UI/window-basic-properties.hpp +++ b/UI/window-basic-properties.hpp @@ -45,16 +45,25 @@ private: QDialogButtonBox *buttonBox; QSplitter *windowSplitter; + OBSSource sourceA; + OBSSource sourceB; + OBSSource sourceClone; + bool direction = true; + static void SourceRemoved(void *data, calldata_t *params); static void SourceRenamed(void *data, calldata_t *params); static void UpdateProperties(void *data, calldata_t *params); static void DrawPreview(void *data, uint32_t cx, uint32_t cy); + static void DrawTransitionPreview(void *data, uint32_t cx, + uint32_t cy); + void UpdateCallback(void *obj, obs_data_t *settings); bool ConfirmQuit(); int CheckSettings(); void Cleanup(); private slots: void on_buttonBox_clicked(QAbstractButton *button); + void AddPreviewButton(); public: OBSBasicProperties(QWidget *parent, OBSSource source_); diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp new file mode 100644 index 0000000..6986244 --- /dev/null +++ b/UI/window-basic-settings-stream.cpp @@ -0,0 +1,505 @@ +#include + +#include "window-basic-settings.hpp" +#include "obs-frontend-api.h" +#include "obs-app.hpp" +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" + +#ifdef BROWSER_AVAILABLE +#include +#include "auth-oauth.hpp" +#endif + +struct QCef; +struct QCefCookieManager; + +extern QCef *cef; +extern QCefCookieManager *panel_cookies; + +enum class ListOpt : int { + ShowAll = 1, + Custom, +}; + +enum class Section : int { + Connect, + StreamKey, +}; + +inline bool OBSBasicSettings::IsCustomService() const +{ + return ui->service->currentData().toInt() == (int)ListOpt::Custom; +} + +void OBSBasicSettings::InitStreamPage() +{ + ui->connectAccount2->setVisible(false); + ui->disconnectAccount->setVisible(false); + ui->bandwidthTestEnable->setVisible(false); + + int vertSpacing = ui->topStreamLayout->verticalSpacing(); + + QMargins m = ui->topStreamLayout->contentsMargins(); + m.setBottom(vertSpacing / 2); + ui->topStreamLayout->setContentsMargins(m); + + m = ui->loginPageLayout->contentsMargins(); + m.setTop(vertSpacing / 2); + ui->loginPageLayout->setContentsMargins(m); + + m = ui->streamkeyPageLayout->contentsMargins(); + m.setTop(vertSpacing / 2); + ui->streamkeyPageLayout->setContentsMargins(m); + + LoadServices(false); + + connect(ui->service, SIGNAL(currentIndexChanged(int)), + this, SLOT(UpdateServerList())); + connect(ui->service, SIGNAL(currentIndexChanged(int)), + this, SLOT(UpdateKeyLink())); +} + +void OBSBasicSettings::LoadStream1Settings() +{ + obs_service_t *service_obj = main->GetService(); + const char *type = obs_service_get_type(service_obj); + + loading = true; + + obs_data_t *settings = obs_service_get_settings(service_obj); + + const char *service = obs_data_get_string(settings, "service"); + const char *server = obs_data_get_string(settings, "server"); + const char *key = obs_data_get_string(settings, "key"); + + if (strcmp(type, "rtmp_custom") == 0) { + ui->service->setCurrentIndex(0); + ui->customServer->setText(server); + + bool use_auth = obs_data_get_bool(settings, "use_auth"); + const char *username = obs_data_get_string(settings, "username"); + const char *password = obs_data_get_string(settings, "password"); + ui->authUsername->setText(QT_UTF8(username)); + ui->authPw->setText(QT_UTF8(password)); + ui->useAuth->setChecked(use_auth); + } else { + int idx = ui->service->findText(service); + if (idx == -1) { + if (service && *service) + ui->service->insertItem(1, service); + idx = 1; + } + ui->service->setCurrentIndex(idx); + + bool bw_test = obs_data_get_bool(settings, "bwtest"); + ui->bandwidthTestEnable->setChecked(bw_test); + } + + UpdateServerList(); + + if (strcmp(type, "rtmp_common") == 0) { + int idx = ui->server->findData(server); + if (idx == -1) { + if (server && *server) + ui->server->insertItem(0, server, server); + idx = 0; + } + ui->server->setCurrentIndex(idx); + } + + ui->key->setText(key); + + lastService.clear(); + on_service_currentIndexChanged(0); + + obs_data_release(settings); + + UpdateKeyLink(); + + bool streamActive = obs_frontend_streaming_active(); + ui->streamPage->setEnabled(!streamActive); + + loading = false; +} + +void OBSBasicSettings::SaveStream1Settings() +{ + bool customServer = IsCustomService(); + 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", + QT_TO_UTF8(ui->service->currentText())); + obs_data_set_string(settings, "server", + QT_TO_UTF8(ui->server->currentData().toString())); + } else { + obs_data_set_string(settings, "server", + QT_TO_UTF8(ui->customServer->text())); + obs_data_set_bool(settings, "use_auth", + ui->useAuth->isChecked()); + if (ui->useAuth->isChecked()) { + obs_data_set_string(settings, "username", + QT_TO_UTF8(ui->authUsername->text())); + obs_data_set_string(settings, "password", + QT_TO_UTF8(ui->authPw->text())); + } + } + + obs_data_set_bool(settings, "bwtest", ui->bandwidthTestEnable->isChecked()); + obs_data_set_string(settings, "key", QT_TO_UTF8(ui->key->text())); + + OBSService newService = obs_service_create(service_id, + "default_service", settings, hotkeyData); + obs_service_release(newService); + + if (!newService) + return; + + main->SetService(newService); + main->SaveService(); + main->auth = auth; + if (!!main->auth) + main->auth->LoadUI(); +} + +void OBSBasicSettings::UpdateKeyLink() +{ + bool custom = IsCustomService(); + 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 OBSBasicSettings::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 (!showAll) { + ui->service->addItem( + QTStr("Basic.AutoConfig.StreamPage.Service.ShowAll"), + QVariant((int)ListOpt::ShowAll)); + } + + ui->service->insertItem(0, + QTStr("Basic.AutoConfig.StreamPage.Service.Custom"), + QVariant((int)ListOpt::Custom)); + + if (!lastService.isEmpty()) { + int idx = ui->service->findText(lastService); + if (idx != -1) + ui->service->setCurrentIndex(idx); + } + + obs_properties_destroy(props); + + ui->service->blockSignals(false); +} + +static inline bool is_auth_service(const std::string &service) +{ + return Auth::AuthType(service) != Auth::Type::None; +} + +void OBSBasicSettings::on_service_currentIndexChanged(int) +{ + bool showMore = + ui->service->currentData().toInt() == (int)ListOpt::ShowAll; + if (showMore) + return; + + std::string service = QT_TO_UTF8(ui->service->currentText()); + bool custom = IsCustomService(); + + ui->disconnectAccount->setVisible(false); + ui->bandwidthTestEnable->setVisible(false); + +#ifdef BROWSER_AVAILABLE + if (cef) { + if (lastService != service.c_str()) { + QString key = ui->key->text(); + bool can_auth = is_auth_service(service); + int page = can_auth && (!loading || key.isEmpty()) + ? (int)Section::Connect + : (int)Section::StreamKey; + + ui->streamStackWidget->setCurrentIndex(page); + ui->streamKeyWidget->setVisible(true); + ui->streamKeyLabel->setVisible(true); + ui->connectAccount2->setVisible(can_auth); + } + } else { + ui->connectAccount2->setVisible(false); + } +#else + ui->connectAccount2->setVisible(false); +#endif + + ui->useAuth->setVisible(custom); + ui->authUsernameLabel->setVisible(custom); + ui->authUsername->setVisible(custom); + ui->authPwLabel->setVisible(custom); + ui->authPwWidget->setVisible(custom); + + if (custom) { + ui->streamkeyPageLayout->insertRow(1, ui->serverLabel, + ui->serverStackedWidget); + + ui->serverStackedWidget->setCurrentIndex(1); + ui->serverStackedWidget->setVisible(true); + ui->serverLabel->setVisible(true); + on_useAuth_toggled(); + } else { + ui->serverStackedWidget->setCurrentIndex(0); + } + +#ifdef BROWSER_AVAILABLE + auth.reset(); + + if (!!main->auth && + service.find(main->auth->service()) != std::string::npos) { + auth = main->auth; + OnAuthConnected(); + } +#endif +} + +void OBSBasicSettings::UpdateServerList() +{ + QString serviceName = ui->service->currentText(); + bool showMore = + ui->service->currentData().toInt() == (int)ListOpt::ShowAll; + + 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 OBSBasicSettings::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 OBSBasicSettings::on_authPwShow_clicked() +{ + if (ui->authPw->echoMode() == QLineEdit::Password) { + ui->authPw->setEchoMode(QLineEdit::Normal); + ui->authPwShow->setText(QTStr("Hide")); + } else { + ui->authPw->setEchoMode(QLineEdit::Password); + ui->authPwShow->setText(QTStr("Show")); + } +} + +OBSService OBSBasicSettings::SpawnTempService() +{ + bool custom = IsCustomService(); + const char *service_id = custom ? "rtmp_custom" : "rtmp_common"; + + OBSData settings = obs_data_create(); + obs_data_release(settings); + + if (!custom) { + obs_data_set_string(settings, "service", + QT_TO_UTF8(ui->service->currentText())); + obs_data_set_string(settings, "server", + QT_TO_UTF8(ui->server->currentData().toString())); + } else { + obs_data_set_string(settings, "server", + QT_TO_UTF8(ui->customServer->text())); + } + obs_data_set_string(settings, "key", QT_TO_UTF8(ui->key->text())); + + OBSService newService = obs_service_create(service_id, + "temp_service", settings, nullptr); + obs_service_release(newService); + + return newService; +} + +void OBSBasicSettings::OnOAuthStreamKeyConnected() +{ +#ifdef BROWSER_AVAILABLE + OAuthStreamKey *a = reinterpret_cast(auth.get()); + + if (a) { + bool validKey = !a->key().empty(); + + if (validKey) + ui->key->setText(QT_UTF8(a->key().c_str())); + + ui->streamKeyWidget->setVisible(false); + ui->streamKeyLabel->setVisible(false); + ui->connectAccount2->setVisible(false); + ui->disconnectAccount->setVisible(true); + + if (strcmp(a->service(), "Twitch") == 0) + ui->bandwidthTestEnable->setVisible(true); + } + + ui->streamStackWidget->setCurrentIndex((int)Section::StreamKey); +#endif +} + +void OBSBasicSettings::OnAuthConnected() +{ + std::string service = QT_TO_UTF8(ui->service->currentText()); + Auth::Type type = Auth::AuthType(service); + + if (type == Auth::Type::OAuth_StreamKey) { + OnOAuthStreamKeyConnected(); + } + + if (!loading) { + stream1Changed = true; + EnableApplyButton(true); + } +} + +void OBSBasicSettings::on_connectAccount_clicked() +{ +#ifdef BROWSER_AVAILABLE + std::string service = QT_TO_UTF8(ui->service->currentText()); + + auth = OAuthStreamKey::Login(this, service); + if (!!auth) + OnAuthConnected(); +#endif +} + +#define DISCONNECT_COMFIRM_TITLE \ + "Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title" +#define DISCONNECT_COMFIRM_TEXT \ + "Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text" + +void OBSBasicSettings::on_disconnectAccount_clicked() +{ + QMessageBox::StandardButton button; + + button = OBSMessageBox::question(this, + QTStr(DISCONNECT_COMFIRM_TITLE), + QTStr(DISCONNECT_COMFIRM_TEXT)); + + if (button == QMessageBox::No) { + return; + } + + main->auth.reset(); + auth.reset(); + + std::string service = QT_TO_UTF8(ui->service->currentText()); + +#ifdef BROWSER_AVAILABLE + OAuth::DeleteCookies(service); +#endif + + ui->streamKeyWidget->setVisible(true); + ui->streamKeyLabel->setVisible(true); + ui->connectAccount2->setVisible(true); + ui->disconnectAccount->setVisible(false); + ui->bandwidthTestEnable->setVisible(false); + ui->key->setText(""); +} + +void OBSBasicSettings::on_useStreamKey_clicked() +{ + ui->streamStackWidget->setCurrentIndex((int)Section::StreamKey); +} + +void OBSBasicSettings::on_useAuth_toggled() +{ + if (!IsCustomService()) + return; + + bool use_auth = ui->useAuth->isChecked(); + + ui->authUsernameLabel->setVisible(use_auth); + ui->authUsername->setVisible(use_auth); + ui->authPwLabel->setVisible(use_auth); + ui->authPwWidget->setVisible(use_auth); +} diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 4701b7b..b66f65e 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -48,6 +48,7 @@ #include "window-projector.hpp" #include +#include "ui-config.h" using namespace std; @@ -280,8 +281,12 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) { string path; + EnableThreadedMessageBoxes(true); + ui->setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + main->EnableOutputs(false); PopulateAACBitrates({ui->simpleOutputABitrate, @@ -291,10 +296,6 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) ui->listWidget->setAttribute(Qt::WA_MacShowFocusRect, false); - auto policy = ui->audioSourceScrollArea->sizePolicy(); - policy.setVerticalStretch(true); - ui->audioSourceScrollArea->setSizePolicy(policy); - HookWidget(ui->language, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->theme, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->enableAutoUpdates, CHECK_CHANGED, GENERAL_CHANGED); @@ -316,14 +317,25 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->centerSnapping, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->sourceSnapping, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->snapDistance, DSCROLL_CHANGED,GENERAL_CHANGED); + HookWidget(ui->overflowHide, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->overflowAlwaysVisible,CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->overflowSelectionHide,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->doubleClickSwitch, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->studioPortraitLayout, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->prevProgLabelToggle, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->multiviewMouseSwitch, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->multiviewDrawNames, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->multiviewDrawAreas, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->multiviewLayout, COMBO_CHANGED, GENERAL_CHANGED); + HookWidget(ui->service, COMBO_CHANGED, STREAM1_CHANGED); + HookWidget(ui->server, COMBO_CHANGED, STREAM1_CHANGED); + HookWidget(ui->customServer, EDIT_CHANGED, STREAM1_CHANGED); + HookWidget(ui->key, EDIT_CHANGED, STREAM1_CHANGED); + HookWidget(ui->bandwidthTestEnable, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->useAuth, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->authUsername, EDIT_CHANGED, STREAM1_CHANGED); + HookWidget(ui->authPw, EDIT_CHANGED, STREAM1_CHANGED); HookWidget(ui->outputMode, COMBO_CHANGED, OUTPUTS_CHANGED); - HookWidget(ui->streamType, COMBO_CHANGED, STREAM1_CHANGED); HookWidget(ui->simpleOutputPath, EDIT_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleNoSpace, CHECK_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutRecFormat, COMBO_CHANGED, OUTPUTS_CHANGED); @@ -410,6 +422,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->auxAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->auxAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->auxAudioDevice3, COMBO_CHANGED, AUDIO_CHANGED); + HookWidget(ui->auxAudioDevice4, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->baseResolution, CBEDIT_CHANGED, VIDEO_RES); HookWidget(ui->outputResolution, CBEDIT_CHANGED, VIDEO_RES); HookWidget(ui->downscaleFilter, COMBO_CHANGED, VIDEO_CHANGED); @@ -447,6 +460,13 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->enableNewSocketLoop, CHECK_CHANGED, ADV_CHANGED); HookWidget(ui->enableLowLatencyMode, CHECK_CHANGED, ADV_CHANGED); HookWidget(ui->disableFocusHotkeys, CHECK_CHANGED, ADV_CHANGED); + HookWidget(ui->autoRemux, CHECK_CHANGED, ADV_CHANGED); + + ui->simpleOutputVBitrate->setSingleStep(50); + ui->simpleOutputVBitrate->setSuffix(" Kbps"); + ui->advOutFFVBitrate->setSingleStep(50); + ui->advOutFFVBitrate->setSuffix(" Kbps"); + ui->advOutFFABitrate->setSuffix(" Kbps"); #if !defined(_WIN32) && !defined(__APPLE__) delete ui->enableAutoUpdates; @@ -454,8 +474,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) #endif #if !defined(_WIN32) && !defined(__APPLE__) && !HAVE_PULSEAUDIO - delete ui->advAudioGroupBox; - ui->advAudioGroupBox = nullptr; + delete ui->audioAdvGroupBox; + ui->audioAdvGroupBox = nullptr; #endif #ifdef _WIN32 @@ -558,7 +578,6 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) installEventFilter(CreateShortcutFilter()); - LoadServiceTypes(); LoadEncoderTypes(); LoadColorRanges(); LoadFormats(); @@ -689,6 +708,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) obs_properties_destroy(ppts); + InitStreamPage(); LoadSettings(false); // Add warning checks to advanced output recording section controls @@ -708,6 +728,10 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) this, SLOT(AdvOutRecCheckWarnings())); AdvOutRecCheckWarnings(); + ui->buttonBox->button(QDialogButtonBox::Apply)->setIcon(QIcon()); + ui->buttonBox->button(QDialogButtonBox::Ok)->setIcon(QIcon()); + ui->buttonBox->button(QDialogButtonBox::Cancel)->setIcon(QIcon()); + SimpleRecordingQualityChanged(); UpdateAutomaticReplayBufferCheckboxes(); @@ -722,6 +746,8 @@ OBSBasicSettings::~OBSBasicSettings() delete ui->filenameFormatting->completer(); main->EnableOutputs(true); App()->EnableInFocusHotkeys(!disableHotkeysInFocus); + + EnableThreadedMessageBoxes(false); } void OBSBasicSettings::SaveCombo(QComboBox *widget, const char *section, @@ -768,23 +794,6 @@ void OBSBasicSettings::SaveSpinBox(QSpinBox *widget, const char *section, config_set_int(main->Config(), section, value, widget->value()); } -void OBSBasicSettings::LoadServiceTypes() -{ - const char *type; - size_t idx = 0; - - while (obs_enum_service_types(idx++, &type)) { - const char *name = obs_service_get_display_name(type); - QString qName = QT_UTF8(name); - QString qType = QT_UTF8(type); - - ui->streamType->addItem(qName, qType); - } - - type = obs_service_get_type(main->GetService()); - SetComboByValue(ui->streamType, type); -} - #define TEXT_USE_STREAM_ENC \ QTStr("Basic.Settings.Output.Adv.Recording.UseStreamEncoder") @@ -1001,17 +1010,31 @@ void OBSBasicSettings::LoadThemeList() } } + QString defaultTheme; + defaultTheme += DEFAULT_THEME; + defaultTheme += " "; + defaultTheme += QTStr("Default"); + /* Check shipped themes. */ QDirIterator uIt(QString(themeDir.c_str()), QStringList() << "*.qss", QDir::Files); while (uIt.hasNext()) { uIt.next(); QString name = uIt.fileName().section(".",0,0); - if (!uniqueSet.contains(name)) + + if (name == DEFAULT_THEME) + name = defaultTheme; + + if (!uniqueSet.contains(name) && name != "Default") ui->theme->addItem(name); } - int idx = ui->theme->findText(App()->GetTheme()); + std::string themeName = App()->GetTheme(); + + if (themeName == DEFAULT_THEME) + themeName = QT_TO_UTF8(defaultTheme); + + int idx = ui->theme->findText(themeName.c_str()); if (idx != -1) ui->theme->setCurrentIndex(idx); } @@ -1100,6 +1123,18 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "ProjectorAlwaysOnTop"); ui->projectorAlwaysOnTop->setChecked(projectorAlwaysOnTop); + bool overflowHide = config_get_bool(GetGlobalConfig(), + "BasicWindow", "OverflowHidden"); + ui->overflowHide->setChecked(overflowHide); + + bool overflowAlwaysVisible = config_get_bool(GetGlobalConfig(), + "BasicWindow", "OverflowAlwaysVisible"); + ui->overflowAlwaysVisible->setChecked(overflowAlwaysVisible); + + bool overflowSelectionHide = config_get_bool(GetGlobalConfig(), + "BasicWindow", "OverflowSelectionHidden"); + ui->overflowSelectionHide->setChecked(overflowSelectionHide); + bool doubleClickSwitch = config_get_bool(GetGlobalConfig(), "BasicWindow", "TransitionOnDoubleClick"); ui->doubleClickSwitch->setChecked(doubleClickSwitch); @@ -1108,6 +1143,10 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "StudioPortraitLayout"); ui->studioPortraitLayout->setChecked(studioPortraitLayout); + bool prevProgLabels = config_get_bool(GetGlobalConfig(), + "BasicWindow", "StudioModeLabels"); + ui->prevProgLabelToggle->setChecked(prevProgLabels); + bool multiviewMouseSwitch = config_get_bool(GetGlobalConfig(), "BasicWindow", "MultiviewMouseSwitch"); ui->multiviewMouseSwitch->setChecked(multiviewMouseSwitch); @@ -1143,37 +1182,6 @@ void OBSBasicSettings::LoadGeneralSettings() loading = false; } -void OBSBasicSettings::LoadStream1Settings() -{ - QLayout *layout = ui->streamContainer->layout(); - obs_service_t *service = main->GetService(); - const char *type = obs_service_get_type(service); - - loading = true; - - obs_data_t *settings = obs_service_get_settings(service); - - delete streamProperties; - streamProperties = new OBSPropertiesView(settings, type, - (PropertiesReloadCallback)obs_get_service_properties, - 170); - - streamProperties->setProperty("changed", QVariant(false)); - layout->addWidget(streamProperties); - - QObject::connect(streamProperties, SIGNAL(Changed()), - this, STREAM1_CHANGED); - - obs_data_release(settings); - - loading = false; - - if (main->StreamingActive()) { - ui->streamType->setEnabled(false); - ui->streamContainer->setEnabled(false); - } -} - void OBSBasicSettings::LoadRendererList() { #ifdef _WIN32 @@ -1416,7 +1424,7 @@ void OBSBasicSettings::LoadVideoSettings() { loading = true; - if (video_output_active(obs_get_video())) { + if (obs_video_active()) { ui->videoPage->setEnabled(false); ui->videoMsg->setText( QTStr("Basic.Settings.Video.CurrentlyActive")); @@ -1772,8 +1780,8 @@ void OBSBasicSettings::LoadAdvOutputFFmpegSettings() "FFVCustom"); int audioBitrate = config_get_int(main->Config(), "AdvOut", "FFABitrate"); - int audioTrack = config_get_int(main->Config(), "AdvOut", - "FFAudioTrack"); + int audioMixes = config_get_int(main->Config(), "AdvOut", + "FFAudioMixes"); const char *aEncoder = config_get_string(main->Config(), "AdvOut", "FFAEncoder"); int aEncoderId = config_get_int(main->Config(), "AdvOut", @@ -1799,14 +1807,12 @@ void OBSBasicSettings::LoadAdvOutputFFmpegSettings() SelectEncoder(ui->advOutFFAEncoder, aEncoder, aEncoderId); ui->advOutFFACfg->setText(aEncCustom); - switch (audioTrack) { - case 1: ui->advOutFFTrack1->setChecked(true); break; - case 2: ui->advOutFFTrack2->setChecked(true); break; - case 3: ui->advOutFFTrack3->setChecked(true); break; - case 4: ui->advOutFFTrack4->setChecked(true); break; - case 5: ui->advOutFFTrack5->setChecked(true); break; - case 6: ui->advOutFFTrack6->setChecked(true); break; - } + ui->advOutFFTrack1->setChecked(audioMixes & (1 << 0)); + ui->advOutFFTrack2->setChecked(audioMixes & (1 << 1)); + ui->advOutFFTrack3->setChecked(audioMixes & (1 << 2)); + ui->advOutFFTrack4->setChecked(audioMixes & (1 << 3)); + ui->advOutFFTrack5->setChecked(audioMixes & (1 << 4)); + ui->advOutFFTrack6->setChecked(audioMixes & (1 << 5)); } void OBSBasicSettings::LoadAdvOutputAudioSettings() @@ -1895,7 +1901,7 @@ void OBSBasicSettings::LoadOutputSettings() LoadAdvOutputFFmpegSettings(); LoadAdvOutputAudioSettings(); - if (video_output_active(obs_get_video())) { + if (obs_video_active()) { ui->outputMode->setEnabled(false); ui->outputModeLabel->setEnabled(false); ui->simpleRecordingGroupBox->setEnabled(false); @@ -1962,7 +1968,7 @@ void OBSBasicSettings::LoadListValues(QComboBox *widget, obs_property_t *prop, deviceId = obs_data_get_string(settings, "device_id"); } - widget->addItem(QTStr("Disabled"), "disabled"); + widget->addItem(QTStr("Basic.Settings.Audio.Disabled"), "disabled"); for (size_t i = 0; i < count; i++) { const char *name = obs_property_list_item_name(prop, i); @@ -2004,6 +2010,7 @@ void OBSBasicSettings::LoadAudioDevices() LoadListValues(ui->auxAudioDevice1, inputs, 3); LoadListValues(ui->auxAudioDevice2, inputs, 4); LoadListValues(ui->auxAudioDevice3, inputs, 5); + LoadListValues(ui->auxAudioDevice4, inputs, 6); obs_properties_destroy(input_props); } @@ -2020,17 +2027,21 @@ void OBSBasicSettings::LoadAudioDevices() void OBSBasicSettings::LoadAudioSources() { + if (ui->audioSourceLayout->rowCount() > 0) { + QLayoutItem *forDeletion = ui->audioSourceLayout->takeAt(0); + delete forDeletion->widget(); + delete forDeletion; + } auto layout = new QFormLayout(); layout->setVerticalSpacing(15); layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - ui->audioSourceScrollArea->takeWidget()->deleteLater(); audioSourceSignals.clear(); audioSources.clear(); auto widget = new QWidget(); widget->setLayout(layout); - ui->audioSourceScrollArea->setWidget(widget); + ui->audioSourceLayout->addRow(widget); const char *enablePtm = Str("Basic.Settings.Audio.EnablePushToMute"); const char *ptmDelay = Str("Basic.Settings.Audio.PushToMuteDelay"); @@ -2109,6 +2120,8 @@ void OBSBasicSettings::LoadAudioSources() ptmCB, pttSB, pttCB, pttSB); auto label = new OBSSourceLabel(source); + label->setMinimumSize(QSize(170, 0)); + label->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); connect(label, &OBSSourceLabel::Removed, [=]() { @@ -2134,9 +2147,9 @@ void OBSBasicSettings::LoadAudioSources() if (layout->rowCount() == 0) - ui->audioSourceScrollArea->hide(); + ui->audioHotkeysGroupBox->hide(); else - ui->audioSourceScrollArea->show(); + ui->audioHotkeysGroupBox->show(); } void OBSBasicSettings::LoadAudioSettings() @@ -2234,6 +2247,8 @@ void OBSBasicSettings::LoadAdvancedSettings() "RecRBTime"); int rbSize = config_get_int(main->Config(), "AdvOut", "RecRBSize"); + bool autoRemux = config_get_bool(main->Config(), "Video", + "AutoRemux"); loading = true; @@ -2260,6 +2275,7 @@ void OBSBasicSettings::LoadAdvancedSettings() ui->streamDelaySec->setValue(delaySec); ui->streamDelayPreserve->setChecked(preserveDelay); ui->streamDelayEnable->setChecked(enableDelay); + ui->autoRemux->setChecked(autoRemux); SetComboByName(ui->colorFormat, videoColorFormat); @@ -2269,7 +2285,7 @@ void OBSBasicSettings::LoadAdvancedSettings() if (!SetComboByValue(ui->bindToIP, bindIP)) SetInvalidValue(ui->bindToIP, bindIP, bindIP); - if (video_output_active(obs_get_video())) { + if (obs_video_active()) { ui->advancedVideoContainer->setEnabled(false); } @@ -2313,12 +2329,22 @@ void OBSBasicSettings::LoadAdvancedSettings() loading = false; } +#define TRUNCATE_TEXT_LENGTH 80 + template static inline void LayoutHotkey(obs_hotkey_id id, obs_hotkey_t *key, Func &&fun, const map> &keys) { auto *label = new OBSHotkeyLabel; - label->setText(obs_hotkey_get_description(key)); + QString text = QT_UTF8(obs_hotkey_get_description(key)); + + if (text.length() > TRUNCATE_TEXT_LENGTH) { + label->setProperty("fullName", text); + text = text.left(TRUNCATE_TEXT_LENGTH); + text += "...'"; + } + + label->setText(text); OBSHotkeyWidget *hw = nullptr; @@ -2344,7 +2370,19 @@ static QLabel *makeLabel(T &t, Func &&getName) template static QLabel *makeLabel(const OBSSource &source, Func &&) { - return new OBSSourceLabel(source); + OBSSourceLabel *label = new OBSSourceLabel(source); + label->setStyleSheet("font-weight: bold;"); + QString name = QT_UTF8(obs_source_get_name(source)); + + if (name.length() > TRUNCATE_TEXT_LENGTH) { + label->setToolTip(name); + name = name.left(TRUNCATE_TEXT_LENGTH); + name += "..."; + } + + label->setText(name); + + return label; } template @@ -2356,13 +2394,8 @@ static inline void AddHotkeys(QFormLayout &layout, if (hotkeys.empty()) return; - auto line = new QFrame(); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout.setItem(layout.rowCount(), QFormLayout::SpanningRole, new QSpacerItem(0, 10)); - layout.addRow(line); using tuple_type = std::tuple, QPointer>; @@ -2423,6 +2456,47 @@ void OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey) widget->setLayout(layout); ui->hotkeyPage->setWidget(widget); + auto filterLayout = new QGridLayout(); + auto filterWidget = new QWidget(); + filterWidget->setLayout(filterLayout); + + auto filterLabel = new QLabel(QTStr("Basic.Settings.Hotkeys.Filter")); + auto filter = new QLineEdit(); + + auto setRowVisible = [=](int row, bool visible, QLayoutItem *label) { + label->widget()->setVisible(visible); + + auto field = layout->itemAt(row, QFormLayout::FieldRole); + if (field) + field->widget()->setVisible(visible); + }; + + auto searchFunction = [=](const QString &text) { + for (int i = 0; i < layout->rowCount(); i++) { + auto label = layout->itemAt(i, QFormLayout::LabelRole); + if (label) { + OBSHotkeyLabel *item = + qobject_cast( + label->widget()); + if(item) { + if (item->text().toLower() + .contains(text.toLower())) + setRowVisible(i, true, label); + else + setRowVisible(i, false, label); + } + } + } + }; + + connect(filter, &QLineEdit::textChanged, + this, searchFunction); + + filterLayout->addWidget(filterLabel, 0, 0); + filterLayout->addWidget(filter, 0, 1); + + layout->addRow(filterWidget); + using namespace std; using encoders_elem_t = tuple, QPointer>; @@ -2582,7 +2656,12 @@ void OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey) auto Update = [&](OBSHotkeyLabel *label, const QString &name, OBSHotkeyLabel *other, const QString &otherName) { - label->setToolTip(tt.arg(otherName)); + QString string = other->property("fullName").value(); + + if (string.isEmpty() || string.isNull()) + string = otherName; + + label->setToolTip(tt.arg(string)); label->setText(name + " *"); label->pairPartner = other; }; @@ -2627,13 +2706,19 @@ void OBSBasicSettings::SaveGeneralSettings() int themeIndex = ui->theme->currentIndex(); QString themeData = ui->theme->itemText(themeIndex); - string theme = themeData.toStdString(); + QString defaultTheme; + defaultTheme += DEFAULT_THEME; + defaultTheme += " "; + defaultTheme += QTStr("Default"); + + if (themeData == defaultTheme) + themeData = DEFAULT_THEME; if (WidgetChanged(ui->theme)) { config_set_string(GetGlobalConfig(), "General", "CurrentTheme", - theme.c_str()); + QT_TO_UTF8(themeData)); - App()->SetTheme(theme); + App()->SetTheme(themeData.toUtf8().constData()); } #if defined(_WIN32) || defined(__APPLE__) @@ -2666,6 +2751,18 @@ void OBSBasicSettings::SaveGeneralSettings() config_set_double(GetGlobalConfig(), "BasicWindow", "SnapDistance", ui->snapDistance->value()); + if (WidgetChanged(ui->overflowAlwaysVisible)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "OverflowAlwaysVisible", + ui->overflowAlwaysVisible->isChecked()); + if (WidgetChanged(ui->overflowHide)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "OverflowHidden", + ui->overflowHide->isChecked()); + if (WidgetChanged(ui->overflowSelectionHide)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "OverflowSelectionHidden", + ui->overflowSelectionHide->isChecked()); if (WidgetChanged(ui->doubleClickSwitch)) config_set_bool(GetGlobalConfig(), "BasicWindow", "TransitionOnDoubleClick", @@ -2731,6 +2828,14 @@ void OBSBasicSettings::SaveGeneralSettings() main->ResetUI(); } + if (WidgetChanged(ui->prevProgLabelToggle)) { + config_set_bool(GetGlobalConfig(), "BasicWindow", + "StudioModeLabels", + ui->prevProgLabelToggle->isChecked()); + + main->ResetUI(); + } + bool multiviewChanged = false; if (WidgetChanged(ui->multiviewMouseSwitch)) { config_set_bool(GetGlobalConfig(), "BasicWindow", @@ -2764,26 +2869,6 @@ void OBSBasicSettings::SaveGeneralSettings() OBSProjector::UpdateMultiviewProjectors(); } -void OBSBasicSettings::SaveStream1Settings() -{ - QString streamType = GetComboData(ui->streamType); - - obs_service_t *oldService = main->GetService(); - obs_data_t *hotkeyData = obs_hotkeys_save_service(oldService); - - obs_service_t *newService = obs_service_create(QT_TO_UTF8(streamType), - "default_service", streamProperties->GetSettings(), - hotkeyData); - - obs_data_release(hotkeyData); - if (!newService) - return; - - main->SetService(newService); - main->SaveService(); - obs_service_release(newService); -} - void OBSBasicSettings::SaveVideoSettings() { QString baseResolution = ui->baseResolution->currentText(); @@ -2892,6 +2977,7 @@ void OBSBasicSettings::SaveAdvancedSettings() SaveSpinBox(ui->reconnectRetryDelay, "Output", "RetryDelay"); SaveSpinBox(ui->reconnectMaxRetries, "Output", "MaxRetries"); SaveComboData(ui->bindToIP, "Output", "BindIP"); + SaveCheckBox(ui->autoRemux, "Video", "AutoRemux"); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO QString newDevice = ui->monitoringDevice->currentData().toString(); @@ -3085,11 +3171,13 @@ void OBSBasicSettings::SaveOutputSettings() SaveSpinBox(ui->advOutFFABitrate, "AdvOut", "FFABitrate"); SaveEncoder(ui->advOutFFAEncoder, "AdvOut", "FFAEncoder"); SaveEdit(ui->advOutFFACfg, "AdvOut", "FFACustom"); - SaveTrackIndex(main->Config(), "AdvOut", "FFAudioTrack", - ui->advOutFFTrack1, ui->advOutFFTrack2, - ui->advOutFFTrack3, ui->advOutFFTrack4, - ui->advOutFFTrack5, ui->advOutFFTrack6); - + config_set_int(main->Config(), "AdvOut", "FFAudioMixes", + (ui->advOutFFTrack1->isChecked() ? (1 << 0) : 0) | + (ui->advOutFFTrack2->isChecked() ? (1 << 1) : 0) | + (ui->advOutFFTrack3->isChecked() ? (1 << 2) : 0) | + (ui->advOutFFTrack4->isChecked() ? (1 << 3) : 0) | + (ui->advOutFFTrack5->isChecked() ? (1 << 4) : 0) | + (ui->advOutFFTrack6->isChecked() ? (1 << 5) : 0)); SaveCombo(ui->advOutTrack1Bitrate, "AdvOut", "Track1Bitrate"); SaveCombo(ui->advOutTrack2Bitrate, "AdvOut", "Track2Bitrate"); SaveCombo(ui->advOutTrack3Bitrate, "AdvOut", "Track3Bitrate"); @@ -3225,6 +3313,8 @@ void OBSBasicSettings::SaveAudioSettings() "Basic.AuxDevice2", 4); UpdateAudioDevice(true, ui->auxAudioDevice3, "Basic.AuxDevice3", 5); + UpdateAudioDevice(true, ui->auxAudioDevice4, + "Basic.AuxDevice4", 6); main->SaveProject(); } @@ -3357,8 +3447,17 @@ void OBSBasicSettings::closeEvent(QCloseEvent *event) void OBSBasicSettings::on_theme_activated(int idx) { - string currT = ui->theme->itemText(idx).toStdString(); - App()->SetTheme(currT); + QString currT = ui->theme->itemText(idx); + + QString defaultTheme; + defaultTheme += DEFAULT_THEME; + defaultTheme += " "; + defaultTheme += QTStr("Default"); + + if (currT == defaultTheme) + currT = DEFAULT_THEME; + + App()->SetTheme(currT.toUtf8().constData()); } void OBSBasicSettings::on_listWidget_itemSelectionChanged() @@ -3395,30 +3494,6 @@ void OBSBasicSettings::on_buttonBox_clicked(QAbstractButton *button) } } -void OBSBasicSettings::on_streamType_currentIndexChanged(int idx) -{ - if (loading) - return; - - QLayout *layout = ui->streamContainer->layout(); - QString streamType = ui->streamType->itemData(idx).toString(); - obs_data_t *settings = obs_service_defaults(QT_TO_UTF8(streamType)); - - delete streamProperties; - streamProperties = new OBSPropertiesView(settings, - QT_TO_UTF8(streamType), - (PropertiesReloadCallback)obs_get_service_properties, - 170); - - streamProperties->setProperty("changed", QVariant(true)); - layout->addWidget(streamProperties); - - QObject::connect(streamProperties, SIGNAL(Changed()), - this, STREAM1_CHANGED); - - obs_data_release(settings); -} - void OBSBasicSettings::on_simpleOutputBrowse_clicked() { QString dir = QFileDialog::getExistingDirectory(this, @@ -3460,35 +3535,50 @@ void OBSBasicSettings::on_advOutFFPathBrowse_clicked() void OBSBasicSettings::on_advOutEncoder_currentIndexChanged(int idx) { - if (loading) - return; - QString encoder = GetComboData(ui->advOutEncoder); - bool loadSettings = encoder == curAdvStreamEncoder; + if (!loading) { + bool loadSettings = encoder == curAdvStreamEncoder; - delete streamEncoderProps; - streamEncoderProps = CreateEncoderPropertyView(QT_TO_UTF8(encoder), - loadSettings ? "streamEncoder.json" : nullptr, true); - ui->advOutputStreamTab->layout()->addWidget(streamEncoderProps); + delete streamEncoderProps; + streamEncoderProps = CreateEncoderPropertyView( + QT_TO_UTF8(encoder), + loadSettings ? "streamEncoder.json" : nullptr, + true); + ui->advOutputStreamTab->layout()->addWidget(streamEncoderProps); + } + + uint32_t caps = obs_get_encoder_caps(QT_TO_UTF8(encoder)); + + if (caps & OBS_ENCODER_CAP_PASS_TEXTURE) { + ui->advOutUseRescale->setChecked(false); + ui->advOutUseRescale->setVisible(false); + ui->advOutRescale->setVisible(false); + } else { + ui->advOutUseRescale->setVisible(true); + ui->advOutRescale->setVisible(true); + } UNUSED_PARAMETER(idx); } void OBSBasicSettings::on_advOutRecEncoder_currentIndexChanged(int idx) { - if (loading) + if (!loading) { + delete recordEncoderProps; + recordEncoderProps = nullptr; + } + + if (idx <= 0) { + ui->advOutRecUseRescale->setChecked(false); + ui->advOutRecUseRescale->setVisible(false); + ui->advOutRecRescaleContainer->setVisible(false); return; + } - ui->advOutRecUseRescale->setEnabled(idx > 0); - ui->advOutRecRescaleContainer->setEnabled(idx > 0); - - delete recordEncoderProps; - recordEncoderProps = nullptr; - - if (idx > 0) { - QString encoder = GetComboData(ui->advOutRecEncoder); - bool loadSettings = encoder == curAdvRecordEncoder; + QString encoder = GetComboData(ui->advOutRecEncoder); + bool loadSettings = encoder == curAdvRecordEncoder; + if (!loading) { recordEncoderProps = CreateEncoderPropertyView( QT_TO_UTF8(encoder), loadSettings ? "recordEncoder.json" : nullptr, @@ -3497,6 +3587,17 @@ void OBSBasicSettings::on_advOutRecEncoder_currentIndexChanged(int idx) connect(recordEncoderProps, SIGNAL(Changed()), this, SLOT(AdvReplayBufferChanged())); } + + uint32_t caps = obs_get_encoder_caps(QT_TO_UTF8(encoder)); + + if (caps & OBS_ENCODER_CAP_PASS_TEXTURE) { + ui->advOutRecUseRescale->setChecked(false); + ui->advOutRecUseRescale->setVisible(false); + ui->advOutRecRescaleContainer->setVisible(false); + } else { + ui->advOutRecUseRescale->setVisible(true); + ui->advOutRecRescaleContainer->setVisible(true); + } } void OBSBasicSettings::on_advOutFFIgnoreCompat_stateChanged(int) @@ -3865,10 +3966,18 @@ void OBSBasicSettings::AdvOutRecCheckWarnings() warningMsg = QTStr("OutputWarnings.MultiTrackRecording"); } - if (ui->advOutRecFormat->currentText().compare("mp4") == 0) { + if (ui->advOutRecFormat->currentText().compare("mp4") == 0 || + ui->advOutRecFormat->currentText().compare("mov") == 0) { if (!warningMsg.isEmpty()) warningMsg += "\n\n"; warningMsg += QTStr("OutputWarnings.MP4Recording"); + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux") + + " " + + QTStr("Basic.Settings.Advanced.AutoRemux.MP4")); + } else { + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux")); } delete advOutRecWarning; @@ -3945,7 +4054,7 @@ void OBSBasicSettings::UpdateStreamDelayEstimate() UpdateAutomaticReplayBufferCheckboxes(); } -static bool EncoderAvailable(const char *encoder) +bool EncoderAvailable(const char *encoder) { const char *val; int i = 0; @@ -4243,7 +4352,6 @@ void OBSBasicSettings::AdvReplayBufferChanged() ui->advRBEstimate->setText(QTStr(ESTIMATE_UNKNOWN_STR)); ui->advReplayBufferGroupBox->setVisible(!lossless && replayBufferEnabled); - ui->line_4->setVisible(!lossless && replayBufferEnabled); ui->advReplayBuf->setEnabled(!lossless); UpdateAutomaticReplayBufferCheckboxes(); @@ -4261,11 +4369,7 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged() OBSService service; if (stream1Changed) { - QString streamType = GetComboData(ui->streamType); - service = obs_service_create_private( - QT_TO_UTF8(streamType), nullptr, - streamProperties->GetSettings()); - obs_service_release(service); + service = SpawnTempService(); } else { service = main->GetService(); } @@ -4321,10 +4425,18 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged() } } - if (ui->simpleOutRecFormat->currentText().compare("mp4") == 0) { + if (ui->simpleOutRecFormat->currentText().compare("mp4") == 0 || + ui->simpleOutRecFormat->currentText().compare("mov") == 0) { if (!warning.isEmpty()) warning += "\n\n"; warning += QTStr("OutputWarnings.MP4Recording"); + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux") + + " " + + QTStr("Basic.Settings.Advanced.AutoRemux.MP4")); + } else { + ui->autoRemux->setText( + QTStr("Basic.Settings.Advanced.AutoRemux")); } if (warning.isEmpty()) @@ -4421,3 +4533,38 @@ void OBSBasicSettings::on_disableOSXVSync_clicked() } #endif } + +void OBSBasicSettings::SetGeneralIcon(const QIcon &icon) +{ + ui->listWidget->item(0)->setIcon(icon); +} + +void OBSBasicSettings::SetStreamIcon(const QIcon &icon) +{ + ui->listWidget->item(1)->setIcon(icon); +} + +void OBSBasicSettings::SetOutputIcon(const QIcon &icon) +{ + ui->listWidget->item(2)->setIcon(icon); +} + +void OBSBasicSettings::SetAudioIcon(const QIcon &icon) +{ + ui->listWidget->item(3)->setIcon(icon); +} + +void OBSBasicSettings::SetVideoIcon(const QIcon &icon) +{ + ui->listWidget->item(4)->setIcon(icon); +} + +void OBSBasicSettings::SetHotkeysIcon(const QIcon &icon) +{ + ui->listWidget->item(5)->setIcon(icon); +} + +void OBSBasicSettings::SetAdvancedIcon(const QIcon &icon) +{ + ui->listWidget->item(6)->setIcon(icon); +} diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 585e535..f899da0 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -20,12 +20,15 @@ #include #include +#include #include #include #include -#include +#include + +#include "auth-base.hpp" class OBSBasic; class QAbstractButton; @@ -84,12 +87,28 @@ using OBSFFFormatDesc = std::unique_ptr ui; + std::shared_ptr auth; + bool generalChanged = false; bool stream1Changed = false; bool outputsChanged = false; @@ -185,7 +204,6 @@ private: bool QueryChanges(); - void LoadServiceTypes(); void LoadEncoderTypes(); void LoadColorRanges(); void LoadFormats(); @@ -207,6 +225,24 @@ private: void LoadLanguageList(); void LoadThemeList(); + /* stream */ + void InitStreamPage(); + inline bool IsCustomService() const; + void LoadServices(bool showAll); + void OnOAuthStreamKeyConnected(); + void OnAuthConnected(); + QString lastService; +private slots: + void UpdateServerList(); + void UpdateKeyLink(); + void on_show_clicked(); + void on_authPwShow_clicked(); + void on_connectAccount_clicked(); + void on_disconnectAccount_clicked(); + void on_useStreamKey_clicked(); + void on_useAuth_toggled(); +private: + /* output */ void LoadSimpleOutputSettings(); void LoadAdvOutputStreamingSettings(); @@ -255,7 +291,7 @@ private slots: void on_listWidget_itemSelectionChanged(); void on_buttonBox_clicked(QAbstractButton *button); - void on_streamType_currentIndexChanged(int idx); + void on_service_currentIndexChanged(int idx); void on_simpleOutputBrowse_clicked(); void on_advOutRecPathBrowse_clicked(); void on_advOutFFPathBrowse_clicked(); @@ -306,6 +342,16 @@ private slots: void SimpleStreamingEncoderChanged(); + OBSService SpawnTempService(); + + void SetGeneralIcon(const QIcon &icon); + void SetStreamIcon(const QIcon &icon); + void SetOutputIcon(const QIcon &icon); + void SetAudioIcon(const QIcon &icon); + void SetVideoIcon(const QIcon &icon); + void SetHotkeysIcon(const QIcon &icon); + void SetAdvancedIcon(const QIcon &icon); + protected: virtual void closeEvent(QCloseEvent *event); diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index da81782..171dc64 100644 --- a/UI/window-basic-source-select.cpp +++ b/UI/window-basic-source-select.cpp @@ -215,7 +215,7 @@ void OBSBasicSourceSelect::on_buttonBox_accepted() AddExisting(QT_TO_UTF8(item->text()), visible, false); } else { if (ui->sourceName->text().isEmpty()) { - OBSMessageBox::information(this, + OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); return; @@ -254,6 +254,8 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) ui (new Ui::OBSBasicSourceSelect), id (id_) { + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + ui->setupUi(this); ui->sourceList->setAttribute(Qt::WA_MacShowFocusRect, false); diff --git a/UI/window-basic-stats.cpp b/UI/window-basic-stats.cpp index b758c2f..0258067 100644 --- a/UI/window-basic-stats.cpp +++ b/UI/window-basic-stats.cpp @@ -4,6 +4,7 @@ #include "window-basic-main.hpp" #include "platform.hpp" #include "obs-app.hpp" +#include "qt-wrappers.hpp" #include #include @@ -15,6 +16,7 @@ #include #define TIMER_INTERVAL 2000 +#define REC_TIME_LEFT_INTERVAL 30000 static void setThemeID(QWidget *widget, const QString &themeID) { @@ -28,15 +30,32 @@ static void setThemeID(QWidget *widget, const QString &themeID) } } -OBSBasicStats::OBSBasicStats(QWidget *parent) +void OBSBasicStats::OBSFrontendEvent(enum obs_frontend_event event, void *ptr) +{ + OBSBasicStats *stats = reinterpret_cast(ptr); + + switch ((int)event) { + case OBS_FRONTEND_EVENT_RECORDING_STARTED: + stats->StartRecTimeLeft(); + break; + case OBS_FRONTEND_EVENT_RECORDING_STOPPED: + stats->ResetRecTimeLeft(); + break; + } +} + +OBSBasicStats::OBSBasicStats(QWidget *parent, bool closeable) : QWidget (parent), cpu_info (os_cpu_usage_info_start()), - timer (this) + timer (this), + recTimeLeft (this) { QVBoxLayout *mainLayout = new QVBoxLayout(); QGridLayout *topLayout = new QGridLayout(); outputLayout = new QGridLayout(); + bitrates.reserve(REC_TIME_LEFT_INTERVAL / TIMER_INTERVAL); + int row = 0; auto newStatBare = [&] (QString name, QWidget *label, int col) @@ -57,10 +76,12 @@ OBSBasicStats::OBSBasicStats(QWidget *parent) cpuUsage = new QLabel(this); hddSpace = new QLabel(this); + recordTimeLeft = new QLabel(this); memUsage = new QLabel(this); newStat("CPUUsage", cpuUsage, 0); newStat("HDDSpaceAvailable", hddSpace, 0); + newStat("DiskFullIn", recordTimeLeft, 0); newStat("MemoryUsage", memUsage, 0); fps = new QLabel(this); @@ -75,13 +96,15 @@ OBSBasicStats::OBSBasicStats(QWidget *parent) newStat("SkippedFrames", skippedFrames, 2); /* --------------------------------------------- */ - - QPushButton *closeButton = new QPushButton(QTStr("Close")); + QPushButton *closeButton = nullptr; + if(closeable) + closeButton = new QPushButton(QTStr("Close")); QPushButton *resetButton = new QPushButton(QTStr("Reset")); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); buttonLayout->addWidget(resetButton); - buttonLayout->addWidget(closeButton); + if(closeable) + buttonLayout->addWidget(closeButton); /* --------------------------------------------- */ @@ -125,26 +148,35 @@ OBSBasicStats::OBSBasicStats(QWidget *parent) setLayout(mainLayout); /* --------------------------------------------- */ - - connect(closeButton, &QPushButton::clicked, [this] () {close();}); + if(closeable) + connect(closeButton, &QPushButton::clicked, + [this] () {close();}); connect(resetButton, &QPushButton::clicked, [this] () {Reset();}); - installEventFilter(CreateShortcutFilter()); + delete shortcutFilter; + shortcutFilter = CreateShortcutFilter(); + installEventFilter(shortcutFilter); resize(800, 280); - setWindowFlags(Qt::Window | - Qt::WindowMinimizeButtonHint | - Qt::WindowCloseButtonHint); + setWindowTitle(QTStr("Basic.Stats")); - setWindowIcon(QIcon(":/res/images/obs.png")); + setWindowIcon(QIcon::fromTheme("obs", QIcon(":/res/images/obs.png"))); + setWindowModality(Qt::NonModal); setAttribute(Qt::WA_DeleteOnClose, true); QObject::connect(&timer, &QTimer::timeout, this, &OBSBasicStats::Update); timer.setInterval(TIMER_INTERVAL); - timer.start(); + + if (isVisible()) + timer.start(); + Update(); + QObject::connect(&recTimeLeft, &QTimer::timeout, this, + &OBSBasicStats::RecordingTimeLeft); + recTimeLeft.setInterval(REC_TIME_LEFT_INTERVAL); + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); const char *geometry = config_get_string(main->Config(), @@ -163,6 +195,8 @@ OBSBasicStats::OBSBasicStats(QWidget *parent) size(), rect)); } } + + obs_frontend_add_event_callback(OBSFrontendEvent, this); } void OBSBasicStats::closeEvent(QCloseEvent *event) @@ -180,6 +214,9 @@ void OBSBasicStats::closeEvent(QCloseEvent *event) OBSBasicStats::~OBSBasicStats() { + obs_frontend_remove_event_callback(OBSFrontendEvent, this); + + delete shortcutFilter; os_cpu_usage_info_destroy(cpu_info); } @@ -272,7 +309,7 @@ void OBSBasicStats::Update() #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); + num_bytes = os_get_free_disk_space(path); QString abrv = QStringLiteral(" MB"); long double num; @@ -385,6 +422,45 @@ void OBSBasicStats::Update() outputLabels[0].Update(strOutput, false); outputLabels[1].Update(recOutput, true); + + if (obs_output_active(recOutput)) { + long double kbps = outputLabels[1].kbps; + bitrates.push_back(kbps); + } +} + +void OBSBasicStats::StartRecTimeLeft() +{ + recordTimeLeft->setText(QTStr("Calculating")); + recTimeLeft.start(); +} + +void OBSBasicStats::ResetRecTimeLeft() +{ + bitrates.clear(); + recTimeLeft.stop(); + recordTimeLeft->setText(QTStr("")); +} + +void OBSBasicStats::RecordingTimeLeft() +{ + long double averageBitrate = accumulate(bitrates.begin(), + bitrates.end(), 0.0) / + (long double)bitrates.size(); + long double bytesPerSec = (averageBitrate / 8.0l) * 1000.0l; + long double secondsUntilFull = (long double)num_bytes / bytesPerSec; + + bitrates.clear(); + + int totalMinutes = (int)secondsUntilFull / 60; + int minutes = totalMinutes % 60; + int hours = totalMinutes / 60; + + QString text; + text.sprintf("%d %s, %d %s", hours, QT_TO_UTF8(QTStr("Hours")), + minutes, QT_TO_UTF8(QTStr("Minutes"))); + recordTimeLeft->setText(text); + recordTimeLeft->setMinimumWidth(recordTimeLeft->width()); } void OBSBasicStats::Reset() @@ -420,8 +496,7 @@ void OBSBasicStats::OutputLabels::Update(obs_output_t *output, bool rec) uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8; long double timePassed = (long double)(curTime - lastBytesSentTime) / 1000000000.0l; - long double kbps = (long double)bitsBetween / - timePassed / 1000.0l; + kbps = (long double)bitsBetween / timePassed / 1000.0l; if (timePassed < 0.01l) kbps = 0.0l; @@ -500,3 +575,13 @@ void OBSBasicStats::OutputLabels::Reset(obs_output_t *output) first_total = obs_output_get_total_frames(output); first_dropped = obs_output_get_frames_dropped(output); } + +void OBSBasicStats::showEvent(QShowEvent *) +{ + timer.start(TIMER_INTERVAL); +} + +void OBSBasicStats::hideEvent(QHideEvent *) +{ + timer.stop(); +} diff --git a/UI/window-basic-stats.hpp b/UI/window-basic-stats.hpp index a719b7d..8a44e42 100644 --- a/UI/window-basic-stats.hpp +++ b/UI/window-basic-stats.hpp @@ -17,6 +17,7 @@ class OBSBasicStats : public QWidget { QLabel *fps = nullptr; QLabel *cpuUsage = nullptr; QLabel *hddSpace = nullptr; + QLabel *recordTimeLeft = nullptr; QLabel *memUsage = nullptr; QLabel *renderTime = nullptr; @@ -28,6 +29,9 @@ class OBSBasicStats : public QWidget { os_cpu_usage_info_t *cpu_info = nullptr; QTimer timer; + QTimer recTimeLeft; + uint64_t num_bytes = 0; + std::vector bitrates; struct OutputLabels { QPointer name; @@ -44,6 +48,8 @@ class OBSBasicStats : public QWidget { void Update(obs_output_t *output, bool rec); void Reset(obs_output_t *output); + + long double kbps = 0.0l; }; QList outputLabels; @@ -54,9 +60,24 @@ class OBSBasicStats : public QWidget { virtual void closeEvent(QCloseEvent *event) override; + static void OBSFrontendEvent(enum obs_frontend_event event, void *ptr); + public: - OBSBasicStats(QWidget *parent = nullptr); + OBSBasicStats(QWidget *parent = nullptr, bool closable = true); ~OBSBasicStats(); static void InitializeValues(); + + void StartRecTimeLeft(); + void ResetRecTimeLeft(); + +private: + QPointer shortcutFilter; + +private slots: + void RecordingTimeLeft(); + +protected: + virtual void showEvent(QShowEvent *event) override; + virtual void hideEvent(QHideEvent *event) override; }; diff --git a/UI/window-basic-transform.cpp b/UI/window-basic-transform.cpp index 1f529dd..7501d27 100644 --- a/UI/window-basic-transform.cpp +++ b/UI/window-basic-transform.cpp @@ -46,6 +46,8 @@ OBSBasicTransform::OBSBasicTransform(OBSBasic *parent) { ui->setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + HookWidget(ui->positionX, DSCROLL_CHANGED, SLOT(OnControlChanged())); HookWidget(ui->positionY, DSCROLL_CHANGED, SLOT(OnControlChanged())); HookWidget(ui->rotation, DSCROLL_CHANGED, SLOT(OnControlChanged())); diff --git a/UI/window-dock.cpp b/UI/window-dock.cpp new file mode 100644 index 0000000..840c4f7 --- /dev/null +++ b/UI/window-dock.cpp @@ -0,0 +1,38 @@ +#include "window-dock.hpp" +#include "obs-app.hpp" + +#include +#include + +void OBSDock::closeEvent(QCloseEvent *event) +{ + auto msgBox = [] () + { + QMessageBox msgbox(App()->GetMainWindow()); + msgbox.setWindowTitle(QTStr("DockCloseWarning.Title")); + msgbox.setText(QTStr("DockCloseWarning.Text")); + msgbox.setIcon(QMessageBox::Icon::Information); + msgbox.addButton(QMessageBox::Ok); + + QCheckBox *cb = new QCheckBox(QTStr("DoNotShowAgain")); + msgbox.setCheckBox(cb); + + msgbox.exec(); + + if (cb->isChecked()) { + config_set_bool(App()->GlobalConfig(), "General", + "WarnedAboutClosingDocks", true); + config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + } + }; + + bool warned = config_get_bool(App()->GlobalConfig(), "General", + "WarnedAboutClosingDocks"); + if (!warned) { + QMetaObject::invokeMethod(App(), "Exec", + Qt::QueuedConnection, + Q_ARG(VoidFunc, msgBox)); + } + + QDockWidget::closeEvent(event); +} diff --git a/UI/window-dock.hpp b/UI/window-dock.hpp new file mode 100644 index 0000000..ccb1cf0 --- /dev/null +++ b/UI/window-dock.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +class OBSDock : public QDockWidget { + Q_OBJECT + +public: + inline OBSDock(QWidget *parent = nullptr) : QDockWidget(parent) {} + + virtual void closeEvent(QCloseEvent *event); +}; diff --git a/UI/window-license-agreement.cpp b/UI/window-license-agreement.cpp deleted file mode 100644 index a7a5f3b..0000000 --- a/UI/window-license-agreement.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include -#include -#include "window-license-agreement.hpp" -#include "qt-wrappers.hpp" - -using namespace std; - -OBSLicenseAgreement::OBSLicenseAgreement(QWidget *parent) - : QDialog (parent), - ui (new Ui::OBSLicenseAgreement) -{ - ui->setupUi(this); - - string path; - if (!GetDataFilePath("license/gplv2.txt", path)) - throw "Could not find license file"; - - BPtr licenseText = os_quick_read_utf8_file(path.c_str()); - if (!licenseText || !*licenseText || strlen(licenseText) < 1000) - throw "Invalid license file data"; - - ui->license->setPlainText(QT_UTF8(licenseText)); -} diff --git a/UI/window-license-agreement.hpp b/UI/window-license-agreement.hpp deleted file mode 100644 index aa3a6f4..0000000 --- a/UI/window-license-agreement.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include "ui_OBSLicenseAgreement.h" - -class OBSLicenseAgreement : public QDialog { - Q_OBJECT - -private: - std::unique_ptr ui; - -public: - OBSLicenseAgreement(QWidget *parent); -}; diff --git a/UI/window-namedialog.cpp b/UI/window-namedialog.cpp index 0a90e97..18b2fee 100644 --- a/UI/window-namedialog.cpp +++ b/UI/window-namedialog.cpp @@ -45,6 +45,7 @@ bool NameDialog::AskForName(QWidget *parent, const QString &title, NameDialog dialog(parent); dialog.setWindowTitle(title); + dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); dialog.ui->label->setText(text); dialog.ui->userText->setMaxLength(maxSize); dialog.ui->userText->setText(placeHolder); diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index 2ee09f7..294c31d 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -30,15 +30,13 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, type = type_; if (isWindow) { - setWindowIcon(QIcon(":/res/images/obs.png")); + setWindowIcon(QIcon::fromTheme("obs", + QIcon(":/res/images/obs.png"))); UpdateProjectorTitle(projectorTitle); windowedProjectors.push_back(this); resize(480, 270); - - SetAlwaysOnTop(this, config_get_bool(GetGlobalConfig(), - "BasicWindow", "ProjectorAlwaysOnTop")); } else { setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); @@ -51,10 +49,11 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, addAction(action); connect(action, SIGNAL(triggered()), this, SLOT(EscapeTriggered())); - - SetAlwaysOnTop(this, true); } + SetAlwaysOnTop(this, config_get_bool(GetGlobalConfig(), + "BasicWindow", "ProjectorAlwaysOnTop")); + setAttribute(Qt::WA_DeleteOnClose, true); //disable application quit when last window closed @@ -68,14 +67,13 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, obs_display_add_draw_callback(GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender, this); - obs_display_set_background_color(GetDisplay(), 0x000000); }; connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback); bool hideCursor = config_get_bool(GetGlobalConfig(), "BasicWindow", "HideProjectorCursor"); - if (hideCursor && !isWindow) { + if (hideCursor && !isWindow && type != ProjectorType::Multiview) { QPixmap empty(16, 16); empty.fill(Qt::transparent); setCursor(QCursor(empty)); @@ -228,28 +226,6 @@ static OBSSource CreateLabel(const char *name, size_t h) return txtSource; } -static inline void renderVB(gs_effect_t *effect, gs_vertbuffer_t *vb, - int cx, int cy) -{ - if (!vb) - return; - - matrix4 transform; - matrix4_identity(&transform); - transform.x.x = cx; - transform.y.y = cy; - - gs_load_vertexbuffer(vb); - - gs_matrix_push(); - gs_matrix_mul(&transform); - - while (gs_effect_loop(effect, "Solid")) - gs_draw(GS_LINESTRIP, 0, 0); - - gs_matrix_pop(); -} - static inline uint32_t labelOffset(obs_source_t *label, uint32_t cx) { uint32_t w = obs_source_get_width(label); @@ -591,6 +567,9 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) window->offset = labelOffset(programLabel, window->pvwprgCX); calcPreviewProgram(true); + paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, + window->ppiCY, backgroundColor); + // Scale and Draw the program gs_matrix_push(); gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f); @@ -673,6 +652,7 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) obs_source_dec_showing(source); obs_source_inc_showing(curSource); source = curSource; + window->source = source; } } diff --git a/UI/window-remux.cpp b/UI/window-remux.cpp index 92de804..f6fa08b 100644 --- a/UI/window-remux.cpp +++ b/UI/window-remux.cpp @@ -20,9 +20,18 @@ #include "obs-app.hpp" #include +#include #include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include "qt-wrappers.hpp" @@ -31,42 +40,664 @@ using namespace std; -OBSRemux::OBSRemux(const char *path, QWidget *parent) - : QDialog (parent), - worker (new RemuxWorker), - ui (new Ui::OBSRemux), - recPath (path) +enum RemuxEntryColumn { + State, + InputPath, + OutputPath, + + Count +}; + +enum RemuxEntryRole { + EntryStateRole = Qt::UserRole, + NewPathsToProcessRole +}; + +/********************************************************** + Delegate - Presents cells in the grid. +**********************************************************/ + +RemuxEntryPathItemDelegate::RemuxEntryPathItemDelegate(bool isOutput, + const QString &defaultPath) + : QStyledItemDelegate(), + isOutput(isOutput), + defaultPath(defaultPath) { +} + +QWidget *RemuxEntryPathItemDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem & /* option */, + const QModelIndex &index) const +{ + RemuxEntryState state = index.model() + ->index(index.row(), RemuxEntryColumn::State) + .data(RemuxEntryRole::EntryStateRole) + .value(); + if (state == RemuxEntryState::Pending || + state == RemuxEntryState::InProgress) { + // Never allow modification of rows that are + // in progress. + return Q_NULLPTR; + } else if (isOutput && state != RemuxEntryState::Ready) { + // Do not allow modification of output rows + // that aren't associated with a valid input. + return Q_NULLPTR; + } else if (!isOutput && state == RemuxEntryState::Complete) { + // Don't allow modification of rows that are + // already complete. + return Q_NULLPTR; + } else { + QSizePolicy buttonSizePolicy( + QSizePolicy::Policy::Minimum, + QSizePolicy::Policy::Expanding, + QSizePolicy::ControlType::PushButton); + + QWidget *container = new QWidget(parent); + + auto browseCallback = [this, container]() + { + const_cast(this) + ->handleBrowse(container); + }; + + auto clearCallback = [this, container]() + { + const_cast(this) + ->handleClear(container); + }; + + QHBoxLayout *layout = new QHBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + + QLineEdit *text = new QLineEdit(); + text->setObjectName(QStringLiteral("text")); + text->setSizePolicy(QSizePolicy( + QSizePolicy::Policy::Expanding, + QSizePolicy::Policy::Expanding, + QSizePolicy::ControlType::LineEdit)); + layout->addWidget(text); + + QToolButton *browseButton = new QToolButton(); + browseButton->setText("..."); + browseButton->setSizePolicy(buttonSizePolicy); + layout->addWidget(browseButton); + + container->connect(browseButton, &QToolButton::clicked, + browseCallback); + + // The "clear" button is not shown in output cells + // or the insertion point's input cell. + if (!isOutput && state != RemuxEntryState::Empty) { + QToolButton *clearButton = new QToolButton(); + clearButton->setText("X"); + clearButton->setSizePolicy(buttonSizePolicy); + layout->addWidget(clearButton); + + container->connect(clearButton, + &QToolButton::clicked, + clearCallback); + } + + container->setLayout(layout); + container->setFocusProxy(text); + return container; + } +} + +void RemuxEntryPathItemDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + QLineEdit *text = editor->findChild(); + text->setText(index.data().toString()); + QObject::connect(text, SIGNAL(textEdited(QString)), this, SLOT(updateText())); + editor->setProperty(PATH_LIST_PROP, QVariant()); +} + +void RemuxEntryPathItemDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const +{ + // We use the PATH_LIST_PROP property to pass a list of + // path strings from the editor widget into the model's + // NewPathsToProcessRole. This is only used when paths + // are selected through the "browse" or "delete" buttons + // in the editor. If the user enters new text in the + // text box, we simply pass that text on to the model + // as normal text data in the default role. + QVariant pathListProp = editor->property(PATH_LIST_PROP); + if (pathListProp.isValid()) { + QStringList list = editor->property(PATH_LIST_PROP) + .toStringList(); + if (isOutput) { + if (list.size() > 0) + model->setData(index, list); + } else + model->setData(index, list, + RemuxEntryRole::NewPathsToProcessRole); + } else { + QLineEdit *lineEdit = editor->findChild(); + model->setData(index, lineEdit->text()); + } +} + +void RemuxEntryPathItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + RemuxEntryState state = index.model() + ->index(index.row(), RemuxEntryColumn::State) + .data(RemuxEntryRole::EntryStateRole) + .value(); + + QStyleOptionViewItem localOption = option; + initStyleOption(&localOption, index); + + if (isOutput) { + if (state != Ready) { + QColor background = localOption.palette + .color(QPalette::ColorGroup::Disabled, + QPalette::ColorRole::Background); + + localOption.backgroundBrush = QBrush(background); + } + } + + QApplication::style()->drawControl(QStyle::CE_ItemViewItem, + &localOption, painter); +} + +void RemuxEntryPathItemDelegate::handleBrowse(QWidget *container) +{ + QString OutputPattern = + "(*.mp4 *.flv *.mov *.mkv *.ts *.m3u8)"; + QString InputPattern = + "(*.flv *.mov *.mkv *.ts *.m3u8)"; + + QLineEdit *text = container->findChild(); + + QString currentPath = text->text(); + if (currentPath.isEmpty()) + currentPath = defaultPath; + + bool isSet = false; + if (isOutput) { + QString newPath = QFileDialog::getSaveFileName( + container, QTStr("Remux.SelectTarget"), + currentPath, OutputPattern); + + if (!newPath.isEmpty()) { + container->setProperty(PATH_LIST_PROP, + QStringList() << newPath); + isSet = true; + } + } else { + QStringList paths = QFileDialog::getOpenFileNames( + container, + QTStr("Remux.SelectRecording"), + currentPath, + QTStr("Remux.OBSRecording") + + QString(" ") + InputPattern); + + if (!paths.empty()) { + container->setProperty(PATH_LIST_PROP, paths); + isSet = true; + } + } + + if (isSet) + emit commitData(container); +} + +void RemuxEntryPathItemDelegate::handleClear(QWidget *container) +{ + // An empty string list will indicate that the entry is being + // blanked and should be deleted. + container->setProperty(PATH_LIST_PROP, QStringList()); + + emit commitData(container); +} + +void RemuxEntryPathItemDelegate::updateText() { + QLineEdit *lineEdit = dynamic_cast(sender()); + QWidget *editor = lineEdit->parentWidget(); + emit commitData(editor); +} + +/********************************************************** + Model - Manages the queue's data +**********************************************************/ + +int RemuxQueueModel::rowCount(const QModelIndex &) const +{ + return queue.length() + (isProcessing ? 0 : 1); +} + +int RemuxQueueModel::columnCount(const QModelIndex &) const +{ + return RemuxEntryColumn::Count; +} + +QVariant RemuxQueueModel::data(const QModelIndex &index, int role) const +{ + QVariant result = QVariant(); + + if (index.row() >= queue.length()) { + return QVariant(); + } else if (role == Qt::DisplayRole) { + switch (index.column()) { + case RemuxEntryColumn::InputPath: + result = queue[index.row()].sourcePath; + break; + case RemuxEntryColumn::OutputPath: + result = queue[index.row()].targetPath; + break; + } + } else if (role == Qt::DecorationRole && + index.column() == RemuxEntryColumn::State) { + result = getIcon(queue[index.row()].state); + } else if (role == RemuxEntryRole::EntryStateRole) { + result = queue[index.row()].state; + } + + return result; +} + +QVariant RemuxQueueModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + QVariant result = QVariant(); + + if (role == Qt::DisplayRole && + orientation == Qt::Orientation::Horizontal) { + switch (section) { + case RemuxEntryColumn::State: + result = QString(); + break; + case RemuxEntryColumn::InputPath: + result = QTStr("Remux.SourceFile"); + break; + case RemuxEntryColumn::OutputPath: + result = QTStr("Remux.TargetFile"); + break; + } + } + + return result; +} + +Qt::ItemFlags RemuxQueueModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + if (index.column() == RemuxEntryColumn::InputPath) { + flags |= Qt::ItemIsEditable; + } else if (index.column() == RemuxEntryColumn::OutputPath && + index.row() != queue.length()) { + flags |= Qt::ItemIsEditable; + } + + return flags; +} + +bool RemuxQueueModel::setData(const QModelIndex &index, const QVariant &value, + int role) +{ + bool success = false; + + if (role == RemuxEntryRole::NewPathsToProcessRole) { + QStringList pathList = value.toStringList(); + + if (pathList.size() == 0) { + if (index.row() < queue.size()) { + beginRemoveRows(QModelIndex(), index.row(), + index.row()); + queue.removeAt(index.row()); + endRemoveRows(); + } + } else { + if (pathList.size() > 1 && index.row() < queue.length()) { + queue[index.row()].sourcePath = pathList[0]; + checkInputPath(index.row()); + + pathList.removeAt(0); + + success = true; + } + + if (pathList.size() > 0) { + int row = index.row(); + int lastRow = row + pathList.size() - 1; + beginInsertRows(QModelIndex(), row, lastRow); + + for (QString path : pathList) { + RemuxQueueEntry entry; + entry.sourcePath = path; + + queue.insert(row, entry); + row++; + } + endInsertRows(); + + for (row = index.row(); row <= lastRow; row++) { + checkInputPath(row); + } + + success = true; + } + } + } else if (index.row() == queue.length()) { + QString path = value.toString(); + + if (!path.isEmpty()) { + RemuxQueueEntry entry; + entry.sourcePath = path; + + beginInsertRows(QModelIndex(), queue.length() + 1, + queue.length() + 1); + queue.append(entry); + endInsertRows(); + + checkInputPath(index.row()); + success = true; + } + } else { + QString path = value.toString(); + + if (path.isEmpty()) { + if (index.column() == RemuxEntryColumn::InputPath) { + beginRemoveRows(QModelIndex(), index.row(), + index.row()); + queue.removeAt(index.row()); + endRemoveRows(); + } + } else { + switch (index.column()) { + case RemuxEntryColumn::InputPath: + queue[index.row()].sourcePath = value.toString(); + checkInputPath(index.row()); + success = true; + break; + case RemuxEntryColumn::OutputPath: + queue[index.row()].targetPath = value.toString(); + emit dataChanged(index, index); + success = true; + break; + } + } + } + + return success; +} + +QVariant RemuxQueueModel::getIcon(RemuxEntryState state) +{ + QVariant icon; + QStyle *style = QApplication::style(); + + switch (state) { + case RemuxEntryState::Complete: + icon = style->standardIcon( + QStyle::SP_DialogApplyButton); + break; + + case RemuxEntryState::InProgress: + icon = style->standardIcon( + QStyle::SP_ArrowRight); + break; + + case RemuxEntryState::Error: + icon = style->standardIcon( + QStyle::SP_DialogCancelButton); + break; + + case RemuxEntryState::InvalidPath: + icon = style->standardIcon( + QStyle::SP_MessageBoxWarning); + break; + + default: + break; + } + + return icon; +} + +void RemuxQueueModel::checkInputPath(int row) +{ + RemuxQueueEntry &entry = queue[row]; + + if (entry.sourcePath.isEmpty()) { + entry.state = RemuxEntryState::Empty; + } else { + QFileInfo fileInfo(entry.sourcePath); + if (fileInfo.exists()) + entry.state = RemuxEntryState::Ready; + else + entry.state = RemuxEntryState::InvalidPath; + + if (entry.state == RemuxEntryState::Ready) + entry.targetPath = fileInfo.path() + QDir::separator() + + fileInfo.completeBaseName() + ".mp4"; + } + + if (entry.state == RemuxEntryState::Ready && isProcessing) + entry.state = RemuxEntryState::Pending; + + emit dataChanged(index(row, 0), index(row, RemuxEntryColumn::Count)); +} + +QFileInfoList RemuxQueueModel::checkForOverwrites() const +{ + QFileInfoList list; + + for (const RemuxQueueEntry &entry : queue) { + if (entry.state == RemuxEntryState::Ready) { + QFileInfo fileInfo(entry.targetPath); + if (fileInfo.exists()) { + list.append(fileInfo); + } + } + } + + return list; +} + +bool RemuxQueueModel::checkForErrors() const +{ + bool hasErrors = false; + + for (const RemuxQueueEntry &entry : queue) { + if (entry.state == RemuxEntryState::Error) { + hasErrors = true; + break; + } + } + + return hasErrors; +} + +void RemuxQueueModel::clearAll() +{ + beginRemoveRows(QModelIndex(), 0, queue.size() - 1); + queue.clear(); + endRemoveRows(); +} + +void RemuxQueueModel::clearFinished() +{ + int index = 0; + + for (index = 0; index < queue.size(); index++) { + const RemuxQueueEntry &entry = queue[index]; + if (entry.state == RemuxEntryState::Complete) { + beginRemoveRows(QModelIndex(), index, index); + queue.removeAt(index); + endRemoveRows(); + index--; + } + } +} + +bool RemuxQueueModel::canClearFinished() const +{ + bool canClearFinished = false; + for (const RemuxQueueEntry &entry : queue) + if (entry.state == RemuxEntryState::Complete) { + canClearFinished = true; + break; + } + + return canClearFinished; +} + +void RemuxQueueModel::beginProcessing() +{ + for (RemuxQueueEntry &entry : queue) + if (entry.state == RemuxEntryState::Ready) + entry.state = RemuxEntryState::Pending; + + // Signal that the insertion point no longer exists. + beginRemoveRows(QModelIndex(), queue.length(), queue.length()); + endRemoveRows(); + + isProcessing = true; + + emit dataChanged(index(0, RemuxEntryColumn::State), + index(queue.length(), RemuxEntryColumn::State)); +} + +void RemuxQueueModel::endProcessing() +{ + for (RemuxQueueEntry &entry : queue) { + if (entry.state == RemuxEntryState::Pending) { + entry.state = RemuxEntryState::Ready; + } + } + + // Signal that the insertion point exists again. + + if (!autoRemux) { + beginInsertRows(QModelIndex(), queue.length(), queue.length()); + endInsertRows(); + } + + isProcessing = false; + + emit dataChanged(index(0, RemuxEntryColumn::State), + index(queue.length(), RemuxEntryColumn::State)); +} + +bool RemuxQueueModel::beginNextEntry(QString &inputPath, QString &outputPath) +{ + bool anyStarted = false; + + for (int row = 0; row < queue.length(); row++) { + RemuxQueueEntry &entry = queue[row]; + if (entry.state == RemuxEntryState::Pending) { + entry.state = RemuxEntryState::InProgress; + + inputPath = entry.sourcePath; + outputPath = entry.targetPath; + + QModelIndex index = this->index(row, + RemuxEntryColumn::State); + emit dataChanged(index, index); + + anyStarted = true; + break; + } + } + + return anyStarted; +} + +void RemuxQueueModel::finishEntry(bool success) +{ + for (int row = 0; row < queue.length(); row++) { + RemuxQueueEntry &entry = queue[row]; + if (entry.state == RemuxEntryState::InProgress) { + if (success) + entry.state = RemuxEntryState::Complete; + else + entry.state = RemuxEntryState::Error; + + QModelIndex index = this->index(row, + RemuxEntryColumn::State); + emit dataChanged(index, index); + + break; + } + } +} + +/********************************************************** + The actual remux window implementation +**********************************************************/ + +OBSRemux::OBSRemux(const char *path, QWidget *parent, bool autoRemux_) + : QDialog (parent), + queueModel(new RemuxQueueModel), + worker (new RemuxWorker()), + ui (new Ui::OBSRemux), + recPath (path), + autoRemux (autoRemux_) +{ + setAcceptDrops(true); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + ui->setupUi(this); ui->progressBar->setVisible(false); ui->buttonBox->button(QDialogButtonBox::Ok)-> setEnabled(false); - ui->targetFile->setEnabled(false); - ui->browseTarget->setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(false); + + if (autoRemux) { + resize(280, 40); + ui->tableView->hide(); + ui->buttonBox->hide(); + ui->label->hide(); + } ui->progressBar->setMinimum(0); ui->progressBar->setMaximum(1000); ui->progressBar->setValue(0); + ui->tableView->setModel(queueModel); + ui->tableView->setItemDelegateForColumn(RemuxEntryColumn::InputPath, + new RemuxEntryPathItemDelegate(false, recPath)); + ui->tableView->setItemDelegateForColumn(RemuxEntryColumn::OutputPath, + new RemuxEntryPathItemDelegate(true, recPath)); + ui->tableView->horizontalHeader()->setSectionResizeMode( + QHeaderView::ResizeMode::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode( + RemuxEntryColumn::State, + QHeaderView::ResizeMode::Fixed); + ui->tableView->setEditTriggers( + QAbstractItemView::EditTrigger::CurrentChanged); + installEventFilter(CreateShortcutFilter()); - connect(ui->browseSource, &QPushButton::clicked, - [&]() { BrowseInput(); }); - connect(ui->browseTarget, &QPushButton::clicked, - [&]() { BrowseOutput(); }); - - connect(ui->sourceFile, &QLineEdit::textChanged, - this, &OBSRemux::inputChanged); - ui->buttonBox->button(QDialogButtonBox::Ok)-> setText(QTStr("Remux.Remux")); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setText(QTStr("Remux.ClearFinished")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setText(QTStr("Remux.ClearAll")); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setDisabled(true); connect(ui->buttonBox->button(QDialogButtonBox::Ok), - SIGNAL(clicked()), this, SLOT(Remux())); - + SIGNAL(clicked()), this, SLOT(beginRemux())); + connect(ui->buttonBox->button(QDialogButtonBox::Reset), + SIGNAL(clicked()), this, SLOT(clearFinished())); + connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), + SIGNAL(clicked()), this, SLOT(clearAll())); connect(ui->buttonBox->button(QDialogButtonBox::Close), - SIGNAL(clicked()), this, SLOT(close())); + SIGNAL(clicked()), this, SLOT(close())); worker->moveToThread(&remuxer); remuxer.start(); @@ -79,107 +710,212 @@ OBSRemux::OBSRemux(const char *path, QWidget *parent) connect(worker_, &RemuxWorker::remuxFinished, this, &OBSRemux::remuxFinished); connect(this, &OBSRemux::remux, worker_, &RemuxWorker::remux); + + // Guessing the GCC bug mentioned above would also affect + // QPointer? Unsure. + RemuxQueueModel *queueModel_ = queueModel; + connect(queueModel_, + SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, + SLOT(rowCountChanged(const QModelIndex &, int, int))); + connect(queueModel_, + SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, + SLOT(rowCountChanged(const QModelIndex &, int, int))); + + QModelIndex index = queueModel->createIndex(0, 1); + QMetaObject::invokeMethod(ui->tableView, + "setCurrentIndex", Qt::QueuedConnection, + Q_ARG(const QModelIndex &, index)); } -bool OBSRemux::Stop() +bool OBSRemux::stopRemux() { - if (!worker->job) + if (!worker->isWorking) return true; + // By locking the worker thread's mutex, we ensure that its + // update poll will be blocked as long as we're in here with + // the popup open. + QMutexLocker lock(&worker->updateMutex); + + bool exit = false; + if (QMessageBox::critical(nullptr, - QTStr("Remux.ExitUnfinishedTitle"), - QTStr("Remux.ExitUnfinished"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No) == + QTStr("Remux.ExitUnfinishedTitle"), + QTStr("Remux.ExitUnfinished"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) == QMessageBox::Yes) { - os_event_signal(worker->stop); - return true; + exit = true; } - return false; + if (exit) { + // Inform the worker it should no longer be + // working. It will interrupt accordingly in + // its next update callback. + worker->isWorking = false; + } + + return exit; } OBSRemux::~OBSRemux() { - Stop(); + stopRemux(); remuxer.quit(); remuxer.wait(); } -#define RECORDING_PATTERN "(*.flv *.mp4 *.mov *.mkv *.ts *.m3u8)" - -void OBSRemux::BrowseInput() +void OBSRemux::rowCountChanged(const QModelIndex &, int, int) { - QString path = ui->sourceFile->text(); - if (path.isEmpty()) - path = recPath; - - path = QFileDialog::getOpenFileName(this, - QTStr("Remux.SelectRecording"), path, - QTStr("Remux.OBSRecording") + QString(" ") + - RECORDING_PATTERN); - - inputChanged(path); + // See if there are still any rows ready to remux. Change + // the state of the "go" button accordingly. + // There must be more than one row, since there will always be + // at least one row for the empty insertion point. + if (queueModel->rowCount() > 1) { + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setEnabled(queueModel->canClearFinished()); + } else { + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setEnabled(false); + } } -void OBSRemux::inputChanged(const QString &path) +void OBSRemux::dropEvent(QDropEvent *ev) { - if (!QFileInfo::exists(path)) { - ui->buttonBox->button(QDialogButtonBox::Ok)-> - setEnabled(false); + QStringList urlList; + + for (QUrl url : ev->mimeData()->urls()) { + QFileInfo fileInfo(url.toLocalFile()); + + if (fileInfo.isDir()) { + QStringList directoryFilter; + directoryFilter << + "*.flv" << + "*.mp4" << + "*.mov" << + "*.mkv" << + "*.ts" << + "*.m3u8"; + + QDirIterator dirIter(fileInfo.absoluteFilePath(), + directoryFilter, QDir::Files, + QDirIterator::Subdirectories); + + while (dirIter.hasNext()) { + urlList.append(dirIter.next()); + } + } else { + urlList.append(fileInfo.canonicalFilePath()); + } + } + + if (urlList.empty()) { + QMessageBox::information(nullptr, + QTStr("Remux.NoFilesAddedTitle"), + QTStr("Remux.NoFilesAdded"), QMessageBox::Ok); + } else if (!autoRemux) { + QModelIndex insertIndex = queueModel->index( + queueModel->rowCount() - 1, + RemuxEntryColumn::InputPath); + queueModel->setData(insertIndex, urlList, + RemuxEntryRole::NewPathsToProcessRole); + } +} + +void OBSRemux::dragEnterEvent(QDragEnterEvent *ev) +{ + if (ev->mimeData()->hasUrls() && !worker->isWorking) + ev->accept(); +} + +void OBSRemux::beginRemux() +{ + if (worker->isWorking) { + stopRemux(); return; } - ui->sourceFile->setText(path); - ui->buttonBox->button(QDialogButtonBox::Ok)-> - setEnabled(true); + bool proceedWithRemux = true; + QFileInfoList overwriteFiles = queueModel->checkForOverwrites(); - QFileInfo fi(path); - QString mp4 = fi.path() + "/" + fi.baseName() + ".mp4"; - ui->targetFile->setText(mp4); + if (!overwriteFiles.empty()) { + QString message = QTStr("Remux.FileExists"); + message += "\n\n"; - ui->targetFile->setEnabled(true); - ui->browseTarget->setEnabled(true); -} + for (QFileInfo fileInfo : overwriteFiles) + message += fileInfo.canonicalFilePath() + "\n"; -void OBSRemux::BrowseOutput() -{ - QString path(ui->targetFile->text()); - path = QFileDialog::getSaveFileName(this, QTStr("Remux.SelectTarget"), - path, RECORDING_PATTERN); + if (OBSMessageBox::question(this, + QTStr("Remux.FileExistsTitle"), message) + != QMessageBox::Yes) + proceedWithRemux = false; + } - if (path.isEmpty()) + if (!proceedWithRemux) return; - ui->targetFile->setText(path); -} - -void OBSRemux::Remux() -{ - if (QFileInfo::exists(ui->targetFile->text())) - if (OBSMessageBox::question(this, QTStr("Remux.FileExistsTitle"), - QTStr("Remux.FileExists")) != - QMessageBox::Yes) - return; - - media_remux_job_t mr_job = nullptr; - if (!media_remux_job_create(&mr_job, QT_TO_UTF8(ui->sourceFile->text()), - QT_TO_UTF8(ui->targetFile->text()))) - return; - - worker->job = job_t(mr_job, media_remux_job_destroy); - worker->lastProgress = 0.f; + // Set all jobs to "pending" first. + queueModel->beginProcessing(); ui->progressBar->setVisible(true); ui->buttonBox->button(QDialogButtonBox::Ok)-> - setEnabled(false); + setText(QTStr("Remux.Stop")); + setAcceptDrops(false); - emit remux(); + remuxNextEntry(); +} + +void OBSRemux::AutoRemux(QString inFile, QString outFile) +{ + if (inFile != "" && outFile != "" && autoRemux) { + emit remux(inFile, outFile); + autoRemuxFile = inFile; + } +} + +void OBSRemux::remuxNextEntry() +{ + worker->lastProgress = 0.f; + + QString inputPath, outputPath; + if (queueModel->beginNextEntry(inputPath, outputPath)) { + emit remux(inputPath, outputPath); + } else { + queueModel->autoRemux = autoRemux; + queueModel->endProcessing(); + + if (!autoRemux) { + OBSMessageBox::information(this, + QTStr("Remux.FinishedTitle"), + queueModel->checkForErrors() + ? QTStr("Remux.FinishedError") + : QTStr("Remux.Finished")); + } + + ui->progressBar->setVisible(autoRemux); + ui->buttonBox->button(QDialogButtonBox::Ok)-> + setText(QTStr("Remux.Remux")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)-> + setEnabled(true); + ui->buttonBox->button(QDialogButtonBox::Reset)-> + setEnabled(queueModel->canClearFinished()); + setAcceptDrops(true); + } } void OBSRemux::closeEvent(QCloseEvent *event) { - if (!Stop()) + if (!stopRemux()) event->ignore(); else QDialog::closeEvent(event); @@ -187,7 +923,7 @@ void OBSRemux::closeEvent(QCloseEvent *event) void OBSRemux::reject() { - if (!Stop()) + if (!stopRemux()) return; QDialog::reject(); @@ -200,27 +936,33 @@ void OBSRemux::updateProgress(float percent) void OBSRemux::remuxFinished(bool success) { - worker->job.reset(); - - OBSMessageBox::information(this, QTStr("Remux.FinishedTitle"), - success ? - QTStr("Remux.Finished") : QTStr("Remux.FinishedError")); - - ui->progressBar->setVisible(false); ui->buttonBox->button(QDialogButtonBox::Ok)-> setEnabled(true); + + queueModel->finishEntry(success); + + if (autoRemux && autoRemuxFile != "") { + QTimer::singleShot(3000, this, SLOT(close())); + } + + remuxNextEntry(); } -RemuxWorker::RemuxWorker() +void OBSRemux::clearFinished() { - os_event_init(&stop, OS_EVENT_TYPE_MANUAL); + queueModel->clearFinished(); } -RemuxWorker::~RemuxWorker() +void OBSRemux::clearAll() { - os_event_destroy(stop); + queueModel->clearAll(); } +/********************************************************** + Worker thread - Executes the libobs remux operation as a + background process. +**********************************************************/ + void RemuxWorker::UpdateProgress(float percent) { if (abs(lastProgress - percent) < 0.1f) @@ -230,16 +972,38 @@ void RemuxWorker::UpdateProgress(float percent) lastProgress = percent; } -void RemuxWorker::remux() +void RemuxWorker::remux(const QString &source, const QString &target) { + isWorking = true; + auto callback = [](void *data, float percent) { - auto rw = static_cast(data); + RemuxWorker *rw = static_cast(data); + + QMutexLocker lock(&rw->updateMutex); + rw->UpdateProgress(percent); - return !!os_event_try(rw->stop); + + return rw->isWorking; }; - bool success = media_remux_job_process(job.get(), callback, this); + bool stopped = false; + bool success = false; - emit remuxFinished(os_event_try(stop) && success); + media_remux_job_t mr_job = nullptr; + if (media_remux_job_create(&mr_job, + QT_TO_UTF8(source), + QT_TO_UTF8(target))) { + + success = media_remux_job_process(mr_job, callback, + this); + + media_remux_job_destroy(mr_job); + + stopped = !isWorking; + } + + isWorking = false; + + emit remuxFinished(!stopped && success); } diff --git a/UI/window-remux.hpp b/UI/window-remux.hpp index d689828..c98d45a 100644 --- a/UI/window-remux.hpp +++ b/UI/window-remux.hpp @@ -17,19 +17,36 @@ #pragma once +#include +#include #include #include +#include #include #include "ui_OBSRemux.h" #include #include +class RemuxQueueModel; class RemuxWorker; +enum RemuxEntryState +{ + Empty, + Ready, + Pending, + InProgress, + Complete, + InvalidPath, + Error +}; +Q_DECLARE_METATYPE(RemuxEntryState); + class OBSRemux : public QDialog { Q_OBJECT + QPointer queueModel; QThread remuxer; QPointer worker; @@ -37,46 +54,106 @@ class OBSRemux : public QDialog { const char *recPath; - void BrowseInput(); - void BrowseOutput(); - - bool Stop(); - virtual void closeEvent(QCloseEvent *event) override; virtual void reject() override; + bool autoRemux; + QString autoRemuxFile; + public: - explicit OBSRemux(const char *recPath, QWidget *parent = nullptr); + explicit OBSRemux(const char *recPath, QWidget *parent = nullptr, + bool autoRemux = false); virtual ~OBSRemux() override; using job_t = std::shared_ptr; + void AutoRemux(QString inFile, QString outFile); + +protected: + void dropEvent(QDropEvent *ev); + void dragEnterEvent(QDragEnterEvent *ev); + + void remuxNextEntry(); + private slots: - void inputChanged(const QString &str); + void rowCountChanged(const QModelIndex &parent, int first, int last); public slots: void updateProgress(float percent); void remuxFinished(bool success); - void Remux(); + void beginRemux(); + bool stopRemux(); + void clearFinished(); + void clearAll(); signals: - void remux(); + void remux(const QString &source, const QString &target); +}; + +class RemuxQueueModel : public QAbstractTableModel { + Q_OBJECT + + friend class OBSRemux; + +public: + RemuxQueueModel(QObject *parent = 0) + : QAbstractTableModel(parent) + , isProcessing(false) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, + int role); + + QFileInfoList checkForOverwrites() const; + bool checkForErrors() const; + void beginProcessing(); + void endProcessing(); + bool beginNextEntry(QString &inputPath, QString &outputPath); + void finishEntry(bool success); + bool canClearFinished() const; + void clearFinished(); + void clearAll(); + + bool autoRemux = false; + +private: + struct RemuxQueueEntry + { + RemuxEntryState state; + + QString sourcePath; + QString targetPath; + }; + + QList queue; + bool isProcessing; + + static QVariant getIcon(RemuxEntryState state); + + void checkInputPath(int row); }; class RemuxWorker : public QObject { Q_OBJECT - OBSRemux::job_t job; - os_event_t *stop; + QMutex updateMutex; + + bool isWorking; float lastProgress; void UpdateProgress(float percent); - explicit RemuxWorker(); - virtual ~RemuxWorker(); + explicit RemuxWorker() + : isWorking(false) { } + virtual ~RemuxWorker() {}; private slots: - void remux(); + void remux(const QString &source, const QString &target); signals: void updateProgress(float percent); @@ -84,3 +161,34 @@ signals: friend class OBSRemux; }; + +class RemuxEntryPathItemDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + RemuxEntryPathItemDelegate(bool isOutput, const QString &defaultPath); + + virtual QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem & /* option */, + const QModelIndex &index) const override; + + virtual void setEditorData(QWidget *editor, const QModelIndex &index) + const override; + virtual void setModelData(QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const override; + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + +private: + bool isOutput; + QString defaultPath; + const char *PATH_LIST_PROP = "pathList"; + + void handleBrowse(QWidget *container); + void handleClear(QWidget *container); + +private slots: + void updateText(); +}; diff --git a/UI/xdg-data/CMakeLists.txt b/UI/xdg-data/CMakeLists.txt new file mode 100644 index 0000000..9acd14e --- /dev/null +++ b/UI/xdg-data/CMakeLists.txt @@ -0,0 +1,24 @@ +if(NOT DEFINED APPDATA_RELEASE_DATE) + if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + execute_process(COMMAND git log --tags -1 --pretty=%cd --date=short + OUTPUT_VARIABLE APPDATA_RELEASE_DATE + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_STRIP_TRAILING_WHITESPACE) + else() + file(TIMESTAMP "${CMAKE_SOURCE_DIR}/CMakeLists.txt" APPDATA_RELEASE_DATE "%Y-%m-%d") + endif() +endif() + +configure_file( + com.obsproject.Studio.appdata.xml.in + com.obsproject.Studio.appdata.xml) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.appdata.xml + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo) + +install(FILES com.obsproject.Studio.desktop + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) + +install(FILES ../forms/images/obs.png + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps + RENAME com.obsproject.Studio.png) diff --git a/UI/xdg-data/com.obsproject.Studio.appdata.xml.in b/UI/xdg-data/com.obsproject.Studio.appdata.xml.in new file mode 100644 index 0000000..6844e88 --- /dev/null +++ b/UI/xdg-data/com.obsproject.Studio.appdata.xml.in @@ -0,0 +1,23 @@ + + + com.obsproject.Studio + com.obsproject.Studio.desktop + CC0-1.0 + GPL-2.0 + OBS Studio + OBS Project + Live streaming and video recording software + +

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

+
+ https://obsproject.com + + + https://obsproject.com/assets/images/OBSDemoApp.jpg + + + + + + +
diff --git a/UI/dist/obs.desktop b/UI/xdg-data/com.obsproject.Studio.desktop similarity index 67% rename from UI/dist/obs.desktop rename to UI/xdg-data/com.obsproject.Studio.desktop index f70a77c..05f80d3 100644 --- a/UI/dist/obs.desktop +++ b/UI/xdg-data/com.obsproject.Studio.desktop @@ -1,11 +1,13 @@ [Desktop Entry] Version=1.0 -Name=OBS +Name=OBS Studio GenericName=Streaming/Recording Software +GenericName[fr]=Logiciel d'enregistrement/diffusion Comment=Free and Open Source Streaming/Recording Software +Comment[fr]=Logiciel libre d'enregistrement et de diffusion sur Internet Comment[ru]=Бесплатная программа с открытым кодом для записи/трансляции видео Exec=obs -Icon=obs +Icon=com.obsproject.Studio Terminal=false Type=Application Categories=AudioVideo;Recorder; diff --git a/appveyor.yml b/appveyor.yml index e679bfc..aa698b2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,38 +1,39 @@ -image: Visual Studio 2017 +image: + - Ubuntu + - Visual Studio 2017 + environment: CURL_VERSION: 7.56.1 CEF_VERSION: 3.3440.1805.gbe070f9 + APPVEYOR_YML_DISABLE_PS_LINUX: true + TWITCH-CLIENTID: + secure: D3vFGk41HZaJWAZu5slwAHZhB868mGI2aIMS03L2rt4= + TWITCH-HASH: + secure: IRP5JLwBmPMjA7ADbZu6x2u4thqeEF9HaU1xolhaj74= + MIXER-CLIENTID: + secure: lQXRRvkeZDVSTUcSaR/kthiKu89K18IubKN913PC0ldjHBQ6sWa8a/t4i1f441N3RPfzAbTrXf2ijRCYYub2Qw== + MIXER-HASH: + secure: fIxeDxHkiNodwPeNGX+E9tSpyjg/XuT+U9N+jtpT7xU= + RESTREAM-CLIENTID: + secure: 4KGqX7PbL100GhXWzMFziaHrD/XT9/QKFTGinmPqwOkfRSMMEkUyL998/z3cclUd + RESTREAM-HASH: + secure: +tu/wCCG3tXnFpjzzXF6zyAOJyt8w48qx47K1bIn/Gs= install: - git submodule update --init --recursive - - if exist dependencies2017.zip (curl -kLO https://obsproject.com/downloads/dependencies2017.zip -f --retry 5 -z dependencies2017.zip) else (curl -kLO https://obsproject.com/downloads/dependencies2017.zip -f --retry 5 -C -) - - if exist vlc.zip (curl -kLO https://obsproject.com/downloads/vlc.zip -f --retry 5 -z vlc.zip) else (curl -kLO https://obsproject.com/downloads/vlc.zip -f --retry 5 -C -) - - if exist cef_binary_%CEF_VERSION%_windows32.zip (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows32.zip) else (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows32.zip -f --retry 5 -C -) - - if exist cef_binary_%CEF_VERSION%_windows64.zip (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64.zip -f --retry 5 -z cef_binary_%CEF_VERSION%_windows64.zip) else (curl -kLO https://obsproject.com/downloads/cef_binary_%CEF_VERSION%_windows64.zip -f --retry 5 -C -) - - 7z x dependencies2017.zip -odependencies2017 - - 7z x vlc.zip -ovlc - - 7z x cef_binary_%CEF_VERSION%_windows32.zip -oCEF_32 - - 7z x cef_binary_%CEF_VERSION%_windows64.zip -oCEF_64 - - set DepsPath32=%CD%\dependencies2017\win32 - - set DepsPath64=%CD%\dependencies2017\win64 - - set VLCPath=%CD%\vlc - - set QTDIR32=C:\Qt\5.11.1\msvc2015 - - set QTDIR64=C:\Qt\5.11.1\msvc2017_64 - - set CEF_32=%CD%\CEF_32\cef_binary_%CEF_VERSION%_windows32 - - set CEF_64=%CD%\CEF_64\cef_binary_%CEF_VERSION%_windows64 - - set build_config=RelWithDebInfo - - mkdir build build32 build64 - - cd ./build32 - - cmake -G "Visual Studio 15 2017" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% .. - - cd ../build64 - - cmake -G "Visual Studio 15 2017 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% .. + - cmd: C:\projects\obs-studio\CI\install-qt-win.cmd + - cmd: C:\projects\obs-studio\CI\install-script-win.cmd + - sh: ./CI/install-dependencies-linux-ubuntu16.sh + - sh: ./CI/before-script-linux.sh + - sh: ./CI/install-script-linux.sh build_script: - - call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - - call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - cmd: call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - cmd: call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - sh: cd ./build && make -j4 && cd - before_deploy: - - C:\projects\obs-studio\CI\before-deploy-win.cmd + - cmd: C:\projects\obs-studio\CI\before-deploy-win.cmd deploy_script: - ps: Push-AppveyorArtifact "build.zip" -FileName "$(git describe).zip" @@ -44,6 +45,7 @@ cache: - vlc.zip - 'cef_binary_%CEF_VERSION%_windows32.zip' - 'cef_binary_%CEF_VERSION%_windows64.zip' + - Qt_5.10.1.7z notifications: - provider: Webhook diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..45fcdaf --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,108 @@ +# https://aka.ms/yaml + +variables: + CMAKE_PREFIX_PATH: /usr/local/opt/qt5/lib/cmake + CEF_BUILD_VERSION: 3.3282.1726.gc8368c8 + CEF_VERSION: 3.3440.1805.gbe070f9 + TWITCH-CLIENTID: $(twitch_clientid) + TWITCH-HASH: $(twitch_hash) + MIXER-CLIENTID: $(mixer_clientid) + MIXER-HASH: $(mixer_hash) + RESTREAM-CLIENTID: $(restream_clientid) + RESTREAM-HASH: $(restream_hash) + +jobs: +- job: 'Build_macOS' + pool: + vmImage: 'macos-10.13' + steps: + - script: git submodule update --init --recursive + displayName: 'Checkout Submodules' + - script: ./CI/install-dependencies-osx.sh + displayName: 'Install Dependencies' + + - script: ./CI/before-script-osx.sh + displayName: 'Cmake' + + - bash: | + cd ./build + make -j4 + cd - + displayName: 'Build' + + - script: ./CI/before-deploy-osx.sh + condition: ne(variables['Build.Reason'], 'PullRequest') + displayName: 'Before Deploy' + + - task: PublishBuildArtifacts@1 + condition: ne(variables['Build.Reason'], 'PullRequest') + inputs: + pathtoPublish: './nightly' + artifactName: build + +- job: 'Build_Windows32' + pool: + vmImage: 'vs2017-win2016' + steps: + - script: git submodule update --init --recursive + displayName: 'Checkout Submodules' + - script: ./CI/install-qt-win.cmd + displayName: 'Install QT' + - script: ./CI/install-script-win.cmd + displayName: 'Download / Setup Deps / Run CMake' + - task: MSBuild@1 + displayName: 'Build 32-bit' + inputs: + msbuildArguments: '/m /p:Configuration=RelWithDebInfo' + solution: .\build32\obs-studio.sln + - script: ./CI/before-deploy-win.cmd + condition: ne(variables['Build.Reason'], 'PullRequest') + displayName: 'Before deploy' + - task: PublishBuildArtifacts@1 + condition: ne(variables['Build.Reason'], 'PullRequest') + inputs: + pathtoPublish: './build' + artifactName: winbuild + +- job: 'Build_Windows64' + pool: + vmImage: 'vs2017-win2016' + steps: + - script: git submodule update --init --recursive + displayName: 'Checkout Submodules' + - script: ./CI/install-qt-win.cmd + displayName: 'Install QT' + - script: ./CI/install-script-win.cmd + displayName: 'Download / Setup Deps / Run CMake' + - task: MSBuild@1 + displayName: 'Build 64-bit' + inputs: + msbuildArguments: '/m /p:Configuration=RelWithDebInfo' + solution: .\build64\obs-studio.sln + - script: ./CI/before-deploy-win.cmd + condition: ne(variables['Build.Reason'], 'PullRequest') + displayName: 'Before deploy' + - task: PublishBuildArtifacts@1 + condition: ne(variables['Build.Reason'], 'PullRequest') + inputs: + pathtoPublish: './build' + artifactName: winbuild + +- job: 'Build_Linux' + pool: + vmImage: 'ubuntu-16.04' + steps: + - script: git submodule update --init --recursive + displayName: 'Checkout Submodules' + - script: ./CI/install-dependencies-linux.sh + displayName: 'Install Dependencies' + + - script: ./CI/before-script-linux.sh + displayName: 'CMake' + + - bash: | + cd ./build + make -j4 + cd - + displayName: 'Build' + diff --git a/cmake/Modules/CopyMSVCBins.cmake b/cmake/Modules/CopyMSVCBins.cmake index 855ce0a..d5c67cc 100644 --- a/cmake/Modules/CopyMSVCBins.cmake +++ b/cmake/Modules/CopyMSVCBins.cmake @@ -151,23 +151,36 @@ file(GLOB QT_DEBUG_BIN_FILES "${Qt5Core_DIR}/../../../bin/Qt5Cored.dll" "${Qt5Core_DIR}/../../../bin/Qt5Guid.dll" "${Qt5Core_DIR}/../../../bin/Qt5Widgetsd.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Svgd.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Xmld.dll" "${Qt5Core_DIR}/../../../bin/libGLESv2d.dll" "${Qt5Core_DIR}/../../../bin/libEGLd.dll") file(GLOB QT_DEBUG_PLAT_BIN_FILES "${Qt5Core_DIR}/../../../plugins/platforms/qwindowsd.dll") file(GLOB QT_DEBUG_STYLES_BIN_FILES "${Qt5Core_DIR}/../../../plugins/styles/qwindowsvistastyled.dll") +file(GLOB QT_DEBUG_ICONENGINE_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/iconengines/qsvgicond.dll") +file(GLOB QT_DEBUG_IMAGEFORMATS_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/imageformats/qsvgd.dll") + file(GLOB QT_BIN_FILES "${Qt5Core_DIR}/../../../bin/Qt5Core.dll" "${Qt5Core_DIR}/../../../bin/Qt5Gui.dll" "${Qt5Core_DIR}/../../../bin/Qt5Widgets.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Svg.dll" + "${Qt5Core_DIR}/../../../bin/Qt5Xml.dll" "${Qt5Core_DIR}/../../../bin/libGLESv2.dll" "${Qt5Core_DIR}/../../../bin/libEGL.dll") file(GLOB QT_PLAT_BIN_FILES "${Qt5Core_DIR}/../../../plugins/platforms/qwindows.dll") file(GLOB QT_STYLES_BIN_FILES "${Qt5Core_DIR}/../../../plugins/styles/qwindowsvistastyle.dll") +file(GLOB QT_ICONENGINE_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/iconengines/qsvgicon.dll") +file(GLOB QT_IMAGEFORMATS_BIN_FILES + "${Qt5Core_DIR}/../../../plugins/imageformats/qsvg.dll") file(GLOB QT_ICU_BIN_FILES "${Qt5Core_DIR}/../../../bin/icu*.dll") @@ -201,10 +214,24 @@ set(ALL_STYLES_REL_BIN_FILES set(ALL_STYLES_DBG_BIN_FILES ${QT_DEBUG_STYLES_BIN_FILES}) +set(ALL_ICONENGINE_BIN_FILES) +set(ALL_ICONENGINE_REL_BIN_FILES + ${QT_ICONENGINE_BIN_FILES}) +set(ALL_ICONENGINE_DBG_BIN_FILES + ${QT_DEBUG_ICONENGINE_BIN_FILES}) + +set(ALL_IMAGEFORMATS_BIN_FILES) +set(ALL_IMAGEFORMATS_REL_BIN_FILES + ${QT_IMAGEFORMATS_BIN_FILES}) +set(ALL_IMAGEFORMATS_DBG_BIN_FILES + ${QT_DEBUG_ICONENGINE_BIN_FILES}) + foreach(list ALL_BASE_BIN_FILES ALL_REL_BIN_FILES ALL_DBG_BIN_FILES ALL_PLATFORM_BIN_FILES ALL_PLATFORM_REL_BIN_FILES ALL_PLATFORM_DBG_BIN_FILES - ALL_STYLES_BIN_FILES ALL_STYLES_REL_BIN_FILES ALL_STYLES_DBG_BIN_FILES) + ALL_STYLES_BIN_FILES ALL_STYLES_REL_BIN_FILES ALL_STYLES_DBG_BIN_FILES + ALL_ICONENGINE_BIN_FILES ALL_ICONENGINE_REL_BIN_FILES ALL_ICONENGINE_DGB_BIN_FILES + ALL_IMAGEFORMATS_BIN_FILES ALL_IMAGEFORMATS_REL_BIN_FILES ALL_IMAGEFORMATS_DGB_BIN_FILES) if(${list}) list(REMOVE_DUPLICATES ${list}) endif() @@ -221,9 +248,13 @@ message(STATUS "zlib files: ${ZLIB_BIN_FILES}") message(STATUS "QT Debug files: ${QT_DEBUG_BIN_FILES}") message(STATUS "QT Debug Platform files: ${QT_DEBUG_PLAT_BIN_FILES}") message(STATUS "QT Debug Styles files: ${QT_DEBUG_STYLES_BIN_FILES}") +message(STATUS "QT Debug Iconengine files: ${QT_DEBUG_ICONENGINE_BIN_FILES}") +message(STATUS "QT Debug Imageformat files: ${QT_DEBUG_IMAGEFORMATS_BIN_FILES}") message(STATUS "QT Release files: ${QT_BIN_FILES}") message(STATUS "QT Release Platform files: ${QT_PLAT_BIN_FILES}") message(STATUS "QT Release Styles files: ${QT_STYLES_BIN_FILES}") +message(STATUS "QT Release Iconengine files: ${QT_REL_ICONENGINE_BIN_FILES}") +message(STATUS "QT Release Imageformat files: ${QT_REL_IMAGEFORMATS_BIN_FILES}") message(STATUS "QT ICU files: ${QT_ICU_BIN_FILES}") foreach(BinFile ${ALL_BASE_BIN_FILES}) @@ -271,4 +302,34 @@ foreach(BinFile ${ALL_STYLES_DBG_BIN_FILES}) file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/styles/") endforeach() +foreach(BinFile ${ALL_ICONENGINE_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/iconengines") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/iconengines/") +endforeach() + +foreach(BinFile ${ALL_ICONENGINE_REL_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/iconengines") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/iconengines/") +endforeach() + +foreach(BinFile ${ALL_ICONENGINE_DBG_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/iconengines") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/iconengines/") +endforeach() + +foreach(BinFile ${ALL_IMAGEFORMATS_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/imageformats") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/imageformats/") +endforeach() + +foreach(BinFile ${ALL_IMAGEFORMATS_REL_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/imageformats") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/imageformats/") +endforeach() + +foreach(BinFile ${ALL_IMAGEFORMATS_DBG_BIN_FILES}) + make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/imageformats") + file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/imageformats/") +endforeach() + set(COPIED_DEPENDENCIES TRUE CACHE BOOL "Dependencies have been copied, set to false to copy again" FORCE) diff --git a/cmake/Modules/FindLibVLC.cmake b/cmake/Modules/FindLibVLC.cmake index 3f68885..c8cb555 100644 --- a/cmake/Modules/FindLibVLC.cmake +++ b/cmake/Modules/FindLibVLC.cmake @@ -10,7 +10,7 @@ find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) - pkg_check_modules(_VLC QUIET VLC) + pkg_check_modules(_VLC QUIET libvlc) endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) diff --git a/cmake/Modules/ObsHelpers.cmake b/cmake/Modules/ObsHelpers.cmake index c0073d7..627aadf 100644 --- a/cmake/Modules/ObsHelpers.cmake +++ b/cmake/Modules/ObsHelpers.cmake @@ -504,6 +504,30 @@ function(install_obs_data target datadir datadest) endif() endfunction() +function(install_obs_data_file target datafile datadest) + install(FILES ${datafile} + DESTINATION "${OBS_DATA_DESTINATION}/${datadest}") + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory + "${OBS_OUTPUT_DIR}/$/data/${datadest}" + VERBATIM) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/${datafile}" "${OBS_OUTPUT_DIR}/$/data/${datadest}" + VERBATIM) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND DEFINED ENV{obsInstallerTempDir}) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory + "$ENV{obsInstallerTempDir}/${OBS_DATA_DESTINATION}/${datadest}" + VERBATIM) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/${datafile}" "$ENV{obsInstallerTempDir}/${OBS_DATA_DESTINATION}/${datadest}" + VERBATIM) + endif() +endfunction() + function(install_obs_datatarget target datadest) install(TARGETS ${target} LIBRARY DESTINATION "${OBS_DATA_DESTINATION}/${datadest}" diff --git a/deps/ipc-util/CMakeLists.txt b/deps/ipc-util/CMakeLists.txt index 8b2e672..ac61039 100644 --- a/deps/ipc-util/CMakeLists.txt +++ b/deps/ipc-util/CMakeLists.txt @@ -23,7 +23,7 @@ else() endif() if(MSVC) - add_compile_options("$<$:/MT>") + add_compile_options($,/MTd,/MT> /Zl) endif() add_library(ipc-util STATIC diff --git a/deps/media-playback/CMakeLists.txt b/deps/media-playback/CMakeLists.txt index ae5b3c5..3accdd8 100644 --- a/deps/media-playback/CMakeLists.txt +++ b/deps/media-playback/CMakeLists.txt @@ -22,7 +22,12 @@ add_library(media-playback STATIC ${media-playback_SOURCES} ) -if(NOT MSVC) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le") + target_compile_options(media-playback + PUBLIC + -mvsx) + add_compile_definitions(NO_WARN_X86_INTRINSICS) +elseif(NOT MSVC) target_compile_options(media-playback PUBLIC -mmmx diff --git a/deps/media-playback/media-playback/closest-format.h b/deps/media-playback/media-playback/closest-format.h index 4a0c25e..8377914 100644 --- a/deps/media-playback/media-playback/closest-format.h +++ b/deps/media-playback/media-playback/closest-format.h @@ -21,6 +21,8 @@ static enum AVPixelFormat closest_format(enum AVPixelFormat fmt) switch (fmt) { case AV_PIX_FMT_YUYV422: return AV_PIX_FMT_YUYV422; + case AV_PIX_FMT_YUV444P: + return AV_PIX_FMT_YUV444P; case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUVJ422P: diff --git a/deps/media-playback/media-playback/media.c b/deps/media-playback/media-playback/media.c index e335e23..426380d 100644 --- a/deps/media-playback/media-playback/media.c +++ b/deps/media-playback/media-playback/media.c @@ -34,6 +34,7 @@ static inline enum video_format convert_pixel_format(int f) 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_YUV444P: return VIDEO_FORMAT_I444; 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; diff --git a/deps/obs-scripting/CMakeLists.txt b/deps/obs-scripting/CMakeLists.txt index 835c1b3..92fb1b9 100644 --- a/deps/obs-scripting/CMakeLists.txt +++ b/deps/obs-scripting/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 2.8) if(NOT ENABLE_SCRIPTING) + message(STATUS "Scripting plugin disabled") return() endif() @@ -11,13 +12,54 @@ if(MSVC) w32-pthreads) endif() -find_package(Luajit QUIET) -find_package(PythonDeps QUIET) -find_package(SwigDeps QUIET 2) +option(DISABLE_LUA "Disable Lua scripting support" OFF) +option(DISABLE_PYTHON "Disable Python scripting support" OFF) set(COMPILE_PYTHON FALSE CACHE BOOL "" FORCE) set(COMPILE_LUA FALSE CACHE BOOL "" FORCE) +if(NOT DISABLE_LUA) + find_package(Luajit QUIET) + + if(NOT DISABLE_LUA AND NOT LUAJIT_FOUND) + message(STATUS "Luajit support not found.") + set(LUAJIT_FOUND FALSE) + else() + message(STATUS "Scripting: Luajit supported") + set(COMPILE_LUA TRUE CACHE BOOL "" FORCE) + endif() +else() + message(STATUS "Scripting: Luajit support disabled") + set(LUAJIT_FOUND FALSE) +endif() + +if(NOT DISABLE_PYTHON) + find_package(PythonDeps QUIET) + + if(NOT DISABLE_PYTHON AND NOT PYTHONLIBS_FOUND) + message(STATUS "Python support not found.") + set(PYTHON_FOUND FALSE) + set(PYTHONLIBS_FOUND FALSE) + else() + message(STATUS "Scripting: Python 3 supported") + set(PYTHON_FOUND TRUE) + set(COMPILE_PYTHON TRUE CACHE BOOL "" FORCE) + + get_filename_component(PYTHON_LIB "${PYTHON_LIBRARIES}" NAME) + string(REGEX REPLACE "\\.[^.]*$" "" PYTHON_LIB ${PYTHON_LIB}) + + if(WIN32) + string(REGEX REPLACE "_d" "" PYTHON_LIB "${PYTHON_LIB}") + endif() + endif() +else() + message(STATUS "Scripting: Python 3 support disabled") + set(PYTHON_FOUND FALSE) + set(PYTHONLIBS_FOUND FALSE) +endif() + +find_package(SwigDeps QUIET 2) + if(NOT SWIG_FOUND) message(STATUS "Scripting: SWIG not found; scripting disabled") return() @@ -28,29 +70,6 @@ if(NOT PYTHONLIBS_FOUND AND NOT LUAJIT_FOUND) return() endif() -if(NOT LUAJIT_FOUND) - message(STATUS "Scripting: Luajit not found; Luajit support disabled") -else() - message(STATUS "Scripting: Luajit supported") - set(COMPILE_LUA TRUE CACHE BOOL "" FORCE) -endif() - -if(NOT PYTHONLIBS_FOUND) - message(STATUS "Scripting: Python 3 not found; Python support disabled") - set(PYTHON_FOUND FALSE) - set(PYTHONLIBS_FOUND FALSE) -else() - message(STATUS "Scripting: Python 3 supported") - set(PYTHON_FOUND TRUE) - set(COMPILE_PYTHON TRUE CACHE BOOL "" FORCE) - - get_filename_component(PYTHON_LIB "${PYTHON_LIBRARIES}" NAME) - string(REGEX REPLACE "\\.[^.]*$" "" PYTHON_LIB ${PYTHON_LIB}) - if(WIN32) - string(REGEX REPLACE "_d" "" PYTHON_LIB "${PYTHON_LIB}") - endif() -endif() - set(SCRIPTING_ENABLED ON CACHE BOOL "Interal global cmake variable" FORCE) if(UI_ENABLED) @@ -58,6 +77,10 @@ if(UI_ENABLED) include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/UI/obs-frontend-api") endif() +IF(BUILD_CAPTIONS) + string(TOUPPER "${BUILD_CAPTIONS}" BUILD_CAPTIONS) +endif() + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/obs-scripting-config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/obs-scripting-config.h") diff --git a/deps/obs-scripting/obs-scripting-config.h.in b/deps/obs-scripting/obs-scripting-config.h.in index 4205126..6fab8d8 100644 --- a/deps/obs-scripting/obs-scripting-config.h.in +++ b/deps/obs-scripting/obs-scripting-config.h.in @@ -21,3 +21,4 @@ #define COMPILE_LUA @LUAJIT_FOUND@ #define COMPILE_PYTHON @PYTHON_FOUND@ #define UI_ENABLED @UI_ENABLED@ +#define BUILD_CAPTIONS @BUILD_CAPTIONS@ diff --git a/deps/obs-scripting/obslua/obslua.i b/deps/obs-scripting/obslua/obslua.i index 98058b8..6a97f2b 100644 --- a/deps/obs-scripting/obslua/obslua.i +++ b/deps/obs-scripting/obslua/obslua.i @@ -84,6 +84,7 @@ static inline void wrap_blog(int log_level, const char *message) %include "graphics/vec2.h" %include "graphics/quat.h" %include "graphics/image-file.h" +%include "obs-scripting-config.h" %include "obs-data.h" %include "obs-source.h" %include "obs-properties.h" @@ -95,7 +96,6 @@ static inline void wrap_blog(int log_level, const char *message) %include "callback/signal.h" %include "util/bmem.h" %include "util/base.h" -%include "obs-scripting-config.h" #if UI_ENABLED %include "obs-frontend-api.h" diff --git a/deps/obs-scripting/obspython/obspython.i b/deps/obs-scripting/obspython/obspython.i index 3c71413..b8a75bb 100644 --- a/deps/obs-scripting/obspython/obspython.i +++ b/deps/obs-scripting/obspython/obspython.i @@ -82,6 +82,7 @@ static inline void wrap_blog(int log_level, const char *message) %include "graphics/vec3.h" %include "graphics/vec2.h" %include "graphics/quat.h" +%include "obs-scripting-config.h" %include "obs-data.h" %include "obs-source.h" %include "obs-properties.h" @@ -93,7 +94,6 @@ static inline void wrap_blog(int log_level, const char *message) %include "callback/signal.h" %include "util/bmem.h" %include "util/base.h" -%include "obs-scripting-config.h" #if UI_ENABLED %include "obs-frontend-api.h" diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index 3746135..3b5b86d 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -235,6 +235,18 @@ Functions --------------------------------------- +.. function:: int obs_frontend_get_transition_duration(void) + + :return: The transition duration (in milliseconds) currently set in the UI. + +--------------------------------------- + +.. function:: void obs_frontend_set_transition_duration(int duration) + + :param duration: Desired transition duration (in milliseconds) + +--------------------------------------- + .. function:: char **obs_frontend_get_scene_collections(void) :return: The list of profile names, ending with NULL. The list is @@ -473,6 +485,12 @@ Functions --------------------------------------- +.. function:: void obs_frontend_preview_program_trigger_transition(void) + + Triggers a preview-to-program transition if studio mode is active. + +--------------------------------------- + .. function:: obs_source_t *obs_frontend_get_current_preview_scene(void) :return: A new reference to the current preview scene if studio mode diff --git a/docs/sphinx/reference-libobs-graphics-effects.rst b/docs/sphinx/reference-libobs-graphics-effects.rst index 1392b41..e7c0ec3 100644 --- a/docs/sphinx/reference-libobs-graphics-effects.rst +++ b/docs/sphinx/reference-libobs-graphics-effects.rst @@ -154,6 +154,36 @@ HLSL format. --------------------- +.. function:: size_t gs_param_get_num_annotations(const gs_eparam_t *param) + + Gets the number of annotations associated with the parameter. + + :param param: Param object + :return: Number of annotations the param has + +--------------------- + +.. function:: gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, size_t annotation) + + Gets an annotation of a param by its index. + + :param param: Param object + :param param: Annotation index + :return: The effect parameter object (annotation), or *NULL* if index + invalid + +--------------------- + +.. function:: gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *pardam, const char *annotation) + + Gets parameter of an effect by its name. + + :param param: Param object + :param name: Name of the annotation + :return: The effect parameter object (annotation), or *NULL* if not found + +--------------------- + .. function:: bool gs_effect_loop(gs_effect_t *effect, const char *name) Helper function that automatically begins techniques/passes. @@ -332,3 +362,39 @@ HLSL format. :param param: Effect parameter :param sampler: Sampler state object + +--------------------- + +.. function:: void *gs_effect_get_val(gs_eparam_t *param) + + Returns a copy of the param's current value. + + :param param: Effect parameter + :return: A pointer to the copied byte value of the param's current value. Freed with :c:func:`bfree()`. + +--------------------- + +.. function:: void gs_effect_get_default_val(gs_eparam_t *param) + + Returns a copy of the param's default value. + + :param param: Effect parameter + :return: A pointer to the copied byte value of the param's default value. Freed with :c:func:`bfree()`. + +--------------------- + +.. function:: size_t gs_effect_get_val_size(gs_eparam_t *param) + + Returns the size in bytes of the param's current value. + + :param param: Effect parameter + :return: The size in bytes of the param's current value. + +--------------------- + +.. function:: size_t gs_effect_get_default_val_size(gs_eparam_t *param) + + Returns the size in bytes of the param's default value. + + :param param: Effect parameter + :return: The size in bytes of the param's default value. diff --git a/docs/sphinx/reference-outputs.rst b/docs/sphinx/reference-outputs.rst index 404dd23..e7c65b6 100644 --- a/docs/sphinx/reference-outputs.rst +++ b/docs/sphinx/reference-outputs.rst @@ -117,6 +117,18 @@ Output Definition Structure (obs_output_info) This is called when the output recieves raw audio data. Only applies to outputs that are not encoded. + **This callback must be used with single-track raw outputs.** + + :param frames: The raw audio frames + +.. member:: void (*obs_output_info.raw_audio2)(void *data, size_t idx, struct audio_data *frames) + + This is called when the output recieves raw audio data. Only applies + to outputs that are not encoded. + + **This callback must be used with multi-track raw outputs.** + + :param idx: The audio track index :param frames: The raw audio frames .. member:: void (*obs_output_info.encoded_packet)(void *data, struct encoder_packet *packet) @@ -125,7 +137,10 @@ Output Definition Structure (obs_output_info) Only applies to outputs that are encoded. Packets will always be given in monotonic timestamp order. - :param packet: The video or audio packet + :param packet: The video or audio packet. If NULL, an encoder error + occurred, and the output should call + :c:func:`obs_output_signal_stop()` with the error code + **OBS_OUTPUT_ENCODE_ERROR**. .. member:: void (*obs_output_info.update)(void *data, obs_data_t *settings) @@ -240,6 +255,7 @@ Output Signals | OBS_OUTPUT_DISCONNECTED - Unexpectedly disconnected | OBS_OUTPUT_UNSUPPORTED - The settings, video/audio format, or codecs are unsupported by this output | OBS_OUTPUT_NO_SPACE - Ran out of disk space + | OBS_OUTPUT_ENCODE_ERROR - Encoder error **starting** (ptr output) @@ -472,7 +488,19 @@ General Output Functions .. function:: void obs_output_set_mixer(obs_output_t *output, size_t mixer_idx) size_t obs_output_get_mixer(const obs_output_t *output) - Sets/gets the current audio mixer for non-encoded outputs. + Sets/gets the current audio mixer for non-encoded outputs. For + multi-track outputs, this would be the equivalent of setting the mask + only for the specified mixer index. + +--------------------- + +.. function:: void obs_output_set_mixers(obs_output_t *output, size_t mixers) + size_t obs_output_get_mixers(const obs_output_t *output) + + Sets/gets the current audio mixers (via mask) for non-encoded + multi-track outputs. If used with single-track outputs, the + single-track output will use either the first set mixer track in the + bitmask, or the first track if none is set in the bitmask. --------------------- diff --git a/docs/sphinx/reference-scenes.rst b/docs/sphinx/reference-scenes.rst index c8faf3d..883b67e 100644 --- a/docs/sphinx/reference-scenes.rst +++ b/docs/sphinx/reference-scenes.rst @@ -279,6 +279,12 @@ Scene Item Functions --------------------- +.. function:: int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item) + + :return: The unique numeric identifier of the scene item. + +--------------------- + .. function:: void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos) void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos) diff --git a/libobs-d3d11/CMakeLists.txt b/libobs-d3d11/CMakeLists.txt index e30828b..34543c7 100644 --- a/libobs-d3d11/CMakeLists.txt +++ b/libobs-d3d11/CMakeLists.txt @@ -30,6 +30,7 @@ set_target_properties(libobs-d3d11 PREFIX "") target_link_libraries(libobs-d3d11 libobs + d3d9 d3d11 dxgi) diff --git a/libobs-d3d11/d3d11-rebuild.cpp b/libobs-d3d11/d3d11-rebuild.cpp index 376b8ea..6912cf7 100644 --- a/libobs-d3d11/d3d11-rebuild.cpp +++ b/libobs-d3d11/d3d11-rebuild.cpp @@ -17,14 +17,14 @@ #include "d3d11-subsystem.hpp" -inline void gs_vertex_buffer::Rebuild() +void gs_vertex_buffer::Rebuild() { uvBuffers.clear(); uvSizes.clear(); BuildBuffers(); } -inline void gs_index_buffer::Rebuild(ID3D11Device *dev) +void gs_index_buffer::Rebuild(ID3D11Device *dev) { HRESULT hr = dev->CreateBuffer(&bd, &srd, &indexBuffer); if (FAILED(hr)) @@ -55,7 +55,7 @@ void gs_texture_2d::RebuildSharedTextureFallback() isShared = false; } -inline void gs_texture_2d::Rebuild(ID3D11Device *dev) +void gs_texture_2d::Rebuild(ID3D11Device *dev) { HRESULT hr; if (isShared) { @@ -89,9 +89,61 @@ inline void gs_texture_2d::Rebuild(ID3D11Device *dev) if (FAILED(hr)) throw HRError("Failed to create GDI surface", hr); } + + acquired = false; + + if ((td.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) != 0) { + ComQIPtr dxgi_res(texture); + if (dxgi_res) + GetSharedHandle(dxgi_res); + device_texture_acquire_sync(this, 0, INFINITE); + } } -inline void gs_zstencil_buffer::Rebuild(ID3D11Device *dev) +void gs_texture_2d::RebuildNV12_Y(ID3D11Device *dev) +{ + gs_texture_2d *tex_uv = pairedNV12texture; + HRESULT hr; + + hr = dev->CreateTexture2D(&td, nullptr, &texture); + if (FAILED(hr)) + throw HRError("Failed to create 2D texture", hr); + + hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes); + if (FAILED(hr)) + throw HRError("Failed to create resource view", hr); + + if (isRenderTarget) + InitRenderTargets(); + + tex_uv->RebuildNV12_UV(dev); + + acquired = false; + + if ((td.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) != 0) { + ComQIPtr dxgi_res(texture); + if (dxgi_res) + GetSharedHandle(dxgi_res); + device_texture_acquire_sync(this, 0, INFINITE); + } +} + +void gs_texture_2d::RebuildNV12_UV(ID3D11Device *dev) +{ + gs_texture_2d *tex_y = pairedNV12texture; + HRESULT hr; + + texture = tex_y->texture; + + hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes); + if (FAILED(hr)) + throw HRError("Failed to create resource view", hr); + + if (isRenderTarget) + InitRenderTargets(); +} + +void gs_zstencil_buffer::Rebuild(ID3D11Device *dev) { HRESULT hr; hr = dev->CreateTexture2D(&td, nullptr, &texture); @@ -103,21 +155,21 @@ inline void gs_zstencil_buffer::Rebuild(ID3D11Device *dev) throw HRError("Failed to create depth stencil view", hr); } -inline void gs_stage_surface::Rebuild(ID3D11Device *dev) +void gs_stage_surface::Rebuild(ID3D11Device *dev) { HRESULT hr = dev->CreateTexture2D(&td, nullptr, &texture); if (FAILED(hr)) throw HRError("Failed to create staging surface", hr); } -inline void gs_sampler_state::Rebuild(ID3D11Device *dev) +void gs_sampler_state::Rebuild(ID3D11Device *dev) { HRESULT hr = dev->CreateSamplerState(&sd, state.Assign()); if (FAILED(hr)) throw HRError("Failed to create sampler state", hr); } -inline void gs_vertex_shader::Rebuild(ID3D11Device *dev) +void gs_vertex_shader::Rebuild(ID3D11Device *dev) { HRESULT hr; hr = dev->CreateVertexShader(data.data(), data.size(), nullptr, &shader); @@ -142,7 +194,7 @@ inline void gs_vertex_shader::Rebuild(ID3D11Device *dev) } } -inline void gs_pixel_shader::Rebuild(ID3D11Device *dev) +void gs_pixel_shader::Rebuild(ID3D11Device *dev) { HRESULT hr; @@ -164,7 +216,7 @@ inline void gs_pixel_shader::Rebuild(ID3D11Device *dev) } } -inline void gs_swap_chain::Rebuild(ID3D11Device *dev) +void gs_swap_chain::Rebuild(ID3D11Device *dev) { HRESULT hr = device->factory->CreateSwapChain(dev, &swapDesc, &swap); if (FAILED(hr)) @@ -172,21 +224,21 @@ inline void gs_swap_chain::Rebuild(ID3D11Device *dev) Init(); } -inline void SavedBlendState::Rebuild(ID3D11Device *dev) +void SavedBlendState::Rebuild(ID3D11Device *dev) { HRESULT hr = dev->CreateBlendState(&bd, &state); if (FAILED(hr)) throw HRError("Failed to create blend state", hr); } -inline void SavedZStencilState::Rebuild(ID3D11Device *dev) +void SavedZStencilState::Rebuild(ID3D11Device *dev) { HRESULT hr = dev->CreateDepthStencilState(&dsd, &state); if (FAILED(hr)) throw HRError("Failed to create depth stencil state", hr); } -inline void SavedRasterState::Rebuild(ID3D11Device *dev) +void SavedRasterState::Rebuild(ID3D11Device *dev) { HRESULT hr = dev->CreateRasterizerState(&rd, &state); if (FAILED(hr)) @@ -287,7 +339,14 @@ try { ((gs_index_buffer*)obj)->Rebuild(dev); break; case gs_type::gs_texture_2d: - ((gs_texture_2d*)obj)->Rebuild(dev); + { + gs_texture_2d *tex = (gs_texture_2d*)obj; + if (!tex->nv12) { + tex->Rebuild(dev); + } else if (!tex->chroma) { + tex->RebuildNV12_Y(dev); + } + } break; case gs_type::gs_zstencil_buffer: ((gs_zstencil_buffer*)obj)->Rebuild(dev); diff --git a/libobs-d3d11/d3d11-shader.cpp b/libobs-d3d11/d3d11-shader.cpp index 453ff24..1a71048 100644 --- a/libobs-d3d11/d3d11-shader.cpp +++ b/libobs-d3d11/d3d11-shader.cpp @@ -137,8 +137,11 @@ void gs_shader::BuildConstantBuffer() case GS_SHADER_PARAM_BOOL: case GS_SHADER_PARAM_INT: case GS_SHADER_PARAM_FLOAT: size = sizeof(float); break; + case GS_SHADER_PARAM_INT2: case GS_SHADER_PARAM_VEC2: size = sizeof(vec2); break; + case GS_SHADER_PARAM_INT3: case GS_SHADER_PARAM_VEC3: size = sizeof(float)*3; break; + case GS_SHADER_PARAM_INT4: case GS_SHADER_PARAM_VEC4: size = sizeof(vec4); break; case GS_SHADER_PARAM_MATRIX4X4: size = sizeof(float)*4*4; diff --git a/libobs-d3d11/d3d11-stagesurf.cpp b/libobs-d3d11/d3d11-stagesurf.cpp index c08b462..efad5df 100644 --- a/libobs-d3d11/d3d11-stagesurf.cpp +++ b/libobs-d3d11/d3d11-stagesurf.cpp @@ -41,3 +41,28 @@ gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width, if (FAILED(hr)) throw HRError("Failed to create staging surface", hr); } + +gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width, + uint32_t height) + : gs_obj (device, gs_type::gs_stage_surface), + width (width), + height (height), + format (GS_UNKNOWN), + dxgiFormat (DXGI_FORMAT_NV12) +{ + HRESULT hr; + + memset(&td, 0, sizeof(td)); + td.Width = width; + td.Height = height; + td.MipLevels = 1; + td.ArraySize = 1; + td.Format = dxgiFormat; + td.SampleDesc.Count = 1; + td.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + td.Usage = D3D11_USAGE_STAGING; + + hr = device->device->CreateTexture2D(&td, NULL, texture.Assign()); + if (FAILED(hr)) + throw HRError("Failed to create staging surface", hr); +} diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index aa335aa..6b7a005 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -18,7 +18,10 @@ #include #include #include +#include +#include #include +#include #include "d3d11-subsystem.hpp" struct UnsupportedHWError : HRError { @@ -227,6 +230,120 @@ const static D3D_FEATURE_LEVEL featureLevels[] = D3D_FEATURE_LEVEL_9_3, }; +/* ------------------------------------------------------------------------- */ + +#define VERT_IN_OUT "\ +struct VertInOut { \ + float4 pos : POSITION; \ +}; " + +#define NV12_Y_PS VERT_IN_OUT "\ +float main(VertInOut vert_in) : TARGET \ +{ \ + return 1.0; \ +}" + +#define NV12_UV_PS VERT_IN_OUT "\ +float2 main(VertInOut vert_in) : TARGET \ +{ \ + return float2(1.0, 1.0); \ +}" + +#define NV12_VS VERT_IN_OUT "\ +VertInOut main(VertInOut vert_in) \ +{ \ + VertInOut vert_out; \ + vert_out.pos = float4(vert_in.pos.xyz, 1.0); \ + return vert_out; \ +} " + +/* ------------------------------------------------------------------------- */ + +#define NV12_CX 128 +#define NV12_CY 128 + +bool gs_device::HasBadNV12Output() +try { + vec3 points[4]; + vec3_set(&points[0], -1.0f, -1.0f, 0.0f); + vec3_set(&points[1], -1.0f, 1.0f, 0.0f); + vec3_set(&points[2], 1.0f, -1.0f, 0.0f); + vec3_set(&points[3], 1.0f, 1.0f, 0.0f); + + gs_texture_2d nv12_y(this, NV12_CX, NV12_CY, GS_R8, 1, nullptr, + GS_RENDER_TARGET | GS_SHARED_KM_TEX, GS_TEXTURE_2D, + false, true); + gs_texture_2d nv12_uv(this, nv12_y.texture, + GS_RENDER_TARGET | GS_SHARED_KM_TEX); + gs_vertex_shader nv12_vs(this, "", NV12_VS); + gs_pixel_shader nv12_y_ps(this, "", NV12_Y_PS); + gs_pixel_shader nv12_uv_ps(this, "", NV12_UV_PS); + gs_stage_surface nv12_stage(this, NV12_CX, NV12_CY); + + gs_vb_data *vbd = gs_vbdata_create(); + vbd->num = 4; + vbd->points = (vec3*)bmemdup(&points, sizeof(points)); + + gs_vertex_buffer buf(this, vbd, 0); + + device_load_vertexbuffer(this, &buf); + device_load_vertexshader(this, &nv12_vs); + + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + device_set_viewport(this, 0, 0, NV12_CX, NV12_CY); + device_set_cull_mode(this, GS_NEITHER); + device_enable_depth_test(this, false); + device_enable_blending(this, false); + LoadVertexBufferData(); + + device_set_render_target(this, &nv12_y, nullptr); + device_load_pixelshader(this, &nv12_y_ps); + UpdateBlendState(); + UpdateRasterState(); + UpdateZStencilState(); + context->Draw(4, 0); + + device_set_viewport(this, 0, 0, NV12_CX/2, NV12_CY/2); + device_set_render_target(this, &nv12_uv, nullptr); + device_load_pixelshader(this, &nv12_uv_ps); + UpdateBlendState(); + UpdateRasterState(); + UpdateZStencilState(); + context->Draw(4, 0); + + device_load_pixelshader(this, nullptr); + device_load_vertexshader(this, nullptr); + device_set_render_target(this, nullptr, nullptr); + + device_stage_texture(this, &nv12_stage, &nv12_y); + + uint8_t *data; + uint32_t linesize; + bool bad_driver = false; + + if (gs_stagesurface_map(&nv12_stage, &data, &linesize)) { + bad_driver = data[linesize * NV12_CY] == 0; + gs_stagesurface_unmap(&nv12_stage); + } else { + throw "Could not map surface"; + } + + if (bad_driver) { + blog(LOG_WARNING, "Bad NV12 texture handling detected! " + "Disabling NV12 texture support."); + } + return bad_driver; + +} catch (HRError error) { + blog(LOG_WARNING, "HasBadNV12Output failed: %s (%08lX)", + error.str, error.hr); + return false; +} catch (const char *error) { + blog(LOG_WARNING, "HasBadNV12Output failed: %s", error); + return false; +} + void gs_device::InitDevice(uint32_t adapterIdx) { wstring adapterName; @@ -244,11 +361,10 @@ void gs_device::InitDevice(uint32_t adapterIdx) adapterName = (adapter->GetDesc(&desc) == S_OK) ? desc.Description : L""; - char *adapterNameUTF8; + BPtr adapterNameUTF8; os_wcs_to_utf8_ptr(adapterName.c_str(), 0, &adapterNameUTF8); blog(LOG_INFO, "Loading up D3D11 on adapter %s (%" PRIu32 ")", - adapterNameUTF8, adapterIdx); - bfree(adapterNameUTF8); + adapterNameUTF8.Get(), adapterIdx); hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, createFlags, featureLevels, @@ -260,6 +376,49 @@ void gs_device::InitDevice(uint32_t adapterIdx) blog(LOG_INFO, "D3D11 loaded successfully, feature level used: %u", (unsigned int)levelUsed); + + /* ---------------------------------------- */ + /* check for nv12 texture output support */ + + nv12Supported = false; + + ComQIPtr d3d11_1(device); + if (!d3d11_1) { + return; + } + + /* needs to support extended resource sharing */ + D3D11_FEATURE_DATA_D3D11_OPTIONS opts = {}; + hr = d3d11_1->CheckFeatureSupport( + D3D11_FEATURE_D3D11_OPTIONS, + &opts, sizeof(opts)); + if (FAILED(hr) || !opts.ExtendedResourceSharing) { + return; + } + + /* needs to support the actual format */ + UINT support = 0; + hr = device->CheckFormatSupport( + DXGI_FORMAT_NV12, + &support); + if (FAILED(hr)) { + return; + } + + if ((support & D3D11_FORMAT_SUPPORT_TEXTURE2D) == 0) { + return; + } + + /* must be usable as a render target */ + if ((support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) == 0) { + return; + } + + if (HasBadNV12Output()) { + return; + } + + nv12Supported = true; } static inline void ConvertStencilSide(D3D11_DEPTH_STENCILOP_DESC &desc, @@ -505,7 +664,7 @@ static inline void EnumD3DAdapters( ComPtr factory; ComPtr adapter; HRESULT hr; - UINT i = 0; + UINT i; IID factoryIID = (GetWinVer() >= 0x602) ? dxgiFactory2 : __uuidof(IDXGIFactory1); @@ -514,7 +673,7 @@ static inline void EnumD3DAdapters( if (FAILED(hr)) throw HRError("Failed to create DXGIFactory", hr); - while (factory->EnumAdapters1(i++, adapter.Assign()) == S_OK) { + for (i = 0; factory->EnumAdapters1(i, adapter.Assign()) == S_OK; ++i) { DXGI_ADAPTER_DESC desc; char name[512] = ""; @@ -528,7 +687,7 @@ static inline void EnumD3DAdapters( os_wcs_to_utf8(desc.Description, 0, name, sizeof(name)); - if (!callback(param, name, i - 1)) + if (!callback(param, name, i)) break; } } @@ -550,10 +709,10 @@ bool device_enum_adapters( static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter) { - UINT i = 0; + UINT i; ComPtr output; - while (adapter->EnumOutputs(i++, &output) == S_OK) { + for (i = 0; adapter->EnumOutputs(i, &output) == S_OK; ++i) { DXGI_OUTPUT_DESC desc; if (FAILED(output->GetDesc(&desc))) continue; @@ -575,7 +734,7 @@ static inline void LogD3DAdapters() ComPtr factory; ComPtr adapter; HRESULT hr; - UINT i = 0; + UINT i; blog(LOG_INFO, "Available Video Adapters: "); @@ -586,7 +745,7 @@ static inline void LogD3DAdapters() if (FAILED(hr)) throw HRError("Failed to create DXGIFactory", hr); - while (factory->EnumAdapters1(i++, adapter.Assign()) == S_OK) { + for (i = 0; factory->EnumAdapters1(i, adapter.Assign()) == S_OK; ++i) { DXGI_ADAPTER_DESC desc; char name[512] = ""; @@ -736,8 +895,7 @@ gs_texture_t *device_texture_create(gs_device_t *device, uint32_t width, gs_texture *texture = NULL; try { texture = new gs_texture_2d(device, width, height, color_format, - levels, data, flags, GS_TEXTURE_2D, false, - false); + levels, data, flags, GS_TEXTURE_2D, false); } catch (HRError error) { blog(LOG_ERROR, "device_texture_create (D3D11): %s (%08lX)", error.str, error.hr); @@ -756,8 +914,7 @@ gs_texture_t *device_cubetexture_create(gs_device_t *device, uint32_t size, gs_texture *texture = NULL; try { texture = new gs_texture_2d(device, size, size, color_format, - levels, data, flags, GS_TEXTURE_CUBE, false, - false); + levels, data, flags, GS_TEXTURE_CUBE, false); } catch (HRError error) { blog(LOG_ERROR, "device_cubetexture_create (D3D11): %s " "(%08lX)", @@ -1317,7 +1474,7 @@ void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, throw "Source texture must be a 2D texture"; if (!dst) throw "Destination surface is NULL"; - if (dst->format != src->format) + if (dst->format != GS_UNKNOWN && dst->format != src->format) throw "Source and destination formats do not match"; if (dst->width != src2d->width || dst->height != src2d->height) @@ -2056,6 +2213,29 @@ extern "C" EXPORT bool device_shared_texture_available(void) return true; } +extern "C" EXPORT bool device_nv12_available(gs_device_t *device) +{ + return device->nv12Supported; +} + +extern "C" EXPORT void device_debug_marker_begin(gs_device_t *, + const char *markername, const float color[4]) +{ + D3DCOLOR bgra = D3DCOLOR_ARGB((DWORD)(255.0f * color[3]), + (DWORD)(255.0f * color[0]), (DWORD)(255.0f * color[1]), + (DWORD)(255.0f * color[2])); + + wchar_t wide[64]; + os_utf8_to_wcs(markername, 0, wide, _countof(wide)); + + D3DPERF_BeginEvent(bgra, wide); +} + +extern "C" EXPORT void device_debug_marker_end(gs_device_t *) +{ + D3DPERF_EndEvent(); +} + extern "C" EXPORT gs_texture_t *device_texture_create_gdi(gs_device_t *device, uint32_t width, uint32_t height) { @@ -2063,7 +2243,7 @@ extern "C" EXPORT gs_texture_t *device_texture_create_gdi(gs_device_t *device, try { texture = new gs_texture_2d(device, width, height, GS_BGRA, 1, nullptr, GS_RENDER_TARGET, GS_TEXTURE_2D, - true, false); + true); } catch (HRError error) { blog(LOG_ERROR, "device_texture_create_gdi (D3D11): %s (%08lX)", error.str, error.hr); @@ -2132,3 +2312,112 @@ extern "C" EXPORT gs_texture_t *device_texture_open_shared(gs_device_t *device, return texture; } + +extern "C" EXPORT uint32_t device_texture_get_shared_handle(gs_texture_t *tex) +{ + gs_texture_2d *tex2d = reinterpret_cast(tex); + if (tex->type != GS_TEXTURE_2D) + return GS_INVALID_HANDLE; + + return tex2d->isShared ? tex2d->sharedHandle : GS_INVALID_HANDLE; +} + +int device_texture_acquire_sync(gs_texture_t *tex, uint64_t key, uint32_t ms) +{ + gs_texture_2d *tex2d = reinterpret_cast(tex); + if (tex->type != GS_TEXTURE_2D) + return -1; + + if (tex2d->acquired) + return 0; + + ComQIPtr keyedMutex(tex2d->texture); + if (!keyedMutex) + return -1; + + HRESULT hr = keyedMutex->AcquireSync(key, ms); + if (hr == S_OK) { + tex2d->acquired = true; + return 0; + } else if (hr == WAIT_TIMEOUT) { + return ETIMEDOUT; + } + + return -1; +} + +extern "C" EXPORT int device_texture_release_sync(gs_texture_t *tex, + uint64_t key) +{ + gs_texture_2d *tex2d = reinterpret_cast(tex); + if (tex->type != GS_TEXTURE_2D) + return -1; + + if (!tex2d->acquired) + return 0; + + ComQIPtr keyedMutex(tex2d->texture); + if (!keyedMutex) + return -1; + + HRESULT hr = keyedMutex->ReleaseSync(key); + if (hr == S_OK) { + tex2d->acquired = false; + return 0; + } + + return -1; +} + +extern "C" EXPORT bool device_texture_create_nv12(gs_device_t *device, + gs_texture_t **p_tex_y, gs_texture_t **p_tex_uv, + uint32_t width, uint32_t height, uint32_t flags) +{ + if (!device->nv12Supported) + return false; + + *p_tex_y = nullptr; + *p_tex_uv = nullptr; + + gs_texture_2d *tex_y; + gs_texture_2d *tex_uv; + + try { + tex_y = new gs_texture_2d(device, width, height, GS_R8, 1, + nullptr, flags, GS_TEXTURE_2D, false, true); + tex_uv = new gs_texture_2d(device, tex_y->texture, flags); + + } catch (HRError error) { + blog(LOG_ERROR, "gs_texture_create_nv12 (D3D11): %s (%08lX)", + error.str, error.hr); + LogD3D11ErrorDetails(error, device); + return false; + + } catch (const char *error) { + blog(LOG_ERROR, "gs_texture_create_nv12 (D3D11): %s", error); + return false; + } + + tex_y->pairedNV12texture = tex_uv; + tex_uv->pairedNV12texture = tex_y; + + *p_tex_y = tex_y; + *p_tex_uv = tex_uv; + return true; +} + +extern "C" EXPORT gs_stagesurf_t *device_stagesurface_create_nv12( + gs_device_t *device, uint32_t width, uint32_t height) +{ + gs_stage_surface *surf = NULL; + try { + surf = new gs_stage_surface(device, width, height); + } catch (HRError error) { + blog(LOG_ERROR, "device_stagesurface_create (D3D11): %s " + "(%08lX)", + error.str, error.hr); + LogD3D11ErrorDetails(error, device); + } + + return surf; +} diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index 54070a1..d373be5 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -80,6 +80,7 @@ static inline DXGI_FORMAT ConvertGSTextureFormat(gs_color_format format) case GS_DXT1: return DXGI_FORMAT_BC1_UNORM; case GS_DXT3: return DXGI_FORMAT_BC2_UNORM; case GS_DXT5: return DXGI_FORMAT_BC3_UNORM; + case GS_R8G8: return DXGI_FORMAT_R8G8_UNORM; } return DXGI_FORMAT_UNKNOWN; @@ -90,6 +91,7 @@ static inline gs_color_format ConvertDXGITextureFormat(DXGI_FORMAT format) switch ((unsigned long)format) { case DXGI_FORMAT_A8_UNORM: return GS_A8; case DXGI_FORMAT_R8_UNORM: return GS_R8; + case DXGI_FORMAT_R8G8_UNORM: return GS_R8G8; case DXGI_FORMAT_R8G8B8A8_UNORM: return GS_RGBA; case DXGI_FORMAT_B8G8R8X8_UNORM: return GS_BGRX; case DXGI_FORMAT_B8G8R8A8_UNORM: return GS_BGRA; @@ -267,7 +269,7 @@ struct gs_vertex_buffer : gs_obj { uvBuffers.clear(); } - inline void Rebuild(); + void Rebuild(); gs_vertex_buffer(gs_device_t *device, struct gs_vb_data *data, uint32_t flags); @@ -294,7 +296,7 @@ struct gs_index_buffer : gs_obj { void InitBuffer(); - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() {indexBuffer.Release();} @@ -310,7 +312,7 @@ struct gs_texture : gs_obj { ComPtr shaderRes; D3D11_SHADER_RESOURCE_VIEW_DESC resourceDesc = {}; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline gs_texture(gs_texture_type type, uint32_t levels, gs_color_format format) @@ -344,13 +346,19 @@ struct gs_texture_2d : gs_texture { ComPtr gdiSurface; uint32_t width = 0, height = 0; + uint32_t flags = 0; DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN; bool isRenderTarget = false; bool isGDICompatible = false; bool isDynamic = false; bool isShared = false; bool genMipmaps = false; - uint32_t sharedHandle = 0; + uint32_t sharedHandle = GS_INVALID_HANDLE; + + gs_texture_2d *pairedNV12texture = nullptr; + bool nv12 = false; + bool chroma = false; + bool acquired = false; vector> data; vector srd; @@ -361,9 +369,12 @@ struct gs_texture_2d : gs_texture { void InitResourceView(); void InitRenderTargets(); void BackupTexture(const uint8_t **data); + void GetSharedHandle(IDXGIResource *dxgi_res); void RebuildSharedTextureFallback(); - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); + void RebuildNV12_Y(ID3D11Device *dev); + void RebuildNV12_UV(ID3D11Device *dev); inline void Release() { @@ -382,8 +393,11 @@ struct gs_texture_2d : gs_texture { gs_texture_2d(gs_device_t *device, uint32_t width, uint32_t height, gs_color_format colorFormat, uint32_t levels, const uint8_t **data, uint32_t flags, - gs_texture_type type, bool gdiCompatible, bool shared); + gs_texture_type type, bool gdiCompatible, + bool nv12 = false); + gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12, + uint32_t flags); gs_texture_2d(gs_device_t *device, uint32_t handle); }; @@ -400,7 +414,7 @@ struct gs_zstencil_buffer : gs_obj { void InitBuffer(); - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -427,7 +441,7 @@ struct gs_stage_surface : gs_obj { gs_color_format format; DXGI_FORMAT dxgiFormat; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -436,6 +450,7 @@ struct gs_stage_surface : gs_obj { gs_stage_surface(gs_device_t *device, uint32_t width, uint32_t height, gs_color_format colorFormat); + gs_stage_surface(gs_device_t *device, uint32_t width, uint32_t height); }; struct gs_sampler_state : gs_obj { @@ -443,7 +458,7 @@ struct gs_sampler_state : gs_obj { D3D11_SAMPLER_DESC sd = {}; gs_sampler_info info; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() {state.Release();} @@ -532,7 +547,7 @@ struct gs_vertex_shader : gs_shader { bool hasTangents; uint32_t nTexUnits; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -579,7 +594,7 @@ struct gs_pixel_shader : gs_shader { ComPtr shader; vector> samplers; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -615,7 +630,7 @@ struct gs_swap_chain : gs_obj { void Resize(uint32_t cx, uint32_t cy); void Init(); - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -644,7 +659,7 @@ struct BlendState { srcFactorC (GS_BLEND_SRCALPHA), destFactorC (GS_BLEND_INVSRCALPHA), srcFactorA (GS_BLEND_ONE), - destFactorA (GS_BLEND_ONE), + destFactorA (GS_BLEND_INVSRCALPHA), redEnabled (true), greenEnabled (true), blueEnabled (true), @@ -662,7 +677,7 @@ struct SavedBlendState : BlendState { ComPtr state; D3D11_BLEND_DESC bd; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -719,7 +734,7 @@ struct SavedZStencilState : ZStencilState { ComPtr state; D3D11_DEPTH_STENCIL_DESC dsd; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -754,7 +769,7 @@ struct SavedRasterState : RasterState { ComPtr state; D3D11_RASTERIZER_DESC rd; - inline void Rebuild(ID3D11Device *dev); + void Rebuild(ID3D11Device *dev); inline void Release() { @@ -779,6 +794,7 @@ struct gs_device { ComPtr device; ComPtr context; uint32_t adpIdx = 0; + bool nv12Supported = false; gs_texture_2d *curRenderTarget = nullptr; gs_zstencil_buffer *curZStencilBuffer = nullptr; @@ -845,6 +861,11 @@ struct gs_device { void RebuildDevice(); + bool HasBadNV12Output(); + gs_device(uint32_t adapterIdx); ~gs_device(); }; + +extern "C" EXPORT int device_texture_acquire_sync(gs_texture_t *tex, + uint64_t key, uint32_t ms); diff --git a/libobs-d3d11/d3d11-texture2d.cpp b/libobs-d3d11/d3d11-texture2d.cpp index 9855466..a3fa205 100644 --- a/libobs-d3d11/d3d11-texture2d.cpp +++ b/libobs-d3d11/d3d11-texture2d.cpp @@ -71,6 +71,20 @@ void gs_texture_2d::BackupTexture(const uint8_t **data) } } +void gs_texture_2d::GetSharedHandle(IDXGIResource *dxgi_res) +{ + HANDLE handle; + HRESULT hr; + + hr = dxgi_res->GetSharedHandle(&handle); + if (FAILED(hr)) { + blog(LOG_WARNING, "GetSharedHandle: Failed to " + "get shared handle: %08lX", hr); + } else { + sharedHandle = (uint32_t)(uintptr_t)handle; + } +} + void gs_texture_2d::InitTexture(const uint8_t **data) { HRESULT hr; @@ -80,7 +94,7 @@ void gs_texture_2d::InitTexture(const uint8_t **data) td.Height = height; td.MipLevels = genMipmaps ? 0 : levels; td.ArraySize = type == GS_TEXTURE_CUBE ? 6 : 1; - td.Format = dxgiFormat; + td.Format = nv12 ? DXGI_FORMAT_NV12 : dxgiFormat; td.BindFlags = D3D11_BIND_SHADER_RESOURCE; td.SampleDesc.Count = 1; td.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0; @@ -96,6 +110,11 @@ void gs_texture_2d::InitTexture(const uint8_t **data) if (isGDICompatible) td.MiscFlags |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE; + if ((flags & GS_SHARED_KM_TEX) != 0) + td.MiscFlags |= D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + else if ((flags & GS_SHARED_TEX) != 0) + td.MiscFlags |= D3D11_RESOURCE_MISC_SHARED; + if (data) { BackupTexture(data); InitSRD(srd); @@ -112,6 +131,36 @@ void gs_texture_2d::InitTexture(const uint8_t **data) if (FAILED(hr)) throw HRError("Failed to create GDI surface", hr); } + + if (isShared) { + ComPtr dxgi_res; + + texture->SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM); + + hr = texture->QueryInterface(__uuidof(IDXGIResource), + (void**)&dxgi_res); + if (FAILED(hr)) { + blog(LOG_WARNING, "InitTexture: Failed to query " + "interface: %08lX", hr); + } else { + GetSharedHandle(dxgi_res); + + if (flags & GS_SHARED_KM_TEX) { + ComPtr km; + hr = texture->QueryInterface( + __uuidof(IDXGIKeyedMutex), + (void**)&km); + if (FAILED(hr)) { + throw HRError("Failed to query " + "IDXGIKeyedMutex", + hr); + } + + km->AcquireSync(0, INFINITE); + acquired = true; + } + } + } } void gs_texture_2d::InitResourceView() @@ -123,10 +172,12 @@ void gs_texture_2d::InitResourceView() if (type == GS_TEXTURE_CUBE) { resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - resourceDesc.TextureCube.MipLevels = genMipmaps ? -1 : 1; + resourceDesc.TextureCube.MipLevels = + genMipmaps || !levels ? -1 : levels; } else { resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - resourceDesc.Texture2D.MipLevels = genMipmaps ? -1 : 1; + resourceDesc.Texture2D.MipLevels = + genMipmaps || !levels ? -1 : levels; } hr = device->device->CreateShaderResourceView(texture, &resourceDesc, @@ -139,7 +190,12 @@ void gs_texture_2d::InitRenderTargets() { HRESULT hr; if (type == GS_TEXTURE_2D) { - hr = device->device->CreateRenderTargetView(texture, NULL, + D3D11_RENDER_TARGET_VIEW_DESC rtv; + rtv.Format = dxgiFormat; + rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtv.Texture2D.MipSlice = 0; + + hr = device->device->CreateRenderTargetView(texture, &rtv, renderTarget[0].Assign()); if (FAILED(hr)) throw HRError("Failed to create render target view", @@ -162,20 +218,25 @@ void gs_texture_2d::InitRenderTargets() } } +#define SHARED_FLAGS (GS_SHARED_TEX | GS_SHARED_KM_TEX) + gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width, uint32_t height, gs_color_format colorFormat, uint32_t levels, - const uint8_t **data, uint32_t flags, gs_texture_type type, - bool gdiCompatible, bool shared) + const uint8_t **data, uint32_t flags_, gs_texture_type type, + bool gdiCompatible, bool nv12_) : gs_texture (device, gs_type::gs_texture_2d, type, levels, colorFormat), width (width), height (height), + flags (flags_), dxgiFormat (ConvertGSTextureFormat(format)), - isRenderTarget ((flags & GS_RENDER_TARGET) != 0), + isRenderTarget ((flags_ & GS_RENDER_TARGET) != 0), isGDICompatible (gdiCompatible), - isDynamic ((flags & GS_DYNAMIC) != 0), - isShared (shared), - genMipmaps ((flags & GS_BUILD_MIPMAPS) != 0) + isDynamic ((flags_ & GS_DYNAMIC) != 0), + isShared ((flags_ & SHARED_FLAGS) != 0), + genMipmaps ((flags_ & GS_BUILD_MIPMAPS) != 0), + sharedHandle (GS_INVALID_HANDLE), + nv12 (nv12_) { InitTexture(data); InitResourceView(); @@ -184,6 +245,33 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width, InitRenderTargets(); } +gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12tex, + uint32_t flags_) + : gs_texture (device, gs_type::gs_texture_2d, GS_TEXTURE_2D), + isRenderTarget ((flags_ & GS_RENDER_TARGET) != 0), + isDynamic ((flags_ & GS_DYNAMIC) != 0), + isShared ((flags_ & SHARED_FLAGS) != 0), + genMipmaps ((flags_ & GS_BUILD_MIPMAPS) != 0), + nv12 (true) +{ + texture = nv12tex; + texture->GetDesc(&td); + + this->type = GS_TEXTURE_2D; + this->format = GS_R8G8; + this->flags = flags_; + this->levels = 1; + this->device = device; + this->chroma = true; + this->width = td.Width / 2; + this->height = td.Height / 2; + this->dxgiFormat = DXGI_FORMAT_R8G8_UNORM; + + InitResourceView(); + if (isRenderTarget) + InitRenderTargets(); +} + gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t handle) : gs_texture (device, gs_type::gs_texture_2d, GS_TEXTURE_2D), diff --git a/libobs-opengl/gl-helpers.c b/libobs-opengl/gl-helpers.c index 8a7a757..c6e9012 100644 --- a/libobs-opengl/gl-helpers.c +++ b/libobs-opengl/gl-helpers.c @@ -57,13 +57,11 @@ bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels, return success; } -static bool gl_copy_fbo(struct gs_device *device, - GLuint dst, GLenum dst_target, uint32_t dst_x, uint32_t dst_y, - GLuint src, GLenum src_target, uint32_t src_x, uint32_t src_y, - uint32_t width, uint32_t height, - enum gs_color_format format) +static bool gl_copy_fbo(struct gs_texture *dst, uint32_t dst_x, uint32_t dst_y, + struct gs_texture *src, uint32_t src_x, uint32_t src_y, + uint32_t width, uint32_t height) { - struct fbo_info *fbo = get_fbo(device, width, height, format); + struct fbo_info *fbo = get_fbo(src, width, height); GLint last_fbo; bool success = false; @@ -74,11 +72,11 @@ static bool gl_copy_fbo(struct gs_device *device, return false; if (!gl_bind_framebuffer(GL_READ_FRAMEBUFFER, fbo->fbo)) return false; - if (!gl_bind_texture(dst_target, dst)) + if (!gl_bind_texture(dst->gl_target, dst->texture)) goto fail; glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, - src_target, src, 0); + src->gl_target, src->texture, 0); if (!gl_success("glFrameBufferTexture2D")) goto fail; @@ -86,7 +84,7 @@ static bool gl_copy_fbo(struct gs_device *device, if (!gl_success("glReadBuffer")) goto fail; - glCopyTexSubImage2D(dst_target, 0, dst_x, dst_y, src_x, src_y, + glCopyTexSubImage2D(dst->gl_target, 0, dst_x, dst_y, src_x, src_y, width, height); if (!gl_success("glCopyTexSubImage2D")) goto fail; @@ -94,7 +92,7 @@ static bool gl_copy_fbo(struct gs_device *device, success = true; fail: - if (!gl_bind_texture(dst_target, 0)) + if (!gl_bind_texture(dst->gl_target, 0)) success = false; if (!gl_bind_framebuffer(GL_READ_FRAMEBUFFER, last_fbo)) success = false; @@ -102,29 +100,28 @@ fail: return success; } -bool gl_copy_texture(struct gs_device *device, - GLuint dst, GLenum dst_target, uint32_t dst_x, uint32_t dst_y, - GLuint src, GLenum src_target, uint32_t src_x, uint32_t src_y, - uint32_t width, uint32_t height, enum gs_color_format format) +bool gl_copy_texture(struct gs_device *device, struct gs_texture *dst, + uint32_t dst_x, uint32_t dst_y, struct gs_texture *src, + uint32_t src_x, uint32_t src_y, uint32_t width, + uint32_t height) { bool success = false; if (device->copy_type == COPY_TYPE_ARB) { - glCopyImageSubData(src, src_target, 0, src_x, src_y, 0, - dst, dst_target, 0, dst_x, dst_y, 0, - width, height, 1); + glCopyImageSubData(src->texture, src->gl_target, 0, src_x, + src_y, 0, dst->texture, dst->gl_target, 0, + dst_x, dst_y, 0, width, height, 1); success = gl_success("glCopyImageSubData"); } else if (device->copy_type == COPY_TYPE_NV) { - glCopyImageSubDataNV(src, src_target, 0, src_x, src_y, 0, - dst, dst_target, 0, dst_x, dst_y, 0, - width, height, 1); + glCopyImageSubDataNV(src->texture, src->gl_target, 0, src_x, + src_y, 0, dst->texture, dst->gl_target, 0, + dst_x, dst_y, 0, width, height, 1); success = gl_success("glCopyImageSubDataNV"); } else if (device->copy_type == COPY_TYPE_FBO_BLIT) { - success = gl_copy_fbo(device, dst, dst_target, dst_x, dst_y, - src, src_target, src_x, src_y, - width, height, format); + success = gl_copy_fbo(dst, dst_x, dst_y, src, src_x, src_y, + width, height); if (!success) blog(LOG_ERROR, "gl_copy_texture failed"); } diff --git a/libobs-opengl/gl-helpers.h b/libobs-opengl/gl-helpers.h index f1f9122..fe9959a 100644 --- a/libobs-opengl/gl-helpers.h +++ b/libobs-opengl/gl-helpers.h @@ -148,11 +148,10 @@ extern bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels, uint32_t width, uint32_t height, uint32_t size, const uint8_t ***p_data); -extern bool gl_copy_texture(struct gs_device *device, - GLuint dst, GLenum dst_target, uint32_t dst_x, uint32_t dst_y, - GLuint src, GLenum src_target, uint32_t src_x, uint32_t src_y, - uint32_t width, uint32_t height, - enum gs_color_format format); +extern bool gl_copy_texture(struct gs_device *device, struct gs_texture *dst, + uint32_t dst_x, uint32_t dst_y, struct gs_texture *src, + uint32_t src_x, uint32_t src_y, uint32_t width, + uint32_t height); extern bool gl_create_buffer(GLenum target, GLuint *buffer, GLsizeiptr size, const GLvoid *data, GLenum usage); diff --git a/libobs-opengl/gl-indexbuffer.c b/libobs-opengl/gl-indexbuffer.c index 187835e..aef280f 100644 --- a/libobs-opengl/gl-indexbuffer.c +++ b/libobs-opengl/gl-indexbuffer.c @@ -38,7 +38,7 @@ gs_indexbuffer_t *device_indexbuffer_create(gs_device_t *device, uint32_t flags) { struct gs_index_buffer *ib = bzalloc(sizeof(struct gs_index_buffer)); - size_t width = type == GS_UNSIGNED_LONG ? sizeof(long) : sizeof(short); + size_t width = type == GS_UNSIGNED_LONG ? 4 : 2; ib->device = device; ib->data = indices; diff --git a/libobs-opengl/gl-shader.c b/libobs-opengl/gl-shader.c index 1ffcce4..cab4d7f 100644 --- a/libobs-opengl/gl-shader.c +++ b/libobs-opengl/gl-shader.c @@ -220,8 +220,20 @@ static bool gl_shader_init(struct gs_shader *shader, if (!gl_success("glGetShaderiv")) return false; - if (!compiled) + if (!compiled) { + GLint infoLength = 0; + glGetShaderiv(shader->obj, GL_INFO_LOG_LENGTH, &infoLength); + + char *infoLog = malloc(sizeof(char) * infoLength); + + GLsizei returnedLength = 0; + glGetShaderInfoLog(shader->obj, infoLength, &returnedLength, infoLog); + blog(LOG_ERROR, "Error compiling shader:\n%s\n", infoLog); + + free(infoLog); + success = false; + } gl_get_shader_info(shader->obj, file, error_string); @@ -455,6 +467,24 @@ static void program_set_param_data(struct gs_program *program, gl_success("glUniform1iv"); } + } else if (pp->param->type == GS_SHADER_PARAM_INT2) { + if (validate_param(pp, sizeof(int) * 2)) { + glUniform2iv(pp->obj, 1, (int*)array); + gl_success("glUniform2iv"); + } + + } else if (pp->param->type == GS_SHADER_PARAM_INT3) { + if (validate_param(pp, sizeof(int) * 3)) { + glUniform3iv(pp->obj, 1, (int*)array); + gl_success("glUniform3iv"); + } + + } else if (pp->param->type == GS_SHADER_PARAM_INT4) { + if (validate_param(pp, sizeof(int) * 4)) { + glUniform4iv(pp->obj, 1, (int*)array); + gl_success("glUniform4iv"); + } + } else if (pp->param->type == GS_SHADER_PARAM_FLOAT) { if (validate_param(pp, sizeof(float))) { glUniform1fv(pp->obj, 1, (float*)array); @@ -698,6 +728,9 @@ void gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size) case GS_SHADER_PARAM_FLOAT: expected_size = sizeof(float); break; case GS_SHADER_PARAM_BOOL: case GS_SHADER_PARAM_INT: expected_size = sizeof(int); break; + case GS_SHADER_PARAM_INT2: expected_size = sizeof(int) * 2; break; + case GS_SHADER_PARAM_INT3: expected_size = sizeof(int) * 3; break; + case GS_SHADER_PARAM_INT4: expected_size = sizeof(int) * 4; break; case GS_SHADER_PARAM_VEC2: expected_size = sizeof(float)*2; break; case GS_SHADER_PARAM_VEC3: expected_size = sizeof(float)*3; break; case GS_SHADER_PARAM_VEC4: expected_size = sizeof(float)*4; break; diff --git a/libobs-opengl/gl-stagesurf.c b/libobs-opengl/gl-stagesurf.c index aa1637c..35dd212 100644 --- a/libobs-opengl/gl-stagesurf.c +++ b/libobs-opengl/gl-stagesurf.c @@ -124,7 +124,7 @@ void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, if (!gl_bind_buffer(GL_PIXEL_PACK_BUFFER, dst->pack_buffer)) goto failed; - fbo = get_fbo(device, dst->width, dst->height, dst->format); + fbo = get_fbo(src, dst->width, dst->height); if (!gl_get_integer_v(GL_READ_FRAMEBUFFER_BINDING, &last_fbo)) goto failed_unbind_buffer; @@ -152,6 +152,8 @@ failed_unbind_buffer: failed: if (!success) blog(LOG_ERROR, "device_stage_texture (GL) failed"); + + UNUSED_PARAMETER(device); } #else diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index 063e1fd..d91a38d 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -259,16 +259,10 @@ fail: void device_destroy(gs_device_t *device) { if (device) { - size_t i; - - for (i = 0; i < device->fbos.num; i++) - fbo_info_destroy(device->fbos.array[i]); - while (device->first_program) gs_program_destroy(device->first_program); da_free(device->proj_stack); - da_free(device->fbos); gl_platform_destroy(device->plat); bfree(device); } @@ -658,46 +652,37 @@ static bool get_tex_dimensions(gs_texture_t *tex, uint32_t *width, * This automatically manages FBOs so that render targets are always given * an FBO that matches their width/height/format to maximize optimization */ -struct fbo_info *get_fbo(struct gs_device *device, - uint32_t width, uint32_t height, enum gs_color_format format) +struct fbo_info *get_fbo(gs_texture_t *tex, uint32_t width, uint32_t height) { - size_t i; + if (tex->fbo && tex->fbo->width == width && + tex->fbo->height == height && + tex->fbo->format == tex->format) + return tex->fbo; + GLuint fbo; - struct fbo_info *ptr; - - for (i = 0; i < device->fbos.num; i++) { - ptr = device->fbos.array[i]; - - if (ptr->width == width && ptr->height == height && - ptr->format == format) - return ptr; - } - glGenFramebuffers(1, &fbo); if (!gl_success("glGenFramebuffers")) return NULL; - ptr = bmalloc(sizeof(struct fbo_info)); - ptr->fbo = fbo; - ptr->width = width; - ptr->height = height; - ptr->format = format; - ptr->cur_render_target = NULL; - ptr->cur_render_side = 0; - ptr->cur_zstencil_buffer = NULL; + tex->fbo = bmalloc(sizeof(struct fbo_info)); + tex->fbo->fbo = fbo; + tex->fbo->width = width; + tex->fbo->height = height; + tex->fbo->format = tex->format; + tex->fbo->cur_render_target = NULL; + tex->fbo->cur_render_side = 0; + tex->fbo->cur_zstencil_buffer = NULL; - da_push_back(device->fbos, &ptr); - return ptr; + return tex->fbo; } -static inline struct fbo_info *get_fbo_by_tex(struct gs_device *device, - gs_texture_t *tex) +static inline struct fbo_info *get_fbo_by_tex(gs_texture_t *tex) { uint32_t width, height; if (!get_tex_dimensions(tex, &width, &height)) return NULL; - return get_fbo(device, width, height, tex->format); + return get_fbo(tex, width, height); } static bool set_current_fbo(gs_device_t *device, struct fbo_info *fbo) @@ -783,7 +768,7 @@ static bool set_target(gs_device_t *device, gs_texture_t *tex, int side, if (!tex) return set_current_fbo(device, NULL); - fbo = get_fbo_by_tex(device, tex); + fbo = get_fbo_by_tex(tex); if (!fbo) return false; @@ -885,9 +870,8 @@ void device_copy_texture_region(gs_device_t *device, goto fail; } - if (!gl_copy_texture(device, dst->texture, dst->gl_target, dst_x, dst_y, - src->texture, src->gl_target, src_x, src_y, - nw, nh, src->format)) + if (!gl_copy_texture(device, dst, dst_x, dst_y, src, src_x, src_y, nw, + nh)) goto fail; return; @@ -1357,6 +1341,22 @@ void device_projection_pop(gs_device_t *device) da_pop_back(device->proj_stack); } +void device_debug_marker_begin(gs_device_t *device, + const char *markername, const float color[4]) +{ + UNUSED_PARAMETER(device); + UNUSED_PARAMETER(color); + + glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 0, -1, markername); +} + +void device_debug_marker_end(gs_device_t *device) +{ + UNUSED_PARAMETER(device); + + glPopDebugGroupKHR(); +} + void gs_swapchain_destroy(gs_swapchain_t *swapchain) { if (!swapchain) diff --git a/libobs-opengl/gl-subsystem.h b/libobs-opengl/gl-subsystem.h index 4467876..d5b89c9 100644 --- a/libobs-opengl/gl-subsystem.h +++ b/libobs-opengl/gl-subsystem.h @@ -36,7 +36,7 @@ enum copy_type { COPY_TYPE_FBO_BLIT }; -static inline GLint convert_gs_format(enum gs_color_format format) +static inline GLenum convert_gs_format(enum gs_color_format format) { switch (format) { case GS_A8: return GL_RED; @@ -51,6 +51,7 @@ static inline GLint convert_gs_format(enum gs_color_format format) case GS_RGBA32F: return GL_RGBA; case GS_RG16F: return GL_RG; case GS_RG32F: return GL_RG; + case GS_R8G8: return GL_RG; case GS_R16F: return GL_RED; case GS_R32F: return GL_RED; case GS_DXT1: return GL_RGB; @@ -62,7 +63,7 @@ static inline GLint convert_gs_format(enum gs_color_format format) return 0; } -static inline GLint convert_gs_internal_format(enum gs_color_format format) +static inline GLenum convert_gs_internal_format(enum gs_color_format format) { switch (format) { case GS_A8: return GL_R8; /* NOTE: use GL_TEXTURE_SWIZZLE_x */ @@ -77,6 +78,7 @@ static inline GLint convert_gs_internal_format(enum gs_color_format format) case GS_RGBA32F: return GL_RGBA32F; case GS_RG16F: return GL_RG16F; case GS_RG32F: return GL_RG32F; + case GS_R8G8: return GL_R16; case GS_R16F: return GL_R16F; case GS_R32F: return GL_R32F; case GS_DXT1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; @@ -103,6 +105,7 @@ static inline GLenum get_gl_format_type(enum gs_color_format format) case GS_RGBA32F: return GL_FLOAT; case GS_RG16F: return GL_UNSIGNED_SHORT; case GS_RG32F: return GL_FLOAT; + case GS_R8G8: return GL_UNSIGNED_SHORT; case GS_R16F: return GL_UNSIGNED_SHORT; case GS_R32F: return GL_FLOAT; case GS_DXT1: return GL_UNSIGNED_BYTE; @@ -399,7 +402,7 @@ struct gs_texture { enum gs_color_format format; GLenum gl_format; GLenum gl_target; - GLint gl_internal_format; + GLenum gl_internal_format; GLenum gl_type; GLuint texture; uint32_t levels; @@ -409,6 +412,7 @@ struct gs_texture { bool gen_mipmaps; gs_samplerstate_t *cur_sampler; + struct fbo_info *fbo; }; struct gs_texture_2d { @@ -501,12 +505,11 @@ struct gs_device { DARRAY(struct matrix4) proj_stack; - DARRAY(struct fbo_info*) fbos; struct fbo_info *cur_fbo; }; -extern struct fbo_info *get_fbo(struct gs_device *device, - uint32_t width, uint32_t height, enum gs_color_format format); +extern struct fbo_info *get_fbo(gs_texture_t *tex, uint32_t width, + uint32_t height); extern void gl_update(gs_device_t *device); diff --git a/libobs-opengl/gl-texture2d.c b/libobs-opengl/gl-texture2d.c index 1ad6c92..da18b9f 100644 --- a/libobs-opengl/gl-texture2d.c +++ b/libobs-opengl/gl-texture2d.c @@ -138,6 +138,9 @@ void gs_texture_destroy(gs_texture_t *tex) if (tex->texture) gl_delete_textures(1, &tex->texture); + if (tex->fbo) + fbo_info_destroy(tex->fbo); + bfree(tex); } diff --git a/libobs-opengl/gl-texturecube.c b/libobs-opengl/gl-texturecube.c index f15d6b2..6086675 100644 --- a/libobs-opengl/gl-texturecube.c +++ b/libobs-opengl/gl-texturecube.c @@ -92,10 +92,11 @@ void gs_cubetexture_destroy(gs_texture_t *tex) if (!tex) return; - if (tex->texture) { - glDeleteTextures(1, &tex->texture); - gl_success("glDeleteTextures"); - } + if (tex->texture) + gl_delete_textures(1, &tex->texture); + + if (tex->fbo) + fbo_info_destroy(tex->fbo); bfree(tex); } diff --git a/libobs-opengl/gl-windows.c b/libobs-opengl/gl-windows.c index a266930..c6c8499 100644 --- a/libobs-opengl/gl-windows.c +++ b/libobs-opengl/gl-windows.c @@ -532,7 +532,7 @@ void device_enter_context(gs_device_t *device) hdc = device->cur_swap->wi->hdc; if (!wgl_make_current(hdc, device->plat->hrc)) - blog(LOG_ERROR, "device_load_swapchain (GL) failed"); + blog(LOG_ERROR, "device_enter_context (GL) failed"); } void device_leave_context(gs_device_t *device) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index bddfa5c..fed3e69 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -116,7 +116,8 @@ elseif(APPLE) util/platform-nix.c util/platform-cocoa.m) set(libobs_PLATFORM_HEADERS - util/threading-posix.h) + util/threading-posix.h + util/apple/cfstring-utils.h) set(libobs_audio_monitoring_SOURCES audio-monitoring/osx/coreaudio-enum-devices.c audio-monitoring/osx/coreaudio-output.c @@ -383,6 +384,7 @@ set(libobs_libobs_SOURCES obs-view.c obs-scene.c obs-audio.c + obs-video-gpu-encode.c obs-video.c) set(libobs_libobs_HEADERS ${libobs_PLATFORM_HEADERS} @@ -446,6 +448,14 @@ if(BUILD_CAPTIONS) endif() add_library(libobs SHARED ${libobs_SOURCES} ${libobs_HEADERS}) +if(UNIX AND NOT APPLE) + set(DEST_DIR "${CMAKE_INSTALL_PREFIX}") + foreach(LIB "obs" "rt") + set(PRIVATE_LIBS "${PRIVATE_LIBS} -l${LIB}") + endforeach() + CONFIGURE_FILE("libobs.pc.in" "libobs.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libobs.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") +endif() set_target_properties(libobs PROPERTIES OUTPUT_NAME obs @@ -455,7 +465,12 @@ target_compile_definitions(libobs PUBLIC HAVE_OBSCONFIG_H) -if(NOT MSVC) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le") + target_compile_options(libobs + PUBLIC + -mvsx) + add_compile_definitions(NO_WARN_X86_INTRINSICS) +elseif(NOT MSVC) target_compile_options(libobs PUBLIC -mmmx diff --git a/libobs/audio-monitoring/osx/coreaudio-enum-devices.c b/libobs/audio-monitoring/osx/coreaudio-enum-devices.c index 9f06cb6..8e6ea9e 100644 --- a/libobs/audio-monitoring/osx/coreaudio-enum-devices.c +++ b/libobs/audio-monitoring/osx/coreaudio-enum-devices.c @@ -3,23 +3,18 @@ #include "../../obs-internal.h" #include "../../util/dstr.h" +#include "../../util/apple/cfstring-utils.h" #include "mac-helpers.h" -static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size) -{ - if (!ref) return false; - return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8); -} - static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, void *data, AudioDeviceID id, bool allow_inputs) { UInt32 size = 0; CFStringRef cf_name = NULL; CFStringRef cf_uid = NULL; - char name[1024]; - char uid[1024]; + char *name = NULL; + char *uid = NULL; OSStatus stat; bool cont = true; @@ -46,12 +41,14 @@ static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, if (!success(stat, "get audio device name")) goto fail; - if (!cf_to_cstr(cf_name, name, sizeof(name))) { + name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8); + if (!name) { blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__); goto fail; } - if (!cf_to_cstr(cf_uid, uid, sizeof(uid))) { + uid = cfstr_copy_cstr(cf_uid, kCFStringEncodingUTF8); + if (!uid) { blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__); goto fail; } @@ -59,6 +56,8 @@ static bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, cont = cb(data, name, uid); fail: + bfree(name); + bfree(uid); if (cf_name) CFRelease(cf_name); if (cf_uid) diff --git a/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c b/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c index fc41d7a..4bbbc9b 100644 --- a/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c +++ b/libobs/audio-monitoring/pulse/pulseaudio-wrapper.c @@ -307,14 +307,15 @@ int_fast32_t pulseaudio_connect_playback(pa_stream *s, const char *name, return -1; size_t dev_len = strlen(name) - 8; - char device[dev_len]; + char *device = bzalloc(dev_len + 1); memcpy(device, name, dev_len); - device[dev_len] = '\0'; pulseaudio_lock(); int_fast32_t ret = pa_stream_connect_playback(s, device, attr, flags, NULL, NULL); pulseaudio_unlock(); + + bfree(device); return ret; } diff --git a/libobs/audio-monitoring/win32/wasapi-output.h b/libobs/audio-monitoring/win32/wasapi-output.h index c3523b5..8c42868 100644 --- a/libobs/audio-monitoring/win32/wasapi-output.h +++ b/libobs/audio-monitoring/win32/wasapi-output.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include diff --git a/libobs/data/area.effect b/libobs/data/area.effect new file mode 100644 index 0000000..c9369f8 --- /dev/null +++ b/libobs/data/area.effect @@ -0,0 +1,64 @@ +uniform float4x4 ViewProj; +uniform float2 base_dimension_i; +uniform texture2d image; + +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertInOut VSDefault(VertInOut vert_in) +{ + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = vert_in.uv; + return vert_out; +} + +float4 PSDrawAreaRGBA(VertInOut vert_in) : TARGET +{ + float4 totalcolor = float4(0.0, 0.0, 0.0, 0.0); + + float2 uv = vert_in.uv; + float2 uvdelta = float2(ddx(uv.x), ddy(uv.y)); + + // Handle potential OpenGL flip. + uvdelta.y = abs(uvdelta.y); + + float2 uvhalfdelta = 0.5 * uvdelta; + float2 uvmin = uv - uvhalfdelta; + float2 uvmax = uv + uvhalfdelta; + + int2 loadindexmin = int2(uvmin / base_dimension_i); + int2 loadindexmax = int2(uvmax / base_dimension_i); + + float2 targetpos = uv / uvdelta; + float2 targetposmin = targetpos - 0.5; + float2 targetposmax = targetpos + 0.5; + float2 scale = base_dimension_i / uvdelta; + for (int loadindexy = loadindexmin.y; loadindexy <= loadindexmax.y; ++loadindexy) + { + for (int loadindexx = loadindexmin.x; loadindexx <= loadindexmax.x; ++loadindexx) + { + int2 loadindex = int2(loadindexx, loadindexy); + float2 potentialtargetmin = float2(loadindex) * scale; + float2 potentialtargetmax = potentialtargetmin + scale; + float2 targetmin = max(potentialtargetmin, targetposmin); + float2 targetmax = min(potentialtargetmax, targetposmax); + float area = (targetmax.x - targetmin.x) * (targetmax.y - targetmin.y); + float4 sample = image.Load(int3(loadindex, 0)); + totalcolor += area * sample; + } + } + + return totalcolor; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawAreaRGBA(vert_in); + } +} diff --git a/libobs/data/bicubic_scale.effect b/libobs/data/bicubic_scale.effect index 67ebb89..0f55292 100644 --- a/libobs/data/bicubic_scale.effect +++ b/libobs/data/bicubic_scale.effect @@ -7,8 +7,6 @@ uniform float4x4 ViewProj; uniform texture2d image; uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; uniform float2 base_dimension_i; uniform float undistort_factor = 1.0; @@ -132,13 +130,19 @@ float4 PSDrawBicubicRGBA(VertData v_in, bool undistort) : TARGET return DrawBicubic(v_in, undistort); } -float4 PSDrawBicubicMatrix(VertData v_in) : TARGET +float4 PSDrawBicubicRGBADivide(VertData v_in) : TARGET { float4 rgba = DrawBicubic(v_in, false); - float4 yuv; + float alpha = rgba.a; + float multiplier = (alpha > 0.0) ? (1.0 / alpha) : 0.0; + return float4(rgba.rgb * multiplier, alpha); +} - yuv.xyz = clamp(rgba.xyz, color_range_min, color_range_max); - return saturate(mul(float4(yuv.xyz, 1.0), color_matrix)); +float4 PSDrawBicubicMatrix(VertData v_in) : TARGET +{ + float3 rgb = DrawBicubic(v_in, false).rgb; + float3 yuv = mul(float4(saturate(rgb), 1.0), color_matrix).xyz; + return float4(yuv, 1.0); } technique Draw @@ -150,6 +154,15 @@ technique Draw } } +technique DrawAlphaDivide +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSDrawBicubicRGBADivide(v_in); + } +} + technique DrawUndistort { pass diff --git a/libobs/data/bilinear_lowres_scale.effect b/libobs/data/bilinear_lowres_scale.effect index bb158d7..e887072 100644 --- a/libobs/data/bilinear_lowres_scale.effect +++ b/libobs/data/bilinear_lowres_scale.effect @@ -6,8 +6,6 @@ uniform float4x4 ViewProj; uniform texture2d image; uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; uniform float2 base_dimension_i; sampler_state textureSampler { @@ -56,12 +54,19 @@ float4 PSDrawLowresBilinearRGBA(VertData v_in) : TARGET return DrawLowresBilinear(v_in); } +float4 PSDrawLowresBilinearRGBADivide(VertData v_in) : TARGET +{ + float4 rgba = DrawLowresBilinear(v_in); + float alpha = rgba.a; + float multiplier = (alpha > 0.0) ? (1.0 / alpha) : 0.0; + return float4(rgba.rgb * multiplier, alpha); +} + float4 PSDrawLowresBilinearMatrix(VertData v_in) : TARGET { - float4 yuv = DrawLowresBilinear(v_in); - - yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max); - return saturate(mul(float4(yuv.xyz, 1.0), color_matrix)); + float3 rgb = DrawLowresBilinear(v_in).rgb; + float3 yuv = mul(float4(saturate(rgb), 1.0), color_matrix).xyz; + return float4(yuv, 1.0); } technique Draw @@ -73,6 +78,15 @@ technique Draw } } +technique DrawAlphaDivide +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSDrawLowresBilinearRGBADivide(v_in); + } +} + technique DrawMatrix { pass diff --git a/libobs/data/default.effect b/libobs/data/default.effect index 37e0110..54e07fa 100644 --- a/libobs/data/default.effect +++ b/libobs/data/default.effect @@ -1,7 +1,5 @@ uniform float4x4 ViewProj; uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; uniform texture2d image; sampler_state def_sampler { @@ -28,11 +26,19 @@ float4 PSDrawBare(VertInOut vert_in) : TARGET return image.Sample(def_sampler, vert_in.uv); } +float4 PSDrawAlphaDivide(VertInOut vert_in) : TARGET +{ + float4 rgba = image.Sample(def_sampler, vert_in.uv); + float alpha = rgba.a; + float multiplier = (alpha > 0.0) ? (1.0 / alpha) : 0.0; + return float4(rgba.rgb * multiplier, alpha); +} + float4 PSDrawMatrix(VertInOut vert_in) : TARGET { - float4 yuv = image.Sample(def_sampler, vert_in.uv); - yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max); - return saturate(mul(float4(yuv.xyz, 1.0), color_matrix)); + float3 rgb = image.Sample(def_sampler, vert_in.uv).rgb; + float3 yuv = mul(float4(rgb, 1.0), color_matrix).xyz; + return float4(yuv, 1.0); } technique Draw @@ -44,6 +50,15 @@ technique Draw } } +technique DrawAlphaDivide +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawAlphaDivide(vert_in); + } +} + technique DrawMatrix { pass diff --git a/libobs/data/deinterlace_base.effect b/libobs/data/deinterlace_base.effect index 8755da7..da4a138 100644 --- a/libobs/data/deinterlace_base.effect +++ b/libobs/data/deinterlace_base.effect @@ -18,9 +18,6 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; uniform texture2d previous_image; uniform float2 dimensions; @@ -267,7 +264,7 @@ VertData VSDefault(VertData v_in) return vert_out; } -#define TECHNIQUE(rgba_ps, matrix_ps) \ +#define TECHNIQUE(rgba_ps) \ technique Draw \ { \ pass \ @@ -275,19 +272,4 @@ technique Draw \ vertex_shader = VSDefault(v_in); \ pixel_shader = rgba_ps(v_in); \ } \ -} \ -float4 matrix_ps(VertData v_in) : TARGET \ -{ \ - float4 yuv = rgba_ps(v_in); \ - yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max); \ - return saturate(mul(float4(yuv.xyz, 1.0), color_matrix)); \ -} \ -\ -technique DrawMatrix \ -{ \ - pass \ - { \ - vertex_shader = VSDefault(v_in); \ - pixel_shader = matrix_ps(v_in); \ - } \ } diff --git a/libobs/data/deinterlace_blend.effect b/libobs/data/deinterlace_blend.effect index 7def8e2..ab12376 100644 --- a/libobs/data/deinterlace_blend.effect +++ b/libobs/data/deinterlace_blend.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE( PSBlendRGBA, PSBlendMatrix); +TECHNIQUE(PSBlendRGBA); diff --git a/libobs/data/deinterlace_blend_2x.effect b/libobs/data/deinterlace_blend_2x.effect index 33c102c..392e4fd 100644 --- a/libobs/data/deinterlace_blend_2x.effect +++ b/libobs/data/deinterlace_blend_2x.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE(PSBlendRGBA_2x, PSBlendMatrix_2x); +TECHNIQUE(PSBlendRGBA_2x); diff --git a/libobs/data/deinterlace_discard.effect b/libobs/data/deinterlace_discard.effect index e1ce6e1..20dc6ce 100644 --- a/libobs/data/deinterlace_discard.effect +++ b/libobs/data/deinterlace_discard.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE(PSDiscardRGBA, PSDiscardMatrix); +TECHNIQUE(PSDiscardRGBA); diff --git a/libobs/data/deinterlace_discard_2x.effect b/libobs/data/deinterlace_discard_2x.effect index fe6eb9e..66299c2 100644 --- a/libobs/data/deinterlace_discard_2x.effect +++ b/libobs/data/deinterlace_discard_2x.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE(PSDiscardRGBA_2x, PSDiscardMatrix_2x); +TECHNIQUE(PSDiscardRGBA_2x); diff --git a/libobs/data/deinterlace_linear.effect b/libobs/data/deinterlace_linear.effect index 19aa513..1291bf0 100644 --- a/libobs/data/deinterlace_linear.effect +++ b/libobs/data/deinterlace_linear.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE(PSLinearRGBA, PSLinearMatrix); +TECHNIQUE(PSLinearRGBA); diff --git a/libobs/data/deinterlace_linear_2x.effect b/libobs/data/deinterlace_linear_2x.effect index 69224ae..989c8ca 100644 --- a/libobs/data/deinterlace_linear_2x.effect +++ b/libobs/data/deinterlace_linear_2x.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE(PSLinearRGBA_2x, PSLinearxMatrixA_2x); +TECHNIQUE(PSLinearRGBA_2x); diff --git a/libobs/data/deinterlace_yadif.effect b/libobs/data/deinterlace_yadif.effect index e22fe06..0a064cc 100644 --- a/libobs/data/deinterlace_yadif.effect +++ b/libobs/data/deinterlace_yadif.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE(PSYadifMode0RGBA, PSYadifMode0Matrix); +TECHNIQUE(PSYadifMode0RGBA); diff --git a/libobs/data/deinterlace_yadif_2x.effect b/libobs/data/deinterlace_yadif_2x.effect index 6c1d329..b5dfe72 100644 --- a/libobs/data/deinterlace_yadif_2x.effect +++ b/libobs/data/deinterlace_yadif_2x.effect @@ -18,4 +18,4 @@ #include "deinterlace_base.effect" -TECHNIQUE(PSYadifMode0RGBA_2x, PSYadifMode0Matrix_2x); +TECHNIQUE(PSYadifMode0RGBA_2x); diff --git a/libobs/data/format_conversion.effect b/libobs/data/format_conversion.effect index edf1a9e..b3d274e 100644 --- a/libobs/data/format_conversion.effect +++ b/libobs/data/format_conversion.effect @@ -42,6 +42,10 @@ uniform int int_input_width; uniform int int_u_plane_offset; uniform int int_v_plane_offset; +uniform float4x4 color_matrix; +uniform float3 color_range_min = {0.0, 0.0, 0.0}; +uniform float3 color_range_max = {1.0, 1.0, 1.0}; + uniform texture2d image; sampler_state def_sampler { @@ -126,6 +130,16 @@ float4 PSNV12(VertInOut vert_in) : TARGET } } +float PSNV12_Y(VertInOut vert_in) : TARGET +{ + return image.Sample(def_sampler, vert_in.uv.xy).y; +} + +float2 PSNV12_UV(VertInOut vert_in) : TARGET +{ + return image.Sample(def_sampler, vert_in.uv.xy).xz; +} + float4 PSPlanar420(VertInOut vert_in) : TARGET { float v_mul = floor(vert_in.uv.y * input_height); @@ -273,8 +287,10 @@ float4 PSPacked422_Reverse(VertInOut vert_in, int u_pos, int v_pos, x += input_width_i_d2; float4 texel = image.Sample(def_sampler, float2(x, y)); - return float4(odd > 0.5 ? texel[y1_pos] : texel[y0_pos], - texel[u_pos], texel[v_pos], 1.0); + float3 yuv = float3(odd > 0.5 ? texel[y1_pos] : texel[y0_pos], + texel[u_pos], texel[v_pos]); + yuv = clamp(yuv, color_range_min, color_range_max); + return saturate(mul(float4(yuv, 1.0), color_matrix)); } float4 PSPlanar420_Reverse(VertInOut vert_in) : TARGET @@ -287,12 +303,32 @@ float4 PSPlanar420_Reverse(VertInOut vert_in) : TARGET int chroma1 = int_u_plane_offset + chroma_offset; int chroma2 = int_v_plane_offset + chroma_offset; - return float4( + float3 yuv = float3( GetIntOffsetColor(lum_offset), GetIntOffsetColor(chroma1), - GetIntOffsetColor(chroma2), - 1.0 + GetIntOffsetColor(chroma2) ); + yuv = clamp(yuv, color_range_min, color_range_max); + return saturate(mul(float4(yuv, 1.0), color_matrix)); +} + +float4 PSPlanar444_Reverse(VertInOut vert_in) : TARGET +{ + int x = int(vert_in.uv.x * width + PRECISION_OFFSET); + int y = int(vert_in.uv.y * height + PRECISION_OFFSET); + + int lum_offset = y * int_width + x; + int chroma_offset = y * int_width + x; + int chroma1 = int_u_plane_offset + chroma_offset; + int chroma2 = int_v_plane_offset + chroma_offset; + + float3 yuv = float3( + GetIntOffsetColor(lum_offset), + GetIntOffsetColor(chroma1), + GetIntOffsetColor(chroma2) + ); + yuv = clamp(yuv, color_range_min, color_range_max); + return saturate(mul(float4(yuv, 1.0), color_matrix)); } float4 PSNV12_Reverse(VertInOut vert_in) : TARGET @@ -304,12 +340,42 @@ float4 PSNV12_Reverse(VertInOut vert_in) : TARGET int chroma_offset = (y / 2) * (int_width / 2) + x / 2; int chroma = int_u_plane_offset + chroma_offset * 2; - return float4( + float3 yuv = float3( GetIntOffsetColor(lum_offset), GetIntOffsetColor(chroma), - GetIntOffsetColor(chroma + 1), - 1.0 + GetIntOffsetColor(chroma + 1) ); + yuv = clamp(yuv, color_range_min, color_range_max); + return saturate(mul(float4(yuv, 1.0), color_matrix)); +} + +float4 PSY800_Limited(VertInOut vert_in) : TARGET +{ + int x = int(vert_in.uv.x * width + PRECISION_OFFSET); + int y = int(vert_in.uv.y * height + PRECISION_OFFSET); + + float limited = image.Load(int3(x, y, 0)).x; + float full = saturate((limited - (16.0 / 255.0)) * (255.0 / 219.0)); + return float4(full, full, full, 1.0); +} + +float4 PSY800_Full(VertInOut vert_in) : TARGET +{ + int x = int(vert_in.uv.x * width + PRECISION_OFFSET); + int y = int(vert_in.uv.y * height + PRECISION_OFFSET); + + float3 full = image.Load(int3(x, y, 0)).xxx; + return float4(full, 1.0); +} + +float4 PSRGB_Limited(VertInOut vert_in) : TARGET +{ + int x = int(vert_in.uv.x * width + PRECISION_OFFSET); + int y = int(vert_in.uv.y * height + PRECISION_OFFSET); + + float4 rgba = image.Load(int3(x, y, 0)); + rgba.rgb = saturate((rgba.rgb - (16.0 / 255.0)) * (255.0 / 219.0)); + return rgba; } technique Planar420 @@ -339,6 +405,24 @@ technique NV12 } } +technique NV12_Y +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSNV12_Y(vert_in); + } +} + +technique NV12_UV +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSNV12_UV(vert_in); + } +} + technique UYVY_Reverse { pass @@ -375,6 +459,15 @@ technique I420_Reverse } } +technique I444_Reverse +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSPlanar444_Reverse(vert_in); + } +} + technique NV12_Reverse { pass @@ -383,3 +476,30 @@ technique NV12_Reverse pixel_shader = PSNV12_Reverse(vert_in); } } + +technique Y800_Limited +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSY800_Limited(vert_in); + } +} + +technique Y800_Full +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSY800_Full(vert_in); + } +} + +technique RGB_Limited +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSRGB_Limited(vert_in); + } +} diff --git a/libobs/data/lanczos_scale.effect b/libobs/data/lanczos_scale.effect index 4fc6453..061acc4 100644 --- a/libobs/data/lanczos_scale.effect +++ b/libobs/data/lanczos_scale.effect @@ -7,8 +7,6 @@ uniform float4x4 ViewProj; uniform texture2d image; uniform float4x4 color_matrix; -uniform float3 color_range_min = {0.0, 0.0, 0.0}; -uniform float3 color_range_max = {1.0, 1.0, 1.0}; uniform float2 base_dimension_i; uniform float undistort_factor = 1.0; @@ -140,13 +138,19 @@ float4 PSDrawLanczosRGBA(FragData v_in, bool undistort) : TARGET return DrawLanczos(v_in, undistort); } -float4 PSDrawLanczosMatrix(FragData v_in) : TARGET +float4 PSDrawLanczosRGBADivide(FragData v_in) : TARGET { float4 rgba = DrawLanczos(v_in, false); - float4 yuv; + float alpha = rgba.a; + float multiplier = (alpha > 0.0) ? (1.0 / alpha) : 0.0; + return float4(rgba.rgb * multiplier, alpha); +} - yuv.xyz = clamp(rgba.xyz, color_range_min, color_range_max); - return saturate(mul(float4(yuv.xyz, 1.0), color_matrix)); +float4 PSDrawLanczosMatrix(FragData v_in) : TARGET +{ + float3 rgb = DrawLanczos(v_in, false).rgb; + float3 yuv = mul(float4(saturate(rgb), 1.0), color_matrix).xyz; + return float4(yuv, 1.0); } technique Draw @@ -158,6 +162,15 @@ technique Draw } } +technique DrawAlphaDivide +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSDrawLanczosRGBADivide(v_in); + } +} + technique DrawUndistort { pass diff --git a/libobs/data/repeat.effect b/libobs/data/repeat.effect new file mode 100644 index 0000000..95ca047 --- /dev/null +++ b/libobs/data/repeat.effect @@ -0,0 +1,36 @@ +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float2 scale; + +sampler_state def_sampler { + Filter = Linear; + AddressU = Repeat; + AddressV = Repeat; +}; + +struct VertInOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertInOut VSDefault(VertInOut vert_in) +{ + VertInOut vert_out; + vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = vert_in.uv * scale; + return vert_out; +} + +float4 PSDrawBare(VertInOut vert_in) : TARGET +{ + return image.Sample(def_sampler, vert_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(vert_in); + pixel_shader = PSDrawBare(vert_in); + } +} diff --git a/libobs/graphics/device-exports.h b/libobs/graphics/device-exports.h index 5bb29c2..2158e58 100644 --- a/libobs/graphics/device-exports.h +++ b/libobs/graphics/device-exports.h @@ -142,6 +142,9 @@ EXPORT void device_frustum(gs_device_t *device, float left, float right, float top, float bottom, float znear, float zfar); EXPORT void device_projection_push(gs_device_t *device); EXPORT void device_projection_pop(gs_device_t *device); +EXPORT void device_debug_marker_begin(gs_device_t *device, + const char *markername, const float color[4]); +EXPORT void device_debug_marker_end(gs_device_t *device); #ifdef __cplusplus } diff --git a/libobs/graphics/effect-parser.c b/libobs/graphics/effect-parser.c index d721b41..70e9842 100644 --- a/libobs/graphics/effect-parser.c +++ b/libobs/graphics/effect-parser.c @@ -21,6 +21,39 @@ #include "effect-parser.h" #include "effect.h" +static inline bool ep_parse_param_assign(struct effect_parser *ep, + struct ep_param *param); + +static enum gs_shader_param_type get_effect_param_type(const char *type) +{ + if (strcmp(type, "float") == 0) + return GS_SHADER_PARAM_FLOAT; + else if (strcmp(type, "float2") == 0) + return GS_SHADER_PARAM_VEC2; + else if (strcmp(type, "float3") == 0) + return GS_SHADER_PARAM_VEC3; + else if (strcmp(type, "float4") == 0) + return GS_SHADER_PARAM_VEC4; + else if (strcmp(type, "int2") == 0) + return GS_SHADER_PARAM_INT2; + else if (strcmp(type, "int3") == 0) + return GS_SHADER_PARAM_INT3; + else if (strcmp(type, "int4") == 0) + return GS_SHADER_PARAM_INT4; + else if (astrcmp_n(type, "texture", 7) == 0) + return GS_SHADER_PARAM_TEXTURE; + else if (strcmp(type, "float4x4") == 0) + return GS_SHADER_PARAM_MATRIX4X4; + else if (strcmp(type, "bool") == 0) + return GS_SHADER_PARAM_BOOL; + else if (strcmp(type, "int") == 0) + return GS_SHADER_PARAM_INT; + else if (strcmp(type, "string") == 0) + return GS_SHADER_PARAM_STRING; + + return GS_SHADER_PARAM_UNKNOWN; +} + void ep_free(struct effect_parser *ep) { size_t i; @@ -92,6 +125,18 @@ static inline struct ep_param *ep_getparam(struct effect_parser *ep, return NULL; } +static inline struct ep_param *ep_getannotation(struct ep_param *param, + const char *name) +{ + size_t i; + for (i = 0; i < param->annotations.num; i++) { + if (strcmp(name, param->annotations.array[i].name) == 0) + return param->annotations.array+i; + } + + return NULL; +} + static inline struct ep_func *ep_getfunc_strref(struct effect_parser *ep, const struct strref *ref) { @@ -262,6 +307,145 @@ error: ep_struct_free(&eps); } +static inline int ep_parse_param_annotation_var(struct effect_parser *ep, + struct ep_param *var) +{ + int code; + + /* -------------------------------------- */ + /* variable type */ + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, ";")) + return PARSE_CONTINUE; + if (cf_token_is(&ep->cfp, ">")) + return PARSE_BREAK; + + code = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, "type name", ";"); + if (code != PARSE_SUCCESS) + return code; + + bfree(var->type); + cf_copy_token(&ep->cfp, &var->type); + + /* -------------------------------------- */ + /* variable name */ + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, ";")) { + cf_adderror_expecting(&ep->cfp, "variable name"); + return PARSE_UNEXPECTED_CONTINUE; + } + if (cf_token_is(&ep->cfp, ">")) { + cf_adderror_expecting(&ep->cfp, "variable name"); + return PARSE_UNEXPECTED_BREAK; + } + + code = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, "variable name", ";"); + if (code != PARSE_SUCCESS) + return code; + + bfree(var->name); + cf_copy_token(&ep->cfp, &var->name); + + /* -------------------------------------- */ + /* variable mapping if any (POSITION, TEXCOORD, etc) */ + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, ":")) { + cf_adderror_expecting(&ep->cfp, "= or ;"); + return PARSE_UNEXPECTED_BREAK; + } else if (cf_token_is(&ep->cfp, ">")) { + cf_adderror_expecting(&ep->cfp, "= or ;"); + return PARSE_UNEXPECTED_BREAK; + } else if (cf_token_is(&ep->cfp, "=")) { + if (!ep_parse_param_assign(ep, var)) { + cf_adderror_expecting(&ep->cfp, "assignment value"); + return PARSE_UNEXPECTED_BREAK; + } + } + + /* -------------------------------------- */ + + if (!cf_token_is(&ep->cfp, ";")) { + if (!cf_go_to_valid_token(&ep->cfp, ";", ">")) { + cf_adderror_expecting(&ep->cfp, "; or >"); + return PARSE_EOF; + } + return PARSE_CONTINUE; + } + + return PARSE_SUCCESS; +} + +static int ep_parse_annotations(struct effect_parser *ep, + struct darray *annotations) +{ + if (!cf_token_is(&ep->cfp, "<")) { + cf_adderror_expecting(&ep->cfp, "<"); + goto error; + } + + /* get annotation variables */ + while (true) { + bool do_break = false; + struct ep_param var; + + ep_param_init(&var, bstrdup(""), bstrdup(""), false, false, + false); + + switch (ep_parse_param_annotation_var(ep, &var)) { + case PARSE_UNEXPECTED_CONTINUE: + cf_adderror_syntax_error(&ep->cfp); + /* Falls through. */ + case PARSE_CONTINUE: + ep_param_free(&var); + continue; + + case PARSE_UNEXPECTED_BREAK: + cf_adderror_syntax_error(&ep->cfp); + /* Falls through. */ + case PARSE_BREAK: + ep_param_free(&var); + do_break = true; + break; + + case PARSE_EOF: + ep_param_free(&var); + goto error; + } + + if (do_break) + break; + + darray_push_back(sizeof(struct ep_param), annotations, &var); + } + + if (!cf_token_is(&ep->cfp, ">")) { + cf_adderror_expecting(&ep->cfp, ">"); + goto error; + } + if (!cf_next_valid_token(&ep->cfp)) + goto error; + + return true; + +error: + return false; +} + +static int ep_parse_param_annotations(struct effect_parser *ep, + struct ep_param *param) +{ + return ep_parse_annotations(ep, ¶m->annotations.da); +} + static inline int ep_parse_pass_command_call(struct effect_parser *ep, struct darray *call) { @@ -328,7 +512,7 @@ static int ep_parse_pass(struct effect_parser *ep, struct ep_pass *pass) if (!cf_token_is(&ep->cfp, "{")) { pass->name = bstrdup_n(ep->cfp.cur_token->str.array, - ep->cfp.cur_token->str.len); + ep->cfp.cur_token->str.len); if (!cf_next_valid_token(&ep->cfp)) return PARSE_EOF; } @@ -356,9 +540,19 @@ static void ep_parse_technique(struct effect_parser *ep) if (cf_next_name(&ep->cfp, &ept.name, "name", ";") != PARSE_SUCCESS) goto error; - if (cf_next_token_should_be(&ep->cfp, "{", ";", NULL) != PARSE_SUCCESS) - goto error; + if (!cf_next_valid_token(&ep->cfp)) + return; + + if (!cf_token_is(&ep->cfp, "{")) { + if (!cf_go_to_token(&ep->cfp, ";", NULL)) { + cf_adderror_expecting(&ep->cfp, ";"); + return; + } + + cf_adderror_expecting(&ep->cfp, "{"); + goto error; + } if (!cf_next_valid_token(&ep->cfp)) goto error; @@ -756,6 +950,30 @@ static inline int ep_parse_param_assign_texture(struct effect_parser *ep, return PARSE_SUCCESS; } +static inline int ep_parse_param_assign_string(struct effect_parser *ep, + struct ep_param *param) +{ + int code; + char *str = NULL; + + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + code = cf_token_is_type(&ep->cfp, CFTOKEN_STRING, "string", ";"); + if (code != PARSE_SUCCESS) + return code; + + str = cf_literal_to_str(ep->cfp.cur_token->str.array, + ep->cfp.cur_token->str.len); + + if (str) { + da_copy_array(param->default_val, str, strlen(str) + 1); + bfree(str); + } + + return PARSE_SUCCESS; +} + static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep, struct ep_param *param, bool is_float) { @@ -789,30 +1007,51 @@ static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep, return PARSE_SUCCESS; } -/* - * parses assignment for float1, float2, float3, float4, and any combination - * for float3x3, float4x4, etc - */ -static inline int ep_parse_param_assign_float_array(struct effect_parser *ep, +static inline int ep_parse_param_assign_bool(struct effect_parser *ep, struct ep_param *param) { - const char *float_type = param->type+5; - int float_count = 0, code, i; + if (!cf_next_valid_token(&ep->cfp)) + return PARSE_EOF; + + if (cf_token_is(&ep->cfp, "true")) { + long l = 1; + da_push_back_array(param->default_val, &l, sizeof(long)); + return PARSE_SUCCESS; + } else if (cf_token_is(&ep->cfp, "false")) { + long l = 0; + da_push_back_array(param->default_val, &l, sizeof(long)); + return PARSE_SUCCESS; + } + + cf_adderror_expecting(&ep->cfp, "true or false"); + + return PARSE_EOF; +} + +/* + * parses assignment for float1, float2, float3, float4, int1, int2, int3, int4, + * and any combination for float3x3, float4x4, int3x3, int4x4, etc +*/ +static inline int ep_parse_param_assign_intfloat_array(struct effect_parser *ep, + struct ep_param *param, bool is_float) +{ + const char *intfloat_type = param->type + (is_float ? 5 : 3); + int intfloat_count = 0, code, i; /* -------------------------------------------- */ - if (float_type[0] < '1' || float_type[0] > '4') + if (intfloat_type[0] < '1' || intfloat_type[0] > '4') cf_adderror(&ep->cfp, "Invalid row count", LEX_ERROR, NULL, NULL, NULL); - float_count = float_type[0]-'0'; + intfloat_count = intfloat_type[0]-'0'; - if (float_type[1] == 'x') { - if (float_type[2] < '1' || float_type[2] > '4') + if (intfloat_type[1] == 'x') { + if (intfloat_type[2] < '1' || intfloat_type[2] > '4') cf_adderror(&ep->cfp, "Invalid column count", LEX_ERROR, NULL, NULL, NULL); - float_count *= float_type[2]-'0'; + intfloat_count *= intfloat_type[2]-'0'; } /* -------------------------------------------- */ @@ -820,10 +1059,10 @@ static inline int ep_parse_param_assign_float_array(struct effect_parser *ep, code = cf_next_token_should_be(&ep->cfp, "{", ";", NULL); if (code != PARSE_SUCCESS) return code; - for (i = 0; i < float_count; i++) { - char *next = ((i+1) < float_count) ? "," : "}"; + for (i = 0; i < intfloat_count; i++) { + char *next = ((i+1) < intfloat_count) ? "," : "}"; - code = ep_parse_param_assign_intfloat(ep, param, true); + code = ep_parse_param_assign_intfloat(ep, param, is_float); if (code != PARSE_SUCCESS) return code; code = cf_next_token_should_be(&ep->cfp, next, ";", NULL); @@ -842,8 +1081,14 @@ static int ep_parse_param_assignment_val(struct effect_parser *ep, return ep_parse_param_assign_intfloat(ep, param, false); else if (strcmp(param->type, "float") == 0) return ep_parse_param_assign_intfloat(ep, param, true); + else if (astrcmp_n(param->type, "int", 3) == 0) + return ep_parse_param_assign_intfloat_array(ep, param, false); else if (astrcmp_n(param->type, "float", 5) == 0) - return ep_parse_param_assign_float_array(ep, param); + return ep_parse_param_assign_intfloat_array(ep, param, true); + else if (astrcmp_n(param->type, "string", 6) == 0) + return ep_parse_param_assign_string(ep, param); + else if (strcmp(param->type, "bool") == 0) + return ep_parse_param_assign_bool(ep, param); cf_adderror(&ep->cfp, "Invalid type '$1' used for assignment", LEX_ERROR, param->type, NULL, NULL); @@ -879,6 +1124,9 @@ static void ep_parse_param(struct effect_parser *ep, goto complete; if (cf_token_is(&ep->cfp, "[") && !ep_parse_param_array(ep, ¶m)) goto error; + if (cf_token_is(&ep->cfp, "<") && !ep_parse_param_annotations(ep, + ¶m)) + goto error; if (cf_token_is(&ep->cfp, "=") && !ep_parse_param_assign(ep, ¶m)) goto error; /* @@ -945,7 +1193,6 @@ static void ep_parse_other(struct effect_parser *ep) goto error; if (cf_next_name(&ep->cfp, &name, "name", ";") != PARSE_SUCCESS) goto error; - if (!cf_next_valid_token(&ep->cfp)) goto error; @@ -971,6 +1218,213 @@ static bool ep_compile(struct effect_parser *ep); extern const char *gs_preprocessor_name(void); +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) +static void debug_get_default_value(struct gs_effect_param *param, + char* buffer, unsigned long long buf_size) +{ + if (param->default_val.num == 0) { + snprintf(buffer, buf_size, "(null)"); + return; + } + + switch (param->type) { + case GS_SHADER_PARAM_STRING: + snprintf(buffer, buf_size, "'%.*s'", + param->default_val.num, + param->default_val.array); + break; + case GS_SHADER_PARAM_INT: + snprintf(buffer, buf_size, "%ld", + *(int*)(param->default_val.array + 0)); + break; + case GS_SHADER_PARAM_INT2: + snprintf(buffer, buf_size, "%ld,%ld", + *(int*)(param->default_val.array + 0), + *(int*)(param->default_val.array + 4)); + break; + case GS_SHADER_PARAM_INT3: + snprintf(buffer, buf_size, "%ld,%ld,%ld", + *(int*)(param->default_val.array + 0), + *(int*)(param->default_val.array + 4), + *(int*)(param->default_val.array + 8)); + break; + case GS_SHADER_PARAM_INT4: + snprintf(buffer, buf_size, "%ld,%ld,%ld,%ld", + *(int*)(param->default_val.array + 0), + *(int*)(param->default_val.array + 4), + *(int*)(param->default_val.array + 8), + *(int*)(param->default_val.array + 12)); + break; + case GS_SHADER_PARAM_FLOAT: + snprintf(buffer, buf_size, "%e", + *(float*)(param->default_val.array + 0)); + break; + case GS_SHADER_PARAM_VEC2: + snprintf(buffer, buf_size, "%e,%e", + *(float*)(param->default_val.array + 0), + *(float*)(param->default_val.array + 4)); + break; + case GS_SHADER_PARAM_VEC3: + snprintf(buffer, buf_size, "%e,%e,%e", + *(float*)(param->default_val.array + 0), + *(float*)(param->default_val.array + 4), + *(float*)(param->default_val.array + 8)); + break; + case GS_SHADER_PARAM_VEC4: + snprintf(buffer, buf_size, "%e,%e,%e,%e", + *(float*)(param->default_val.array + 0), + *(float*)(param->default_val.array + 4), + *(float*)(param->default_val.array + 8), + *(float*)(param->default_val.array + 12)); + break; + case GS_SHADER_PARAM_MATRIX4X4: + snprintf(buffer, buf_size, + "[[%e,%e,%e,%e],[%e,%e,%e,%e]," + "[%e,%e,%e,%e],[%e,%e,%e,%e]]", + *(float*)(param->default_val.array + 0), + *(float*)(param->default_val.array + 4), + *(float*)(param->default_val.array + 8), + *(float*)(param->default_val.array + 12), + *(float*)(param->default_val.array + 16), + *(float*)(param->default_val.array + 20), + *(float*)(param->default_val.array + 24), + *(float*)(param->default_val.array + 28), + *(float*)(param->default_val.array + 32), + *(float*)(param->default_val.array + 36), + *(float*)(param->default_val.array + 40), + *(float*)(param->default_val.array + 44), + *(float*)(param->default_val.array + 48), + *(float*)(param->default_val.array + 52), + *(float*)(param->default_val.array + 56), + *(float*)(param->default_val.array + 60)); + break; + case GS_SHADER_PARAM_BOOL: + snprintf(buffer, buf_size, "%s", + (*param->default_val.array) != 0 + ? "true\0" + : "false\0"); + break; + case GS_SHADER_PARAM_UNKNOWN: + case GS_SHADER_PARAM_TEXTURE: + snprintf(buffer, buf_size, ""); + break; + } +} + +static void debug_param(struct gs_effect_param *param, + struct ep_param *param_in, unsigned long long idx, const char* offset) +{ + char _debug_type[4096]; + switch (param->type) { + case GS_SHADER_PARAM_STRING: + snprintf(_debug_type, sizeof(_debug_type), "string"); + break; + case GS_SHADER_PARAM_INT: + snprintf(_debug_type, sizeof(_debug_type), "int"); + break; + case GS_SHADER_PARAM_INT2: + snprintf(_debug_type, sizeof(_debug_type), "int2"); + break; + case GS_SHADER_PARAM_INT3: + snprintf(_debug_type, sizeof(_debug_type), "int3"); + break; + case GS_SHADER_PARAM_INT4: + snprintf(_debug_type, sizeof(_debug_type), "int4"); + break; + case GS_SHADER_PARAM_FLOAT: + snprintf(_debug_type, sizeof(_debug_type), "float"); + break; + case GS_SHADER_PARAM_VEC2: + snprintf(_debug_type, sizeof(_debug_type), "float2"); + break; + case GS_SHADER_PARAM_VEC3: + snprintf(_debug_type, sizeof(_debug_type), "float3"); + break; + case GS_SHADER_PARAM_VEC4: + snprintf(_debug_type, sizeof(_debug_type), "float4"); + break; + case GS_SHADER_PARAM_MATRIX4X4: + snprintf(_debug_type, sizeof(_debug_type), "float4x4"); + break; + case GS_SHADER_PARAM_BOOL: + snprintf(_debug_type, sizeof(_debug_type), "bool"); + break; + case GS_SHADER_PARAM_UNKNOWN: + snprintf(_debug_type, sizeof(_debug_type), "unknown"); + break; + case GS_SHADER_PARAM_TEXTURE: + snprintf(_debug_type, sizeof(_debug_type), "texture"); + break; + } + + char _debug_buf[4096]; + debug_get_default_value(param, _debug_buf, sizeof(_debug_buf)); + if (param->annotations.num > 0) { + blog(LOG_DEBUG, "%s[%4lld] %.*s '%s' with value %.*s and %lld annotations:", + offset, + idx, + sizeof(_debug_type), _debug_type, + param->name, + sizeof(_debug_buf), _debug_buf, + param->annotations.num); + } else { + blog(LOG_DEBUG, "%s[%4lld] %.*s '%s' with value %.*s.", + offset, + idx, + sizeof(_debug_type), _debug_type, + param->name, + sizeof(_debug_buf), _debug_buf); + } + +} + +static void debug_param_annotation(struct gs_effect_param *param, + struct ep_param *param_in, unsigned long long idx, const char* offset) +{ + char _debug_buf[4096]; + debug_get_default_value(param, _debug_buf, sizeof(_debug_buf)); + blog(LOG_DEBUG, "%s[%4lld] %s '%s' with value %.*s", + offset, + idx, + param_in->type, + param->name, + sizeof(_debug_buf), _debug_buf); +} + +static void debug_print_string(const char* offset, const char* str) +{ + // Bypass 4096 limit in def_log_handler. + char const *begin = str; + unsigned long long line = 1; + for (char const *here = begin; here[0] != '\0'; here++) { + char const * str = begin; + unsigned long long len = here - begin; + bool is_line = false; + + if (here[0] == '\r') { + is_line = true; + if (here[1] == '\n') { + here += 1; + } + begin = here + 1; + } else if (here[0] == '\n') { + is_line = true; + begin = here + 1; + } + + if (is_line) { + blog(LOG_DEBUG, "\t\t\t\t[%4lld] %.*s", line, + len, str); + line++; + } + } + if (begin[0] != '\0') { + // Final line was not written. + blog(LOG_DEBUG, "\t\t\t\t[%4lld] %*s", line, strlen(begin), begin); + } +} +#endif + bool ep_parse(struct effect_parser *ep, gs_effect_t *effect, const char *effect_string, const char *file) { @@ -1020,10 +1474,21 @@ bool ep_parse(struct effect_parser *ep, gs_effect_t *effect, } } +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + blog(LOG_DEBUG, "================================================================================"); + blog(LOG_DEBUG, "Effect Parser reformatted shader '%s' to:", file); + debug_print_string("\t", ep->cfp.lex.reformatted); +#endif + success = !error_data_has_errors(&ep->cfp.error_list); if (success) success = ep_compile(ep); + +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + blog(LOG_DEBUG, "================================================================================"); +#endif + return success; } @@ -1309,6 +1774,9 @@ static void ep_makeshaderstring(struct effect_parser *ep, dstr_init(&call_str); + if (!token) + return; + while (token->type != CFTOKEN_NONE && is_whitespace(*token->str.array)) token++; @@ -1339,6 +1807,39 @@ static void ep_makeshaderstring(struct effect_parser *ep, ep_reset_written(ep); } +static void ep_compile_annotations(struct darray *ep_annotations, + struct darray *gsp_annotations, struct effect_parser *ep) +{ + darray_resize(sizeof(struct gs_effect_param), + gsp_annotations, ep_annotations->num); + + size_t i; + for (i = 0; i < ep_annotations->num; i++) { + struct gs_effect_param *param = ((struct gs_effect_param *) + gsp_annotations->array)+i; + struct ep_param *param_in = ((struct ep_param *) + ep_annotations->array)+i; + + param->name = bstrdup(param_in->name); + param->section = EFFECT_ANNOTATION; + param->effect = ep->effect; + da_move(param->default_val, param_in->default_val); + + param->type = get_effect_param_type(param_in->type); + +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + debug_param(param, param_in, i, "\t\t"); +#endif + } +} + +static void ep_compile_param_annotations(struct ep_param *ep_param_input, + struct gs_effect_param *gs_effect_input, struct effect_parser *ep) +{ + ep_compile_annotations(&(ep_param_input->annotations.da), + &(gs_effect_input->annotations.da), ep); +} + static void ep_compile_param(struct effect_parser *ep, size_t idx) { struct gs_effect_param *param; @@ -1353,27 +1854,18 @@ static void ep_compile_param(struct effect_parser *ep, size_t idx) param->effect = ep->effect; da_move(param->default_val, param_in->default_val); - if (strcmp(param_in->type, "bool") == 0) - param->type = GS_SHADER_PARAM_BOOL; - else if (strcmp(param_in->type, "float") == 0) - param->type = GS_SHADER_PARAM_FLOAT; - else if (strcmp(param_in->type, "int") == 0) - param->type = GS_SHADER_PARAM_INT; - else if (strcmp(param_in->type, "float2") == 0) - param->type = GS_SHADER_PARAM_VEC2; - else if (strcmp(param_in->type, "float3") == 0) - param->type = GS_SHADER_PARAM_VEC3; - else if (strcmp(param_in->type, "float4") == 0) - param->type = GS_SHADER_PARAM_VEC4; - else if (strcmp(param_in->type, "float4x4") == 0) - param->type = GS_SHADER_PARAM_MATRIX4X4; - else if (param_in->is_texture) - param->type = GS_SHADER_PARAM_TEXTURE; + param->type = get_effect_param_type(param_in->type); if (strcmp(param_in->name, "ViewProj") == 0) ep->effect->view_proj = param; else if (strcmp(param_in->name, "World") == 0) ep->effect->world = param; + +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + debug_param(param, param_in, idx, "\t"); +#endif + + ep_compile_param_annotations(param_in, param, ep); } static bool ep_compile_pass_shaderparams(struct effect_parser *ep, @@ -1397,6 +1889,10 @@ static bool ep_compile_pass_shaderparams(struct effect_parser *ep, param->sparam = gs_shader_get_param_by_name(shader, param_name->array); +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + debug_param(param->eparam, 0, i, "\t\t\t\t"); +#endif + if (!param->sparam) { blog(LOG_ERROR, "Effect shader parameter not found"); return false; @@ -1441,6 +1937,7 @@ static inline bool ep_compile_pass_shader(struct effect_parser *ep, pass->vertshader = gs_vertexshader_create(shader_str.array, location.array, NULL); + shader = pass->vertshader; pass_params = &pass->vertshader_params.da; } else if (type == GS_SHADER_PIXEL) { @@ -1454,12 +1951,12 @@ static inline bool ep_compile_pass_shader(struct effect_parser *ep, pass_params = &pass->pixelshader_params.da; } -#if 0 - blog(LOG_DEBUG, "+++++++++++++++++++++++++++++++++++"); - blog(LOG_DEBUG, " %s", location.array); - blog(LOG_DEBUG, "-----------------------------------"); - blog(LOG_DEBUG, "%s", shader_str.array); - blog(LOG_DEBUG, "+++++++++++++++++++++++++++++++++++"); +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + blog(LOG_DEBUG, "\t\t\t%s Shader:", + type == GS_SHADER_VERTEX ? "Vertex" : "Fragment"); + blog(LOG_DEBUG, "\t\t\tCode:"); + debug_print_string("\t\t\t\t\t", shader_str.array); + blog(LOG_DEBUG, "\t\t\tParameters:"); #endif if (shader) @@ -1491,14 +1988,23 @@ static bool ep_compile_pass(struct effect_parser *ep, pass->name = bstrdup(pass_in->name); pass->section = EFFECT_PASS; - if (!ep_compile_pass_shader(ep, tech, pass, pass_in, idx, - GS_SHADER_VERTEX)) - success = false; +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + blog(LOG_DEBUG, "\t\t[%4lld] Pass '%s':", + idx, pass->name); +#endif if (!ep_compile_pass_shader(ep, tech, pass, pass_in, idx, - GS_SHADER_PIXEL)) + GS_SHADER_VERTEX)) { success = false; - + blog(LOG_ERROR, "Pass (%zu) <%s> missing vertex shader!", + idx, pass->name ? pass->name : ""); + } + if (!ep_compile_pass_shader(ep, tech, pass, pass_in, idx, + GS_SHADER_PIXEL)) { + success = false; + blog(LOG_ERROR, "Pass (%zu) <%s> missing pixel shader!", + idx, pass->name ? pass->name : ""); + } return success; } @@ -1518,6 +2024,11 @@ static inline bool ep_compile_technique(struct effect_parser *ep, size_t idx) da_resize(tech->passes, tech_in->passes.num); +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + blog(LOG_DEBUG, "\t[%4lld] Technique '%s' has %lld passes:", + idx, tech->name, tech->passes.num); +#endif + for (i = 0; i < tech->passes.num; i++) { if (!ep_compile_pass(ep, tech, tech_in, i)) success = false; @@ -1536,8 +2047,17 @@ static bool ep_compile(struct effect_parser *ep) da_resize(ep->effect->params, ep->params.num); da_resize(ep->effect->techniques, ep->techniques.num); +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + blog(LOG_DEBUG, "Shader has %lld parameters:", ep->params.num); +#endif + for (i = 0; i < ep->params.num; i++) ep_compile_param(ep, i); + +#if defined(_DEBUG) && defined(_DEBUG_SHADERS) + blog(LOG_DEBUG, "Shader has %lld techniques:", ep->techniques.num); +#endif + for (i = 0; i < ep->techniques.num; i++) { if (!ep_compile_technique(ep, i)) success = false; diff --git a/libobs/graphics/effect-parser.h b/libobs/graphics/effect-parser.h index c0eb6f0..1281399 100644 --- a/libobs/graphics/effect-parser.h +++ b/libobs/graphics/effect-parser.h @@ -20,6 +20,7 @@ #include "../util/darray.h" #include "../util/cf-parser.h" #include "graphics.h" +#include "shader-parser.h" #ifdef __cplusplus extern "C" { @@ -72,6 +73,7 @@ struct ep_param { struct gs_effect_param *param; bool is_const, is_property, is_uniform, is_texture, written; int writeorder, array_count; + DARRAY(struct ep_param) annotations; }; extern void ep_param_writevar(struct dstr *dst, struct darray *use_params); @@ -91,6 +93,7 @@ static inline void ep_param_init(struct ep_param *epp, epp->array_count = 0; da_init(epp->default_val); da_init(epp->properties); + da_init(epp->annotations); } static inline void ep_param_free(struct ep_param *epp) @@ -99,6 +102,10 @@ static inline void ep_param_free(struct ep_param *epp) bfree(epp->name); da_free(epp->default_val); da_free(epp->properties); + + for (size_t i = 0; i < epp->annotations.num; i++) + ep_param_free(epp->annotations.array + i); + da_free(epp->annotations); } /* ------------------------------------------------------------------------- */ diff --git a/libobs/graphics/effect.c b/libobs/graphics/effect.c index 4b2a716..ddd5d5a 100644 --- a/libobs/graphics/effect.c +++ b/libobs/graphics/effect.c @@ -286,6 +286,63 @@ gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, return NULL; } +size_t gs_param_get_num_annotations(const gs_eparam_t *param) +{ + return param ? param->annotations.num : 0; +} + +gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, + size_t annotation) +{ + if (!param) return NULL; + + struct gs_effect_param *params = param->annotations.array; + if (annotation > param->annotations.num) + return NULL; + + return params + annotation; +} + +gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param, + const char *name) +{ + if (!param) return NULL; + struct gs_effect_param *params = param->annotations.array; + + for (size_t i = 0; i < param->annotations.num; i++) { + struct gs_effect_param *g_param = params + i; + if (strcmp(g_param->name, name) == 0) + return g_param; + } + return NULL; +} + +gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique, + size_t pass) +{ + if (!technique) return NULL; + struct gs_effect_pass *passes = technique->passes.array; + + if (pass > technique->passes.num) + return NULL; + + return passes + pass; +} + +gs_epass_t *gs_technique_get_pass_by_name(const gs_technique_t *technique, + const char *name) +{ + if (!technique) return NULL; + struct gs_effect_pass *passes = technique->passes.array; + + for (size_t i = 0; i < technique->passes.num; i++) { + struct gs_effect_pass *g_pass = passes + i; + if (strcmp(g_pass->name, name) == 0) + return g_pass; + } + return NULL; +} + gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect) { return effect ? effect->view_proj : NULL; @@ -332,6 +389,45 @@ static inline void effect_setval_inline(gs_eparam_t *param, } } +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +static inline void effect_getval_inline(gs_eparam_t *param, void *data, + size_t size) +{ + if (!param) { + blog(LOG_ERROR, "effect_getval_inline: invalid param"); + return; + } + + if (!data) { + blog(LOG_ERROR, "effect_getval_inline: invalid data"); + return; + } + + size_t bytes = min(size, param->cur_val.num); + + memcpy(data, param->cur_val.array, bytes); +} + +static inline void effect_getdefaultval_inline(gs_eparam_t *param, void *data, + size_t size) +{ + if (!param) { + blog(LOG_ERROR, "effect_getdefaultval_inline: invalid param"); + return; + } + + if (!data) { + blog(LOG_ERROR, "effect_getdefaultval_inline: invalid data"); + return; + } + + size_t bytes = min(size, param->default_val.num); + + memcpy(data, param->default_val.array, bytes); +} + void gs_effect_set_bool(gs_eparam_t *param, bool val) { int b_val = (int)val; @@ -385,6 +481,54 @@ void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size) effect_setval_inline(param, val, size); } +void *gs_effect_get_val(gs_eparam_t *param) +{ + if (!param) { + blog(LOG_ERROR, "gs_effect_get_val: invalid param"); + return NULL; + } + size_t size = param->cur_val.num; + void *data; + + if (size) + data = (void*)bzalloc(size); + else + return NULL; + + effect_getval_inline(param, data, size); + + return data; +} + +size_t gs_effect_get_val_size(gs_eparam_t *param) +{ + return param ? param->cur_val.num : 0; +} + +void *gs_effect_get_default_val(gs_eparam_t *param) +{ + if (!param) { + blog(LOG_ERROR, "gs_effect_get_default_val: invalid param"); + return NULL; + } + size_t size = param->default_val.num; + void *data; + + if (size) + data = (void*)bzalloc(size); + else + return NULL; + + effect_getdefaultval_inline(param, data, size); + + return data; +} + +size_t gs_effect_get_default_val_size(gs_eparam_t *param) +{ + return param ? param->default_val.num : 0; +} + void gs_effect_set_default(gs_eparam_t *param) { effect_setval_inline(param, param->default_val.array, diff --git a/libobs/graphics/effect.h b/libobs/graphics/effect.h index d76fb85..9a86868 100644 --- a/libobs/graphics/effect.h +++ b/libobs/graphics/effect.h @@ -41,7 +41,8 @@ enum effect_section { EFFECT_PARAM, EFFECT_TECHNIQUE, EFFECT_SAMPLER, - EFFECT_PASS + EFFECT_PASS, + EFFECT_ANNOTATION }; /* ------------------------------------------------------------------------- */ @@ -61,11 +62,13 @@ struct gs_effect_param { /*char *full_name; float scroller_min, scroller_max, scroller_inc, scroller_mul;*/ + DARRAY(struct gs_effect_param) annotations; }; static inline void effect_param_init(struct gs_effect_param *param) { memset(param, 0, sizeof(struct gs_effect_param)); + da_init(param->annotations); } static inline void effect_param_free(struct gs_effect_param *param) @@ -74,6 +77,12 @@ static inline void effect_param_free(struct gs_effect_param *param) //bfree(param->full_name); da_free(param->cur_val); da_free(param->default_val); + + size_t i; + for (i = 0; i < param->annotations.num; i++) + effect_param_free(param->annotations.array + i); + + da_free(param->annotations); } EXPORT void effect_param_parse_property(gs_eparam_t *param, @@ -106,6 +115,7 @@ static inline void effect_pass_free(struct gs_effect_pass *pass) bfree(pass->name); da_free(pass->vertshader_params); da_free(pass->pixelshader_params); + gs_shader_destroy(pass->vertshader); gs_shader_destroy(pass->pixelshader); } @@ -130,6 +140,7 @@ static inline void effect_technique_free(struct gs_effect_technique *t) size_t i; for (i = 0; i < t->passes.num; i++) effect_pass_free(t->passes.array+i); + da_free(t->passes); bfree(t->name); } diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index 63c19ab..31ef4ab 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -171,6 +171,11 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT(gs_shader_set_default); GRAPHICS_IMPORT(gs_shader_set_next_sampler); + GRAPHICS_IMPORT_OPTIONAL(device_nv12_available); + + GRAPHICS_IMPORT(device_debug_marker_begin); + GRAPHICS_IMPORT(device_debug_marker_end); + /* OSX/Cocoa specific functions */ #ifdef __APPLE__ GRAPHICS_IMPORT_OPTIONAL(device_texture_create_from_iosurface); @@ -189,6 +194,11 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT_OPTIONAL(gs_texture_get_dc); GRAPHICS_IMPORT_OPTIONAL(gs_texture_release_dc); GRAPHICS_IMPORT_OPTIONAL(device_texture_open_shared); + GRAPHICS_IMPORT_OPTIONAL(device_texture_get_shared_handle); + GRAPHICS_IMPORT_OPTIONAL(device_texture_acquire_sync); + GRAPHICS_IMPORT_OPTIONAL(device_texture_release_sync); + GRAPHICS_IMPORT_OPTIONAL(device_texture_create_nv12); + GRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_nv12); #endif return success; diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 6be06d5..2c55484 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -232,6 +232,12 @@ struct gs_exports { void (*gs_shader_set_next_sampler)(gs_sparam_t *param, gs_samplerstate_t *sampler); + bool (*device_nv12_available)(gs_device_t *device); + + void (*device_debug_marker_begin)(gs_device_t *device, + const char *markername, const float color[4]); + void (*device_debug_marker_end)(gs_device_t *device); + #ifdef __APPLE__ /* OSX/Cocoa specific functions */ gs_texture_t *(*device_texture_create_from_iosurface)(gs_device_t *dev, @@ -261,6 +267,16 @@ struct gs_exports { gs_texture_t *(*device_texture_open_shared)(gs_device_t *device, uint32_t handle); + uint32_t (*device_texture_get_shared_handle)(gs_texture_t *tex); + int (*device_texture_acquire_sync)(gs_texture_t *tex, uint64_t key, + uint32_t ms); + int (*device_texture_release_sync)(gs_texture_t *tex, uint64_t key); + bool (*device_texture_create_nv12)(gs_device_t *device, + gs_texture_t **tex_y, gs_texture_t **tex_uv, + uint32_t width, uint32_t height, uint32_t flags); + + gs_stagesurf_t *(*device_stagesurface_create_nv12)(gs_device_t *device, + uint32_t width, uint32_t height); #endif }; diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index b6f5bd0..671d925 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -160,12 +160,12 @@ static bool graphics_init(struct graphics_subsystem *graphics) graphics->exports.device_blend_function_separate(graphics->device, GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA, - GS_BLEND_ONE, GS_BLEND_ONE); + GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); graphics->cur_blend_state.enabled = true; graphics->cur_blend_state.src_c = GS_BLEND_SRCALPHA; graphics->cur_blend_state.dest_c = GS_BLEND_INVSRCALPHA; graphics->cur_blend_state.src_a = GS_BLEND_ONE; - graphics->cur_blend_state.dest_a = GS_BLEND_ONE; + graphics->cur_blend_state.dest_a = GS_BLEND_INVSRCALPHA; graphics->exports.device_leave_context(graphics->device); @@ -1240,10 +1240,10 @@ void gs_reset_blend_state(void) if (graphics->cur_blend_state.src_c != GS_BLEND_SRCALPHA || graphics->cur_blend_state.dest_c != GS_BLEND_INVSRCALPHA || graphics->cur_blend_state.src_a != GS_BLEND_ONE || - graphics->cur_blend_state.dest_a != GS_BLEND_ONE) + graphics->cur_blend_state.dest_a != GS_BLEND_INVSRCALPHA) gs_blend_function_separate( GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA, - GS_BLEND_ONE, GS_BLEND_ONE); + GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); } /* ------------------------------------------------------------------------- */ @@ -1517,9 +1517,7 @@ gs_indexbuffer_t *gs_indexbuffer_create(enum gs_index_type type, return NULL; if (indices && num && (flags & GS_DUP_BUFFER) != 0) { - size_t size = type == GS_UNSIGNED_SHORT - ? sizeof(unsigned short) - : sizeof(unsigned long); + size_t size = type == GS_UNSIGNED_SHORT ? 2 : 4; indices = bmemdup(indices, size * num); } @@ -2545,6 +2543,61 @@ enum gs_index_type gs_indexbuffer_get_type(const gs_indexbuffer_t *indexbuffer) return thread_graphics->exports.gs_indexbuffer_get_type(indexbuffer); } +bool gs_nv12_available(void) +{ + if (!gs_valid("gs_nv12_available")) + return false; + + if (!thread_graphics->exports.device_nv12_available) + return false; + + return thread_graphics->exports.device_nv12_available( + thread_graphics->device); +} + +void gs_debug_marker_begin(const float color[4], + const char *markername) +{ + if (!gs_valid("gs_debug_marker_begin")) + return; + + if (!markername) + markername = "(null)"; + + thread_graphics->exports.device_debug_marker_begin( + thread_graphics->device, markername, + color); +} + +void gs_debug_marker_begin_format(const float color[4], + const char *format, ...) +{ + if (!gs_valid("gs_debug_marker_begin")) + return; + + if (format) { + char markername[64]; + va_list args; + va_start(args, format); + vsnprintf(markername, sizeof(markername), format, args); + va_end(args); + thread_graphics->exports.device_debug_marker_begin( + thread_graphics->device, markername, + color); + } else { + gs_debug_marker_begin(color, NULL); + } +} + +void gs_debug_marker_end(void) +{ + if (!gs_valid("gs_debug_marker_end")) + return; + + thread_graphics->exports.device_debug_marker_end( + thread_graphics->device); +} + #ifdef __APPLE__ /** Platform specific functions */ @@ -2692,4 +2745,98 @@ gs_texture_t *gs_texture_open_shared(uint32_t handle) return NULL; } +uint32_t gs_texture_get_shared_handle(gs_texture_t *tex) +{ + graphics_t *graphics = thread_graphics; + if (!gs_valid("gs_texture_get_shared_handle")) + return GS_INVALID_HANDLE; + + if (graphics->exports.device_texture_get_shared_handle) + return graphics->exports.device_texture_get_shared_handle(tex); + return GS_INVALID_HANDLE; +} + +int gs_texture_acquire_sync(gs_texture_t *tex, uint64_t key, uint32_t ms) +{ + graphics_t *graphics = thread_graphics; + if (!gs_valid("gs_texture_acquire_sync")) + return -1; + + if (graphics->exports.device_texture_acquire_sync) + return graphics->exports.device_texture_acquire_sync(tex, + key, ms); + return -1; +} + +int gs_texture_release_sync(gs_texture_t *tex, uint64_t key) +{ + graphics_t *graphics = thread_graphics; + if (!gs_valid("gs_texture_release_sync")) + return -1; + + if (graphics->exports.device_texture_release_sync) + return graphics->exports.device_texture_release_sync(tex, key); + return -1; +} + +bool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv, + uint32_t width, uint32_t height, uint32_t flags) +{ + graphics_t *graphics = thread_graphics; + bool success = false; + + if (!gs_valid("gs_texture_create_nv12")) + return false; + + if ((width & 1) == 1 || (height & 1) == 1) { + blog(LOG_ERROR, "NV12 textures must have dimensions " + "divisible by 2."); + return false; + } + + if (graphics->exports.device_texture_create_nv12) { + success = graphics->exports.device_texture_create_nv12( + graphics->device, tex_y, tex_uv, + width, height, flags); + if (success) + return true; + } + + *tex_y = gs_texture_create(width, height, GS_R8, 1, NULL, flags); + *tex_uv = gs_texture_create(width / 2, height / 2, GS_R8G8, 1, NULL, + flags); + + if (!*tex_y || !*tex_uv) { + if (*tex_y) + gs_texture_destroy(*tex_y); + if (*tex_uv) + gs_texture_destroy(*tex_uv); + *tex_y = NULL; + *tex_uv = NULL; + return false; + } + + return true; +} + +gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height) +{ + graphics_t *graphics = thread_graphics; + + if (!gs_valid("gs_stagesurface_create_nv12")) + return NULL; + + if ((width & 1) == 1 || (height & 1) == 1) { + blog(LOG_ERROR, "NV12 textures must have dimensions " + "divisible by 2."); + return NULL; + } + + if (graphics->exports.device_stagesurface_create_nv12) + return graphics->exports.device_stagesurface_create_nv12( + graphics->device, width, height); + + return NULL; +} + #endif diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index df9583f..a3f6de8 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -71,7 +71,8 @@ enum gs_color_format { GS_R32F, GS_DXT1, GS_DXT3, - GS_DXT5 + GS_DXT5, + GS_R8G8, }; enum gs_zstencil_format { @@ -267,6 +268,7 @@ typedef struct gs_shader gs_shader_t; typedef struct gs_shader_param gs_sparam_t; typedef struct gs_effect gs_effect_t; typedef struct gs_effect_technique gs_technique_t; +typedef struct gs_effect_pass gs_epass_t; typedef struct gs_effect_param gs_eparam_t; typedef struct gs_device gs_device_t; typedef struct graphics_subsystem graphics_t; @@ -368,12 +370,21 @@ EXPORT bool gs_technique_begin_pass(gs_technique_t *technique, size_t pass); EXPORT bool gs_technique_begin_pass_by_name(gs_technique_t *technique, const char *name); EXPORT void gs_technique_end_pass(gs_technique_t *technique); +EXPORT gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique, + size_t pass); +EXPORT gs_epass_t *gs_technique_get_pass_by_name( + const gs_technique_t *technique, const char *name); EXPORT size_t gs_effect_get_num_params(const gs_effect_t *effect); EXPORT gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param); EXPORT gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, const char *name); +EXPORT size_t gs_param_get_num_annotations(const gs_eparam_t *param); +EXPORT gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, + size_t annotation); +EXPORT gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param, + const char *name); /** Helper function to simplify effect usage. Use with a while loop that * contains drawing functions. Automatically handles techniques, passes, and @@ -402,6 +413,10 @@ EXPORT void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val); EXPORT void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val); EXPORT void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size); EXPORT void gs_effect_set_default(gs_eparam_t *param); +EXPORT size_t gs_effect_get_val_size(gs_eparam_t *param); +EXPORT void *gs_effect_get_val(gs_eparam_t *param); +EXPORT size_t gs_effect_get_default_val_size(gs_eparam_t *param); +EXPORT void *gs_effect_get_default_val(gs_eparam_t *param); EXPORT void gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler); @@ -430,6 +445,8 @@ EXPORT gs_texture_t *gs_texrender_get_texture(const gs_texrender_t *texrender); #define GS_GL_DUMMYTEX (1<<3) /**<< texture with no allocated texture data */ #define GS_DUP_BUFFER (1<<4) /**<< do not pass buffer ownership when * creating a vertex/index buffer */ +#define GS_SHARED_TEX (1<<5) +#define GS_SHARED_KM_TEX (1<<6) /* ---------------- */ /* global functions */ @@ -742,6 +759,36 @@ EXPORT size_t gs_indexbuffer_get_num_indices( EXPORT enum gs_index_type gs_indexbuffer_get_type( const gs_indexbuffer_t *indexbuffer); +EXPORT bool gs_nv12_available(void); + +#define GS_USE_DEBUG_MARKERS 0 +#if GS_USE_DEBUG_MARKERS +static const float GS_DEBUG_COLOR_DEFAULT[] = { 0.5f, 0.5f, 0.5f, 1.0f }; +static const float GS_DEBUG_COLOR_RENDER_VIDEO[] = { 0.0f, 0.5f, 0.0f, 1.0f }; +static const float GS_DEBUG_COLOR_MAIN_TEXTURE[] = { 0.0f, 0.25f, 0.0f, 1.0f }; +static const float GS_DEBUG_COLOR_DISPLAY[] = { 0.0f, 0.5f, 0.5f, 1.0f }; +static const float GS_DEBUG_COLOR_SOURCE[] = { 0.0f, 0.5f, 5.0f, 1.0f }; +static const float GS_DEBUG_COLOR_ITEM[] = { 0.5f, 0.0f, 0.0f, 1.0f }; +static const float GS_DEBUG_COLOR_ITEM_TEXTURE[] = { 0.25f, 0.0f, 0.0f, 1.0f }; +static const float GS_DEBUG_COLOR_CONVERT_FORMAT[] = { 0.5f, 0.5f, 0.0f, 1.0f }; +#define GS_DEBUG_MARKER_BEGIN(color, markername) \ + gs_debug_marker_begin(color, markername) +#define GS_DEBUG_MARKER_BEGIN_FORMAT(color, format, ...) \ + gs_debug_marker_begin_format(color, format, \ + __VA_ARGS__) +#define GS_DEBUG_MARKER_END() gs_debug_marker_end() +#else +#define GS_DEBUG_MARKER_BEGIN(color, markername) ((void)0) +#define GS_DEBUG_MARKER_BEGIN_FORMAT(color, format, ...) ((void)0) +#define GS_DEBUG_MARKER_END() ((void)0) +#endif + +EXPORT void gs_debug_marker_begin(const float color[4], + const char *markername); +EXPORT void gs_debug_marker_begin_format(const float color[4], + const char *format, ...); +EXPORT void gs_debug_marker_end(void); + #ifdef __APPLE__ /** platform specific function for creating (GL_TEXTURE_RECTANGLE) textures @@ -780,6 +827,30 @@ EXPORT void gs_texture_release_dc(gs_texture_t *gdi_tex); /** creates a windows shared texture from a texture handle */ EXPORT gs_texture_t *gs_texture_open_shared(uint32_t handle); + +#define GS_INVALID_HANDLE (uint32_t)-1 +EXPORT uint32_t gs_texture_get_shared_handle(gs_texture_t *tex); + +#define GS_WAIT_INFINITE (uint32_t)-1 + +/** + * acquires a lock on a keyed mutex texture. + * returns -1 on generic failure, ETIMEDOUT if timed out + */ +EXPORT int gs_texture_acquire_sync(gs_texture_t *tex, uint64_t key, uint32_t ms); + +/** + * releases a lock on a keyed mutex texture to another device. + * return 0 on success, -1 on error + */ +EXPORT int gs_texture_release_sync(gs_texture_t *tex, uint64_t key); + +EXPORT bool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv, + uint32_t width, uint32_t height, uint32_t flags); + +EXPORT gs_stagesurf_t *gs_stagesurface_create_nv12( + uint32_t width, uint32_t height); + #endif /* inline functions used by modules */ @@ -804,6 +875,7 @@ static inline uint32_t gs_get_format_bpp(enum gs_color_format format) case GS_DXT1: return 4; case GS_DXT3: return 8; case GS_DXT5: return 8; + case GS_R8G8: return 16; case GS_UNKNOWN: return 0; } diff --git a/libobs/graphics/image-file.c b/libobs/graphics/image-file.c index 4262c0b..402014f 100644 --- a/libobs/graphics/image-file.c +++ b/libobs/graphics/image-file.c @@ -59,7 +59,18 @@ static inline int get_full_decoded_gif_size(gs_image_file_t *image) return image->gif.width * image->gif.height * 4 * image->gif.frame_count; } -static bool init_animated_gif(gs_image_file_t *image, const char *path) +static inline void *alloc_mem(gs_image_file_t *image, uint64_t *mem_usage, + size_t size) +{ + UNUSED_PARAMETER(image); + + if (mem_usage) + *mem_usage += size; + return bzalloc(size); +} + +static bool init_animated_gif(gs_image_file_t *image, const char *path, + uint64_t *mem_usage) { bool is_animated_gif = true; gif_result result; @@ -121,9 +132,9 @@ static bool init_animated_gif(gs_image_file_t *image, const char *path) if (image->is_animated_gif) { gif_decode_frame(&image->gif, 0); - image->animation_frame_cache = bzalloc( + image->animation_frame_cache = alloc_mem(image, mem_usage, image->gif.frame_count * sizeof(uint8_t*)); - image->animation_frame_data = bzalloc( + image->animation_frame_data = alloc_mem(image, mem_usage, get_full_decoded_gif_size(image)); for (unsigned int i = 0; i < image->gif.frame_count; i++) { @@ -137,6 +148,11 @@ static bool init_animated_gif(gs_image_file_t *image, const char *path) image->cx = (uint32_t)image->gif.width; image->cy = (uint32_t)image->gif.height; image->format = GS_RGBA; + + if (mem_usage) { + *mem_usage += image->cx * image->cy * 4; + *mem_usage += size; + } } else { gif_finalise(&image->gif); bfree(image->gif_data); @@ -157,7 +173,8 @@ not_animated: return is_animated_gif; } -void gs_image_file_init(gs_image_file_t *image, const char *file) +static void gs_image_file_init_internal(gs_image_file_t *image, + const char *file, uint64_t *mem_usage) { size_t len; @@ -172,13 +189,18 @@ void gs_image_file_init(gs_image_file_t *image, const char *file) len = strlen(file); if (len > 4 && strcmp(file + len - 4, ".gif") == 0) { - if (init_animated_gif(image, file)) + if (init_animated_gif(image, file, mem_usage)) return; } image->texture_data = gs_create_texture_file_data(file, &image->format, &image->cx, &image->cy); + if (mem_usage) { + *mem_usage += image->cx * image->cy * + gs_get_format_bpp(image->format) / 8; + } + image->loaded = !!image->texture_data; if (!image->loaded) { blog(LOG_WARNING, "Failed to load file '%s'", file); @@ -186,6 +208,11 @@ void gs_image_file_init(gs_image_file_t *image, const char *file) } } +void gs_image_file_init(gs_image_file_t *image, const char *file) +{ + gs_image_file_init_internal(image, file, NULL); +} + void gs_image_file_free(gs_image_file_t *image) { if (!image) @@ -206,6 +233,11 @@ void gs_image_file_free(gs_image_file_t *image) memset(image, 0, sizeof(*image)); } +void gs_image_file2_init(gs_image_file2_t *if2, const char *file) +{ + gs_image_file_init_internal(&if2->image, file, &if2->mem_usage); +} + void gs_image_file_init_texture(gs_image_file_t *image) { if (!image->loaded) diff --git a/libobs/graphics/image-file.h b/libobs/graphics/image-file.h index 3720352..74e944e 100644 --- a/libobs/graphics/image-file.h +++ b/libobs/graphics/image-file.h @@ -20,6 +20,10 @@ #include "graphics.h" #include "libnsgif/libnsgif.h" +#ifdef __cplusplus +extern "C" { +#endif + struct gs_image_file { gs_texture_t *texture; enum gs_color_format format; @@ -42,7 +46,13 @@ struct gs_image_file { gif_bitmap_callback_vt bitmap_callbacks; }; +struct gs_image_file2 { + struct gs_image_file image; + uint64_t mem_usage; +}; + typedef struct gs_image_file gs_image_file_t; +typedef struct gs_image_file2 gs_image_file2_t; EXPORT void gs_image_file_init(gs_image_file_t *image, const char *file); EXPORT void gs_image_file_free(gs_image_file_t *image); @@ -51,3 +61,31 @@ EXPORT void gs_image_file_init_texture(gs_image_file_t *image); EXPORT bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns); EXPORT void gs_image_file_update_texture(gs_image_file_t *image); + +EXPORT void gs_image_file2_init(gs_image_file2_t *if2, const char *file); + +static void gs_image_file2_free(gs_image_file2_t *if2) +{ + gs_image_file_free(&if2->image); + if2->mem_usage = 0; +} + +static inline void gs_image_file2_init_texture(gs_image_file2_t *if2) +{ + gs_image_file_init_texture(&if2->image); +} + +static inline bool gs_image_file2_tick(gs_image_file2_t *if2, + uint64_t elapsed_time_ns) +{ + return gs_image_file_tick(&if2->image, elapsed_time_ns); +} + +static inline void gs_image_file2_update_texture(gs_image_file2_t *if2) +{ + gs_image_file_update_texture(&if2->image); +} + +#ifdef __cplusplus +} +#endif diff --git a/libobs/graphics/shader-parser.c b/libobs/graphics/shader-parser.c index 5464101..a9b354e 100644 --- a/libobs/graphics/shader-parser.c +++ b/libobs/graphics/shader-parser.c @@ -104,6 +104,8 @@ void shader_sampler_convert(struct shader_sampler *ss, size_t i; memset(info, 0, sizeof(struct gs_sampler_info)); + info->max_anisotropy = 1; + for (i = 0; i < ss->states.num; i++) { const char *state = ss->states.array[i]; const char *value = ss->values.array[i]; diff --git a/libobs/libobs.pc.in b/libobs/libobs.pc.in new file mode 100644 index 0000000..03fe4cd --- /dev/null +++ b/libobs/libobs.pc.in @@ -0,0 +1,11 @@ +prefix=@DEST_DIR@ +exec_prefix=${prefix} +libdir=${prefix}/@OBS_LIBRARY_DESTINATION@ +includedir=${prefix}/include + +Name: libobs +Description: OBS Studio Library +Version: @OBS_VERSION@ +Requires: x11 +Cflags: -I${includedir} +Libs: -L${libdir} @PRIVATE_LIBS@ diff --git a/libobs/media-io/audio-resampler-ffmpeg.c b/libobs/media-io/audio-resampler-ffmpeg.c index 7680125..e731e7e 100644 --- a/libobs/media-io/audio-resampler-ffmpeg.c +++ b/libobs/media-io/audio-resampler-ffmpeg.c @@ -102,6 +102,21 @@ audio_resampler_t *audio_resampler_create(const struct resample_info *dst, return NULL; } + if (rs->input_layout == AV_CH_LAYOUT_MONO && rs->output_ch > 1) { + const double matrix[MAX_AUDIO_CHANNELS][MAX_AUDIO_CHANNELS] = { + {1}, + {1, 1}, + {1, 1, 0}, + {1, 1, 1, 1}, + {1, 1, 1, 0, 1}, + {1, 1, 1, 1, 1, 1}, + {1, 1, 1, 0, 1, 1, 1}, + {1, 1, 1, 0, 1, 1, 1, 1}, + }; + if (swr_set_matrix(rs->context, matrix[rs->output_ch - 1], 1) < 0) + blog(LOG_DEBUG, "swr_set_matrix failed for mono upmix\n"); + } + errcode = swr_init(rs->context); if (errcode != 0) { blog(LOG_ERROR, "avresample_open failed: error code %d", diff --git a/libobs/media-io/video-io.c b/libobs/media-io/video-io.c index f490ceb..43227b7 100644 --- a/libobs/media-io/video-io.c +++ b/libobs/media-io/video-io.c @@ -65,8 +65,8 @@ struct video_output { os_sem_t *update_semaphore; uint64_t frame_time; - uint32_t skipped_frames; - uint32_t total_frames; + volatile long skipped_frames; + volatile long total_frames; bool initialized; @@ -77,6 +77,9 @@ struct video_output { size_t first_added; size_t last_added; struct cached_frame_info cache[MAX_CACHE_SIZE]; + + volatile bool raw_active; + volatile long gpu_refs; }; /* ------------------------------------------------------------------------- */ @@ -156,7 +159,7 @@ static inline bool video_output_cur_frame(struct video_output *video) video->last_added = video->first_added; } else if (skipped) { --frame_info->skipped; - ++video->skipped_frames; + os_atomic_inc_long(&video->skipped_frames); } pthread_mutex_unlock(&video->data_mutex); @@ -182,10 +185,10 @@ static void *video_thread(void *param) profile_start(video_thread_name); while (!video->stop && !video_output_cur_frame(video)) { - video->total_frames++; + os_atomic_inc_long(&video->total_frames); } - video->total_frames++; + os_atomic_inc_long(&video->total_frames); profile_end(video_thread_name); profile_reenable_thread(); @@ -330,6 +333,12 @@ static inline bool video_input_init(struct video_input *input, return true; } +static inline void reset_frames(video_t *video) +{ + os_atomic_set_long(&video->skipped_frames, 0); + os_atomic_set_long(&video->total_frames, 0); +} + bool video_output_connect(video_t *video, const struct video_scale_info *conversion, void (*callback)(void *param, struct video_data *frame), @@ -342,11 +351,6 @@ 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)); @@ -368,8 +372,15 @@ bool video_output_connect(video_t *video, input.conversion.height = video->info.height; success = video_input_init(&input, video); - if (success) + if (success) { + if (video->inputs.num == 0) { + if (!os_atomic_load_long(&video->gpu_refs)) { + reset_frames(video); + } + os_atomic_set_bool(&video->raw_active, true); + } da_push_back(video->inputs, &input); + } } pthread_mutex_unlock(&video->input_mutex); @@ -377,6 +388,24 @@ bool video_output_connect(video_t *video, return success; } +static void log_skipped(video_t *video) +{ + long skipped = os_atomic_load_long(&video->skipped_frames); + double percentage_skipped = + (double)skipped / + (double)os_atomic_load_long(&video->total_frames) * + 100.0; + + if (skipped) + blog(LOG_INFO, "Video stopped, number of " + "skipped frames due " + "to encoding lag: " + "%ld/%ld (%0.1f%%)", + video->skipped_frames, + video->total_frames, + percentage_skipped); +} + void video_output_disconnect(video_t *video, void (*callback)(void *param, struct video_data *frame), void *param) @@ -390,20 +419,13 @@ void video_output_disconnect(video_t *video, if (idx != DARRAY_INVALID) { video_input_free(video->inputs.array+idx); 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); + if (video->inputs.num == 0) { + os_atomic_set_bool(&video->raw_active, false); + if (!os_atomic_load_long(&video->gpu_refs)) { + log_skipped(video); + } + } } pthread_mutex_unlock(&video->input_mutex); @@ -412,7 +434,7 @@ void video_output_disconnect(video_t *video, bool video_output_active(const video_t *video) { if (!video) return false; - return video->inputs.num != 0; + return os_atomic_load_bool(&video->raw_active); } const struct video_output_info *video_output_get_info(const video_t *video) @@ -521,10 +543,42 @@ double video_output_get_frame_rate(const video_t *video) uint32_t video_output_get_skipped_frames(const video_t *video) { - return video->skipped_frames; + return (uint32_t)os_atomic_load_long(&video->skipped_frames); } uint32_t video_output_get_total_frames(const video_t *video) { - return video->total_frames; + return (uint32_t)os_atomic_load_long(&video->total_frames); +} + +/* Note: These four functions below are a very slight bit of a hack. If the + * texture encoder thread is active while the raw encoder thread is active, the + * total frame count will just be doubled while they're both active. Which is + * fine. What's more important is having a relatively accurate skipped frame + * count. */ + +void video_output_inc_texture_encoders(video_t *video) +{ + if (os_atomic_inc_long(&video->gpu_refs) == 1 && + !os_atomic_load_bool(&video->raw_active)) { + reset_frames(video); + } +} + +void video_output_dec_texture_encoders(video_t *video) +{ + if (os_atomic_dec_long(&video->gpu_refs) == 0 && + !os_atomic_load_bool(&video->raw_active)) { + log_skipped(video); + } +} + +void video_output_inc_texture_frames(video_t *video) +{ + os_atomic_inc_long(&video->total_frames); +} + +void video_output_inc_texture_skipped_frames(video_t *video) +{ + os_atomic_inc_long(&video->skipped_frames); } diff --git a/libobs/media-io/video-io.h b/libobs/media-io/video-io.h index 03ea20b..77acf8f 100644 --- a/libobs/media-io/video-io.h +++ b/libobs/media-io/video-io.h @@ -135,15 +135,23 @@ static inline const char *get_video_colorspace_name(enum video_colorspace cs) return "601"; } -static inline const char *get_video_range_name(enum video_range_type range) +static inline enum video_range_type resolve_video_range( + enum video_format format, enum video_range_type range) { - switch (range) { - case VIDEO_RANGE_FULL: return "Full"; - case VIDEO_RANGE_PARTIAL: - case VIDEO_RANGE_DEFAULT:; + if (range == VIDEO_RANGE_DEFAULT) { + range = format_is_yuv(format) + ? VIDEO_RANGE_PARTIAL + : VIDEO_RANGE_FULL; } - return "Partial"; + return range; +} + +static inline const char *get_video_range_name(enum video_format format, + enum video_range_type range) +{ + range = resolve_video_range(format, range); + return range == VIDEO_RANGE_FULL ? "Full" : "Partial"; } enum video_scale_type { @@ -202,6 +210,11 @@ EXPORT double video_output_get_frame_rate(const video_t *video); EXPORT uint32_t video_output_get_skipped_frames(const video_t *video); EXPORT uint32_t video_output_get_total_frames(const video_t *video); +extern void video_output_inc_texture_encoders(video_t *video); +extern void video_output_dec_texture_encoders(video_t *video); +extern void video_output_inc_texture_frames(video_t *video); +extern void video_output_inc_texture_skipped_frames(video_t *video); + #ifdef __cplusplus } diff --git a/libobs/obs-audio-controls.c b/libobs/obs-audio-controls.c index 0de00c7..2808165 100644 --- a/libobs/obs-audio-controls.c +++ b/libobs/obs-audio-controls.c @@ -932,3 +932,12 @@ void obs_volmeter_remove_callback(obs_volmeter_t *volmeter, pthread_mutex_unlock(&volmeter->callback_mutex); } +float obs_mul_to_db(float mul) +{ + return mul_to_db(mul); +} + +float obs_db_to_mul(float db) +{ + return db_to_mul(db); +} diff --git a/libobs/obs-audio-controls.h b/libobs/obs-audio-controls.h index 384d618..c16b32b 100644 --- a/libobs/obs-audio-controls.h +++ b/libobs/obs-audio-controls.h @@ -273,6 +273,9 @@ EXPORT void obs_volmeter_add_callback(obs_volmeter_t *volmeter, EXPORT void obs_volmeter_remove_callback(obs_volmeter_t *volmeter, obs_volmeter_updated_t callback, void *param); +EXPORT float obs_mul_to_db(float mul); +EXPORT float obs_db_to_mul(float db); + #ifdef __cplusplus } #endif diff --git a/libobs/obs-audio.c b/libobs/obs-audio.c index d644ea7..7bbcc20 100644 --- a/libobs/obs-audio.c +++ b/libobs/obs-audio.c @@ -31,8 +31,8 @@ static void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p) struct obs_core_audio *audio = p; if (da_find(audio->render_order, &source, 0) == DARRAY_INVALID) { - obs_source_addref(source); - da_push_back(audio->render_order, &source); + obs_source_t *s = obs_source_get_ref(source); + if (s) da_push_back(audio->render_order, &s); } UNUSED_PARAMETER(parent); @@ -227,7 +227,8 @@ static inline void discard_audio(struct obs_core_audio *audio, } static void add_audio_buffering(struct obs_core_audio *audio, - size_t sample_rate, struct ts_info *ts, uint64_t min_ts) + size_t sample_rate, struct ts_info *ts, uint64_t min_ts, + const char *buffering_name) { struct ts_info new_ts; uint64_t offset; @@ -259,8 +260,9 @@ static void add_audio_buffering(struct obs_core_audio *audio, sample_rate; blog(LOG_INFO, "adding %d milliseconds of audio buffering, total " - "audio buffering is now %d milliseconds", - (int)ms, (int)total_ms); + "audio buffering is now %d milliseconds" + " (source: %s)\n", + (int)ms, (int)total_ms, buffering_name); #if DEBUG_AUDIO == 1 blog(LOG_DEBUG, "min_ts (%"PRIu64") < start timestamp " "(%"PRIu64")", min_ts, ts->start); @@ -322,17 +324,21 @@ static bool audio_buffer_insuffient(struct obs_source *source, return false; } -static inline void find_min_ts(struct obs_core_data *data, +static inline const char *find_min_ts(struct obs_core_data *data, uint64_t *min_ts) { + obs_source_t *buffering_source = NULL; struct obs_source *source = data->first_audio_source; while (source) { if (!source->audio_pending && source->audio_ts && - source->audio_ts < *min_ts) + source->audio_ts < *min_ts) { *min_ts = source->audio_ts; + buffering_source = source; + } source = (struct obs_source*)source->next_audio_source; } + return buffering_source ? obs_source_get_name(buffering_source) : NULL; } static inline bool mark_invalid_sources(struct obs_core_data *data, @@ -350,12 +356,13 @@ static inline bool mark_invalid_sources(struct obs_core_data *data, return recalculate; } -static inline void calc_min_ts(struct obs_core_data *data, +static inline const char *calc_min_ts(struct obs_core_data *data, size_t sample_rate, uint64_t *min_ts) { - find_min_ts(data, min_ts); + const char *buffering_name = find_min_ts(data, min_ts); if (mark_invalid_sources(data, sample_rate, *min_ts)) - find_min_ts(data, min_ts); + buffering_name = find_min_ts(data, min_ts); + return buffering_name; } static inline void release_audio_sources(struct obs_core_audio *audio) @@ -425,13 +432,14 @@ bool audio_callback(void *param, /* ------------------------------------------------ */ /* get minimum audio timestamp */ pthread_mutex_lock(&data->audio_sources_mutex); - calc_min_ts(data, sample_rate, &min_ts); + const char *buffering_name = calc_min_ts(data, sample_rate, &min_ts); pthread_mutex_unlock(&data->audio_sources_mutex); /* ------------------------------------------------ */ /* if a source has gone backward in time, buffer */ if (min_ts < ts.start) - add_audio_buffering(audio, sample_rate, &ts, min_ts); + add_audio_buffering(audio, sample_rate, &ts, min_ts, + buffering_name); /* ------------------------------------------------ */ /* mix audio */ diff --git a/libobs/obs-config.h b/libobs/obs-config.h index b6d8dc8..c233ff0 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -27,21 +27,21 @@ /* * Increment if major breaking API changes */ -#define LIBOBS_API_MAJOR_VER 22 +#define LIBOBS_API_MAJOR_VER 23 /* * Increment if backward-compatible additions * * Reset to zero each major version */ -#define LIBOBS_API_MINOR_VER 0 +#define LIBOBS_API_MINOR_VER 2 /* * Increment if backward-compatible bug fix * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 3 +#define LIBOBS_API_PATCH_VER 1 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | \ diff --git a/libobs/obs-defs.h b/libobs/obs-defs.h index 822111a..694b11c 100644 --- a/libobs/obs-defs.h +++ b/libobs/obs-defs.h @@ -40,6 +40,7 @@ #define OBS_OUTPUT_DISCONNECTED -5 #define OBS_OUTPUT_UNSUPPORTED -6 #define OBS_OUTPUT_NO_SPACE -7 +#define OBS_OUTPUT_ENCODE_ERROR -8 #define OBS_VIDEO_SUCCESS 0 #define OBS_VIDEO_FAIL -1 diff --git a/libobs/obs-display.c b/libobs/obs-display.c index f5d01ab..5244863 100644 --- a/libobs/obs-display.c +++ b/libobs/obs-display.c @@ -46,17 +46,19 @@ bool obs_display_init(struct obs_display *display, return false; } - display->background_color = 0x4C4C4C; display->enabled = true; return true; } -obs_display_t *obs_display_create(const struct gs_init_data *graphics_data) +obs_display_t *obs_display_create(const struct gs_init_data *graphics_data, + uint32_t background_color) { struct obs_display *display = bzalloc(sizeof(struct obs_display)); gs_enter_context(obs->video.graphics); + display->background_color = background_color; + if (!obs_display_init(display, graphics_data)) { obs_display_destroy(display); display = NULL; @@ -173,7 +175,6 @@ static inline void render_display_begin(struct obs_display *display, static inline void render_display_end() { gs_end_scene(); - gs_present(); } void render_display(struct obs_display *display) @@ -183,6 +184,8 @@ void render_display(struct obs_display *display) if (!display || !display->enabled) return; + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DISPLAY, "obs_display"); + /* -------------------------------------------- */ pthread_mutex_lock(&display->draw_info_mutex); @@ -212,6 +215,10 @@ void render_display(struct obs_display *display) pthread_mutex_unlock(&display->draw_callbacks_mutex); render_display_end(); + + GS_DEBUG_MARKER_END(); + + gs_present(); } void obs_display_set_enabled(obs_display_t *display, bool enable) @@ -230,3 +237,19 @@ void obs_display_set_background_color(obs_display_t *display, uint32_t color) if (display) display->background_color = color; } + +void obs_display_size(obs_display_t *display, + uint32_t *width, uint32_t *height) +{ + *width = 0; + *height = 0; + + if (display) { + pthread_mutex_lock(&display->draw_info_mutex); + + *width = display->cx; + *height = display->cy; + + pthread_mutex_unlock(&display->draw_info_mutex); + } +} diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 83b2d7f..cede06a 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -64,8 +64,8 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name, if (pthread_mutex_init(&encoder->outputs_mutex, NULL) != 0) return false; - if (encoder->info.get_defaults) - encoder->info.get_defaults(encoder->context.settings); + if (encoder->orig_info.get_defaults) + encoder->orig_info.get_defaults(encoder->context.settings); return true; } @@ -90,8 +90,10 @@ static struct obs_encoder *create_encoder(const char *id, encoder->info.id = bstrdup(id); encoder->info.type = type; encoder->owns_info_id = true; + encoder->orig_info = encoder->info; } else { encoder->info = *ei; + encoder->orig_info = *ei; } success = init_encoder(encoder, name, settings, hotkey_data); @@ -177,6 +179,12 @@ static inline bool has_scaling(const struct obs_encoder *encoder) video_height != encoder->scaled_height); } +static inline bool gpu_encode_available(const struct obs_encoder *encoder) +{ + return (encoder->info.caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0 && + obs->video.using_nv12_tex; +} + static void add_connection(struct obs_encoder *encoder) { if (encoder->info.type == OBS_ENCODER_AUDIO) { @@ -189,21 +197,37 @@ static void add_connection(struct obs_encoder *encoder) struct video_scale_info info = {0}; get_video_info(encoder, &info); - start_raw_video(encoder->media, &info, receive_video, encoder); + if (gpu_encode_available(encoder)) { + start_gpu_encode(encoder); + } else { + start_raw_video(encoder->media, &info, receive_video, + encoder); + } } set_encoder_active(encoder, true); } -static void remove_connection(struct obs_encoder *encoder) +static void remove_connection(struct obs_encoder *encoder, bool shutdown) { - if (encoder->info.type == OBS_ENCODER_AUDIO) + if (encoder->info.type == OBS_ENCODER_AUDIO) { audio_output_disconnect(encoder->media, encoder->mixer_idx, receive_audio, encoder); - else - stop_raw_video(encoder->media, receive_video, encoder); + } else { + if (gpu_encode_available(encoder)) { + stop_gpu_encode(encoder); + } else { + stop_raw_video(encoder->media, receive_video, encoder); + } + } - obs_encoder_shutdown(encoder); + /* obs_encoder_shutdown locks init_mutex, so don't call it on encode + * errors, otherwise you can get a deadlock with outputs when they end + * data capture, which will lock init_mutex and the video callback + * mutex in the reverse order. instead, call shutdown before starting + * up again */ + if (shutdown) + obs_encoder_shutdown(encoder); set_encoder_active(encoder, false); } @@ -253,11 +277,13 @@ void obs_encoder_destroy(obs_encoder_t *encoder) obs_context_data_remove(&encoder->context); + pthread_mutex_lock(&encoder->init_mutex); pthread_mutex_lock(&encoder->callbacks_mutex); destroy = encoder->callbacks.num == 0; if (!destroy) encoder->destroy_on_stop = true; pthread_mutex_unlock(&encoder->callbacks_mutex); + pthread_mutex_unlock(&encoder->init_mutex); if (destroy) obs_encoder_actually_destroy(encoder); @@ -282,8 +308,11 @@ void obs_encoder_set_name(obs_encoder_t *encoder, const char *name) static inline obs_data_t *get_defaults(const struct obs_encoder_info *info) { obs_data_t *settings = obs_data_create(); - if (info->get_defaults) + if (info->get_defaults2) { + info->get_defaults2(settings, info->type_data); + } else if (info->get_defaults) { info->get_defaults(settings); + } return settings; } @@ -304,11 +333,16 @@ obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder) obs_properties_t *obs_get_encoder_properties(const char *id) { const struct obs_encoder_info *ei = find_encoder(id); - if (ei && ei->get_properties) { + if (ei && (ei->get_properties || ei->get_properties2)) { obs_data_t *defaults = get_defaults(ei); obs_properties_t *properties; - properties = ei->get_properties(NULL); + if (ei->get_properties2) { + properties = ei->get_properties2(NULL, ei->type_data); + } else if (ei->get_properties) { + properties = ei->get_properties(NULL); + } + obs_properties_apply_settings(properties, defaults); obs_data_release(defaults); return properties; @@ -321,12 +355,21 @@ obs_properties_t *obs_encoder_properties(const obs_encoder_t *encoder) if (!obs_encoder_valid(encoder, "obs_encoder_properties")) return NULL; - if (encoder->info.get_properties) { + if (encoder->orig_info.get_properties2) { obs_properties_t *props; - props = encoder->info.get_properties(encoder->context.data); + props = encoder->orig_info.get_properties2( + encoder->context.data, + encoder->orig_info.type_data); + obs_properties_apply_settings(props, encoder->context.settings); + return props; + + } else if (encoder->orig_info.get_properties) { + obs_properties_t *props; + props = encoder->orig_info.get_properties(encoder->context.data); obs_properties_apply_settings(props, encoder->context.settings); return props; } + return NULL; } @@ -388,6 +431,8 @@ static void intitialize_audio_encoder(struct obs_encoder *encoder) reset_audio_buffers(encoder); } +static THREAD_LOCAL bool can_reroute = false; + static inline bool obs_encoder_initialize_internal(obs_encoder_t *encoder) { if (encoder_active(encoder)) @@ -397,19 +442,45 @@ static inline bool obs_encoder_initialize_internal(obs_encoder_t *encoder) obs_encoder_shutdown(encoder); - if (encoder->info.create) - encoder->context.data = encoder->info.create( + if (encoder->orig_info.create) { + can_reroute = true; + encoder->info = encoder->orig_info; + encoder->context.data = encoder->orig_info.create( encoder->context.settings, encoder); + can_reroute = false; + } if (!encoder->context.data) return false; - if (encoder->info.type == OBS_ENCODER_AUDIO) + if (encoder->orig_info.type == OBS_ENCODER_AUDIO) intitialize_audio_encoder(encoder); encoder->initialized = true; return true; } +void *obs_encoder_create_rerouted(obs_encoder_t *encoder, const char *reroute_id) +{ + if (!obs_ptr_valid(encoder, "obs_encoder_reroute")) + return NULL; + if (!obs_ptr_valid(reroute_id, "obs_encoder_reroute")) + return NULL; + if (!can_reroute) + return NULL; + + const struct obs_encoder_info *ei = find_encoder(reroute_id); + if (ei) { + if (ei->type != encoder->orig_info.type || + astrcmpi(ei->codec, encoder->orig_info.codec) != 0) { + return NULL; + } + encoder->info = *ei; + return encoder->info.create(encoder->context.settings, encoder); + } + + return NULL; +} + bool obs_encoder_initialize(obs_encoder_t *encoder) { bool success; @@ -510,7 +581,7 @@ static inline bool obs_encoder_stop_internal(obs_encoder_t *encoder, pthread_mutex_unlock(&encoder->callbacks_mutex); if (last) { - remove_connection(encoder); + remove_connection(encoder, true); encoder->initialized = false; if (encoder->destroy_on_stop) { @@ -766,19 +837,65 @@ static inline void send_packet(struct obs_encoder *encoder, cb->new_packet(cb->param, packet); } -static void full_stop(struct obs_encoder *encoder) +void full_stop(struct obs_encoder *encoder) { if (encoder) { + pthread_mutex_lock(&encoder->outputs_mutex); + for (size_t i = 0; i < encoder->outputs.num; i++) { + struct obs_output *output = encoder->outputs.array[i]; + obs_output_force_stop(output); + + pthread_mutex_lock(&output->interleaved_mutex); + output->info.encoded_packet(output->context.data, NULL); + pthread_mutex_unlock(&output->interleaved_mutex); + } + pthread_mutex_unlock(&encoder->outputs_mutex); + pthread_mutex_lock(&encoder->callbacks_mutex); da_free(encoder->callbacks); - remove_connection(encoder); + pthread_mutex_unlock(&encoder->callbacks_mutex); + + remove_connection(encoder, false); + encoder->initialized = false; + } +} + +void send_off_encoder_packet(obs_encoder_t *encoder, bool success, + bool received, struct encoder_packet *pkt) +{ + if (!success) { + blog(LOG_ERROR, "Error encoding with encoder '%s'", + encoder->context.name); + full_stop(encoder); + return; + } + + if (received) { + if (!encoder->first_received) { + encoder->offset_usec = packet_dts_usec(pkt); + encoder->first_received = true; + } + + /* we use system time here to ensure sync with other encoders, + * you do not want to use relative timestamps here */ + pkt->dts_usec = encoder->start_ts / 1000 + + packet_dts_usec(pkt) - encoder->offset_usec; + pkt->sys_dts_usec = pkt->dts_usec; + + pthread_mutex_lock(&encoder->callbacks_mutex); + + for (size_t i = encoder->callbacks.num; i > 0; i--) { + struct encoder_callback *cb; + cb = encoder->callbacks.array+(i-1); + send_packet(encoder, cb, pkt); + } + pthread_mutex_unlock(&encoder->callbacks_mutex); } } static const char *do_encode_name = "do_encode"; -static inline void do_encode(struct obs_encoder *encoder, - struct encoder_frame *frame) +bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame) { profile_start(do_encode_name); if (!encoder->profile_encoder_encode_name) @@ -798,38 +915,11 @@ static inline void do_encode(struct obs_encoder *encoder, success = encoder->info.encode(encoder->context.data, frame, &pkt, &received); profile_end(encoder->profile_encoder_encode_name); - if (!success) { - full_stop(encoder); - blog(LOG_ERROR, "Error encoding with encoder '%s'", - encoder->context.name); - goto error; - } + send_off_encoder_packet(encoder, success, received, &pkt); - if (received) { - if (!encoder->first_received) { - encoder->offset_usec = packet_dts_usec(&pkt); - encoder->first_received = true; - } - - /* we use system time here to ensure sync with other encoders, - * you do not want to use relative timestamps here */ - pkt.dts_usec = encoder->start_ts / 1000 + - packet_dts_usec(&pkt) - encoder->offset_usec; - pkt.sys_dts_usec = pkt.dts_usec; - - pthread_mutex_lock(&encoder->callbacks_mutex); - - for (size_t i = encoder->callbacks.num; i > 0; i--) { - struct encoder_callback *cb; - cb = encoder->callbacks.array+(i-1); - send_packet(encoder, cb, &pkt); - } - - pthread_mutex_unlock(&encoder->callbacks_mutex); - } - -error: profile_end(do_encode_name); + + return success; } static const char *receive_video_name = "receive_video"; @@ -861,9 +951,8 @@ static void receive_video(void *param, struct video_data *frame) enc_frame.frames = 1; enc_frame.pts = encoder->cur_pts; - do_encode(encoder, &enc_frame); - - encoder->cur_pts += encoder->timebase_num; + if (do_encode(encoder, &enc_frame)) + encoder->cur_pts += encoder->timebase_num; wait_for_audio: profile_end(receive_video_name); @@ -971,7 +1060,7 @@ fail: return success; } -static void send_audio_data(struct obs_encoder *encoder) +static bool send_audio_data(struct obs_encoder *encoder) { struct encoder_frame enc_frame; @@ -989,9 +1078,11 @@ static void send_audio_data(struct obs_encoder *encoder) enc_frame.frames = (uint32_t)encoder->framesize; enc_frame.pts = encoder->cur_pts; - do_encode(encoder, &enc_frame); + if (!do_encode(encoder, &enc_frame)) + return false; encoder->cur_pts += encoder->framesize; + return true; } static const char *receive_audio_name = "receive_audio"; @@ -1010,8 +1101,11 @@ static void receive_audio(void *param, size_t mix_idx, struct audio_data *data) if (!buffer_audio(encoder, data)) goto end; - while (encoder->audio_input_buffer[0].size >= encoder->framesize_bytes) - send_audio_data(encoder); + while (encoder->audio_input_buffer[0].size >= encoder->framesize_bytes) { + if (!send_audio_data(encoder)) { + break; + } + } UNUSED_PARAMETER(mix_idx); @@ -1186,13 +1280,13 @@ bool obs_weak_encoder_references_encoder(obs_weak_encoder_t *weak, void *obs_encoder_get_type_data(obs_encoder_t *encoder) { return obs_encoder_valid(encoder, "obs_encoder_get_type_data") - ? encoder->info.type_data : NULL; + ? encoder->orig_info.type_data : NULL; } const char *obs_encoder_get_id(const obs_encoder_t *encoder) { return obs_encoder_valid(encoder, "obs_encoder_get_id") - ? encoder->info.id : NULL; + ? encoder->orig_info.id : NULL; } uint32_t obs_get_encoder_caps(const char *encoder_id) @@ -1200,3 +1294,9 @@ uint32_t obs_get_encoder_caps(const char *encoder_id) struct obs_encoder_info *info = find_encoder(encoder_id); return info ? info->caps : 0; } + +uint32_t obs_encoder_get_caps(const obs_encoder_t *encoder) +{ + return obs_encoder_valid(encoder, "obs_encoder_get_caps") + ? encoder->orig_info.caps : 0; +} diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h index 2220c5a..3264423 100644 --- a/libobs/obs-encoder.h +++ b/libobs/obs-encoder.h @@ -30,6 +30,7 @@ extern "C" { #endif #define OBS_ENCODER_CAP_DEPRECATED (1<<0) +#define OBS_ENCODER_CAP_PASS_TEXTURE (1<<1) /** Specifies the encoder type */ enum obs_encoder_type { @@ -234,6 +235,27 @@ struct obs_encoder_info { void (*free_type_data)(void *type_data); uint32_t caps; + + /** + * Gets the default settings for this encoder + * + * @param[out] settings Data to assign default settings to + * @param[in] typedata Type Data + */ + void (*get_defaults2)(obs_data_t *settings, void *type_data); + + /** + * Gets the property information of this encoder + * + * @param[in] data Pointer from create (or null) + * @param[in] typedata Type Data + * @return The properties data + */ + obs_properties_t *(*get_properties2)(void *data, void *type_data); + + bool (*encode_texture)(void *data, uint32_t handle, int64_t pts, + uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, bool *received_packet); }; EXPORT void obs_register_encoder_s(const struct obs_encoder_info *info, diff --git a/libobs/obs-hotkey.c b/libobs/obs-hotkey.c index b150d29..d31076e 100644 --- a/libobs/obs-hotkey.c +++ b/libobs/obs-hotkey.c @@ -1479,6 +1479,7 @@ void obs_hotkeys_set_translations_s( ADD_TRANSLATION(OBS_KEY_META, meta); ADD_TRANSLATION(OBS_KEY_MENU, menu); ADD_TRANSLATION(OBS_KEY_SPACE, space); + ADD_TRANSLATION(OBS_KEY_ESCAPE, escape); #ifdef __APPLE__ const char *numpad_str = t.apple_keypad_num; ADD_TRANSLATION(OBS_KEY_NUMSLASH, apple_keypad_divide); diff --git a/libobs/obs-hotkey.h b/libobs/obs-hotkey.h index 4e4a86a..74a047f 100644 --- a/libobs/obs-hotkey.h +++ b/libobs/obs-hotkey.h @@ -126,6 +126,7 @@ struct obs_hotkeys_translations { const char *apple_keypad_decimal; const char *apple_keypad_equal; const char *mouse_num; /* For example, "Mouse %1" */ + const char *escape; }; /* This function is an optional way to provide translations for specific keys diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index b317fd9..717360f 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -38,6 +38,8 @@ #define NUM_TEXTURES 2 #define MICROSECOND_DEN 1000000 +#define NUM_ENCODE_TEXTURES 3 +#define NUM_ENCODE_TEXTURE_FRAMES_TO_WAIT 1 static inline int64_t packet_dts_usec(struct encoder_packet *packet) { @@ -225,30 +227,55 @@ struct obs_vframe_info { int count; }; +struct obs_tex_frame { + gs_texture_t *tex; + gs_texture_t *tex_uv; + uint32_t handle; + uint64_t timestamp; + uint64_t lock_key; + int count; + bool released; +}; + struct obs_core_video { graphics_t *graphics; gs_stagesurf_t *copy_surfaces[NUM_TEXTURES]; gs_texture_t *render_textures[NUM_TEXTURES]; gs_texture_t *output_textures[NUM_TEXTURES]; gs_texture_t *convert_textures[NUM_TEXTURES]; + gs_texture_t *convert_uv_textures[NUM_TEXTURES]; bool textures_rendered[NUM_TEXTURES]; bool textures_output[NUM_TEXTURES]; bool textures_copied[NUM_TEXTURES]; bool textures_converted[NUM_TEXTURES]; + bool using_nv12_tex; struct circlebuf vframe_info_buffer; + struct circlebuf vframe_info_buffer_gpu; gs_effect_t *default_effect; gs_effect_t *default_rect_effect; gs_effect_t *opaque_effect; gs_effect_t *solid_effect; + gs_effect_t *repeat_effect; gs_effect_t *conversion_effect; gs_effect_t *bicubic_effect; gs_effect_t *lanczos_effect; + gs_effect_t *area_effect; gs_effect_t *bilinear_lowres_effect; gs_effect_t *premultiplied_alpha_effect; gs_samplerstate_t *point_sampler; gs_stagesurf_t *mapped_surface; int cur_texture; long raw_active; + long gpu_encoder_active; + pthread_mutex_t gpu_encoder_mutex; + struct circlebuf gpu_encoder_queue; + struct circlebuf gpu_encoder_avail_queue; + DARRAY(obs_encoder_t *) gpu_encoders; + os_sem_t *gpu_encode_semaphore; + os_event_t *gpu_encode_inactive; + pthread_t gpu_encode_thread; + bool gpu_encode_thread_initialized; + volatile bool gpu_encode_stop; uint64_t video_time; uint64_t video_avg_frame_time_ns; @@ -608,6 +635,7 @@ struct obs_source { float volume; int64_t sync_offset; int64_t last_sync_offset; + float balance; /* async video data */ gs_texture_t *async_texture; @@ -615,12 +643,10 @@ struct obs_source { struct obs_source_frame *cur_async_frame; bool async_gpu_conversion; enum video_format async_format; - enum video_format async_cache_format; - enum gs_color_format async_texture_format; - float async_color_matrix[16]; bool async_full_range; - float async_color_range_min[3]; - float async_color_range_max[3]; + enum video_format async_cache_format; + bool async_cache_full_range; + enum gs_color_format async_texture_format; int async_plane_offset[2]; bool async_flip; bool async_active; @@ -816,6 +842,7 @@ struct obs_weak_output { #define CAPTION_LINE_BYTES (4*CAPTION_LINE_CHARS) struct caption_text { char text[CAPTION_LINE_BYTES+1]; + double display_duration; struct caption_text *next; }; @@ -862,7 +889,7 @@ struct obs_output { obs_encoder_t *video_encoder; obs_encoder_t *audio_encoders[MAX_AUDIO_MIXES]; obs_service_t *service; - size_t mixer_idx; + size_t mixer_mask; uint32_t scaled_width; uint32_t scaled_height; @@ -939,6 +966,9 @@ struct obs_encoder { struct obs_encoder_info info; struct obs_weak_encoder *control; + /* allows re-routing to another encoder */ + struct obs_encoder_info orig_info; + pthread_mutex_t init_mutex; uint32_t samplerate; @@ -1009,6 +1039,13 @@ extern void obs_encoder_add_output(struct obs_encoder *encoder, extern void obs_encoder_remove_output(struct obs_encoder *encoder, struct obs_output *output); +extern bool start_gpu_encode(obs_encoder_t *encoder); +extern void stop_gpu_encode(obs_encoder_t *encoder); + +extern bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame); +extern void send_off_encoder_packet(obs_encoder_t *encoder, bool success, + bool received, struct encoder_packet *pkt); + void obs_encoder_destroy(obs_encoder_t *encoder); /* ------------------------------------------------------------------------- */ diff --git a/libobs/obs-module.c b/libobs/obs-module.c index 4136965..38276f8 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -26,7 +26,7 @@ extern const char *get_module_extension(void); static inline int req_func_not_found(const char *name, const char *path) { - blog(LOG_ERROR, "Required module function '%s' in module '%s' not " + blog(LOG_DEBUG, "Required module function '%s' in module '%s' not " "found, loading of module failed", name, path); return MODULE_MISSING_EXPORTS; @@ -675,9 +675,15 @@ void obs_register_output_s(const struct obs_output_info *info, size_t size) CHECK_REQUIRED_VAL_(info, raw_video, obs_register_output); - if (info->flags & OBS_OUTPUT_AUDIO) - CHECK_REQUIRED_VAL_(info, raw_audio, - obs_register_output); + if (info->flags & OBS_OUTPUT_AUDIO) { + if (info->flags & OBS_OUTPUT_MULTI_TRACK) { + CHECK_REQUIRED_VAL_(info, raw_audio2, + obs_register_output); + } else { + CHECK_REQUIRED_VAL_(info, raw_audio, + obs_register_output); + } + } } #undef CHECK_REQUIRED_VAL_ @@ -701,7 +707,11 @@ void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size) CHECK_REQUIRED_VAL_(info, get_name, obs_register_encoder); CHECK_REQUIRED_VAL_(info, create, obs_register_encoder); CHECK_REQUIRED_VAL_(info, destroy, obs_register_encoder); - CHECK_REQUIRED_VAL_(info, encode, obs_register_encoder); + + if ((info->caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0) + CHECK_REQUIRED_VAL_(info, encode_texture, obs_register_encoder); + else + CHECK_REQUIRED_VAL_(info, encode, obs_register_encoder); if (info->type == OBS_ENCODER_AUDIO) CHECK_REQUIRED_VAL_(info, get_frame_size, obs_register_encoder); diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c index 355b832..f7ce93d 100644 --- a/libobs/obs-nix.c +++ b/libobs/obs-nix.c @@ -422,6 +422,17 @@ static int get_keysym(obs_key_t key) case OBS_KEY_F22: return XK_F22; case OBS_KEY_F23: return XK_F23; case OBS_KEY_F24: return XK_F24; + case OBS_KEY_F25: return XK_F25; + case OBS_KEY_F26: return XK_F26; + case OBS_KEY_F27: return XK_F27; + case OBS_KEY_F28: return XK_F28; + case OBS_KEY_F29: return XK_F29; + case OBS_KEY_F30: return XK_F30; + case OBS_KEY_F31: return XK_F31; + case OBS_KEY_F32: return XK_F32; + case OBS_KEY_F33: return XK_F33; + case OBS_KEY_F34: return XK_F34; + case OBS_KEY_F35: return XK_F35; case OBS_KEY_MENU: return XK_Menu; case OBS_KEY_HYPER_L: return XK_Hyper_L; @@ -1114,6 +1125,8 @@ void obs_key_to_str(obs_key_t key, struct dstr *dstr) case OBS_KEY_NUMCOMMA: return translate_key(key, "Numpad ,"); case OBS_KEY_NUMPERIOD: return translate_key(key, "Numpad ."); case OBS_KEY_NUMSLASH: return translate_key(key, "Numpad /"); + case OBS_KEY_SPACE: return translate_key(key, "Space"); + case OBS_KEY_ESCAPE: return translate_key(key, "Escape"); default:; } diff --git a/libobs/obs-output.c b/libobs/obs-output.c index f82382f..b8fe903 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -255,13 +255,17 @@ bool obs_output_actual_start(obs_output_t *output) bool obs_output_start(obs_output_t *output) { bool encoded; + bool has_service; if (!obs_output_valid(output, "obs_output_start")) return false; if (!output->context.data) return false; - encoded = (output->info.flags & OBS_OUTPUT_ENCODED) != 0; + has_service = (output->info.flags & OBS_OUTPUT_SERVICE) != 0; + if (has_service && !obs_service_initialize(output->service, output)) + return false; + encoded = (output->info.flags & OBS_OUTPUT_ENCODED) != 0; if (encoded && output->delay_sec) { return obs_output_delay_start(output); } else { @@ -544,19 +548,46 @@ audio_t *obs_output_audio(const obs_output_t *output) output->audio : NULL; } +static inline size_t get_first_mixer(const obs_output_t *output) +{ + for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) { + if ((((size_t)1 << i) & output->mixer_mask) != 0) { + return i; + } + } + + return 0; +} + void obs_output_set_mixer(obs_output_t *output, size_t mixer_idx) { if (!obs_output_valid(output, "obs_output_set_mixer")) return; if (!active(output)) - output->mixer_idx = mixer_idx; + output->mixer_mask = (size_t)1 << mixer_idx; } size_t obs_output_get_mixer(const obs_output_t *output) { - return obs_output_valid(output, "obs_output_get_mixer") ? - output->mixer_idx : 0; + if (!obs_output_valid(output, "obs_output_get_mixer")) + return 0; + + return get_first_mixer(output); +} + +void obs_output_set_mixers(obs_output_t *output, size_t mixers) +{ + if (!obs_output_valid(output, "obs_output_set_mixers")) + return; + + output->mixer_mask = mixers; +} + +size_t obs_output_get_mixers(const obs_output_t *output) +{ + return obs_output_valid(output, "obs_output_get_mixers") ? + output->mixer_mask : 0; } void obs_output_remove_encoder(struct obs_output *output, @@ -1043,17 +1074,18 @@ static inline void send_interleaved(struct obs_output *output) double frame_timestamp = (out.pts * out.timebase_num) / (double)out.timebase_den; - /* TODO if output->caption_timestamp is more than 5 seconds - * old, send empty frame */ if (output->caption_head && output->caption_timestamp <= frame_timestamp) { - blog(LOG_INFO,"Sending caption: %f \"%s\"", + blog(LOG_DEBUG,"Sending caption: %f \"%s\"", frame_timestamp, &output->caption_head->text[0]); + double display_duration = + output->caption_head->display_duration; + if (add_caption(output, &out)) { output->caption_timestamp = - frame_timestamp + 2.0; + frame_timestamp + display_duration; } } @@ -1476,6 +1508,7 @@ static void default_raw_video_callback(void *param, struct video_data *frame) output->total_frames++; } + static void default_raw_audio_callback(void *param, size_t mix_idx, struct audio_data *frames) { @@ -1483,9 +1516,10 @@ static void default_raw_audio_callback(void *param, size_t mix_idx, if (!data_active(output)) return; - output->info.raw_audio(output->context.data, frames); - - UNUSED_PARAMETER(mix_idx); + if (output->info.raw_audio2) + output->info.raw_audio2(output->context.data, mix_idx, frames); + else + output->info.raw_audio(output->context.data, frames); } static inline void start_audio_encoders(struct obs_output *output, @@ -1499,6 +1533,25 @@ static inline void start_audio_encoders(struct obs_output *output, } } +static inline void start_raw_audio(obs_output_t *output) +{ + if (output->info.raw_audio2) { + for (int idx = 0; idx < MAX_AUDIO_MIXES; idx++) { + if ((output->mixer_mask & ((size_t)1 << idx)) != 0) { + audio_output_connect(output->audio, idx, + get_audio_conversion(output), + default_raw_audio_callback, + output); + } + } + } else { + audio_output_connect(output->audio, get_first_mixer(output), + get_audio_conversion(output), + default_raw_audio_callback, + output); + } +} + static void reset_packet_data(obs_output_t *output) { output->received_audio = false; @@ -1508,7 +1561,7 @@ static void reset_packet_data(obs_output_t *output) output->video_offset = 0; for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) - output->audio_offsets[0] = 0; + output->audio_offsets[i] = 0; free_packets(output); } @@ -1557,9 +1610,7 @@ static void hook_data_capture(struct obs_output *output, bool encoded, get_video_conversion(output), default_raw_video_callback, output); if (has_audio) - audio_output_connect(output->audio, output->mixer_idx, - get_audio_conversion(output), - default_raw_audio_callback, output); + start_raw_audio(output); } } @@ -1653,7 +1704,7 @@ static inline obs_encoder_t *find_inactive_audio_encoder(obs_output_t *output, for (size_t i = 0; i < num_mixes; i++) { struct obs_encoder *audio = output->audio_encoders[i]; - if (!audio->active && !audio->paired_encoder) + if (audio && !audio->active && !audio->paired_encoder) return audio; } @@ -1700,16 +1751,11 @@ bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags) if (!encoded) return false; - if (has_service && !obs_service_initialize(output->service, output)) - return false; if (has_video && !obs_encoder_initialize(output->video_encoder)) return false; if (has_audio && !initialize_audio_encoders(output, num_mixes)) return false; - if (has_video && has_audio) - pair_encoders(output, num_mixes); - return true; } @@ -1736,6 +1782,7 @@ static bool begin_delayed_capture(obs_output_t *output) bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags) { bool encoded, has_video, has_audio, has_service; + size_t num_mixes; if (!obs_output_valid(output, "obs_output_begin_data_capture")) return false; @@ -1752,6 +1799,10 @@ bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags) has_service)) return false; + num_mixes = num_audio_mixes(output); + if (has_video && has_audio) + pair_encoders(output, num_mixes); + os_atomic_set_bool(&output->data_active, true); hook_data_capture(output, encoded, has_video, has_audio); @@ -1786,6 +1837,25 @@ static inline void stop_audio_encoders(obs_output_t *output, } } +static inline void stop_raw_audio(obs_output_t *output) +{ + if (output->info.raw_audio2) { + for (int idx = 0; idx < MAX_AUDIO_MIXES; idx++) { + if ((output->mixer_mask & ((size_t)1 << idx)) != 0) { + audio_output_disconnect(output->audio, + idx, + default_raw_audio_callback, + output); + } + } + } else { + audio_output_disconnect(output->audio, + get_first_mixer(output), + default_raw_audio_callback, + output); + } +} + static void *end_data_capture_thread(void *data) { bool encoded, has_video, has_audio, has_service; @@ -1812,9 +1882,7 @@ static void *end_data_capture_thread(void *data) stop_raw_video(output->video, default_raw_video_callback, output); if (has_audio) - audio_output_disconnect(output->audio, - output->mixer_idx, - default_raw_audio_callback, output); + stop_raw_audio(output); } if (has_service) @@ -2070,11 +2138,13 @@ const char *obs_output_get_id(const obs_output_t *output) #if BUILD_CAPTIONS static struct caption_text *caption_text_new(const char *text, size_t bytes, - struct caption_text *tail, struct caption_text **head) + struct caption_text *tail, struct caption_text **head, + double display_duration) { struct caption_text *next = bzalloc(sizeof(struct caption_text)); snprintf(&next->text[0], CAPTION_LINE_BYTES + 1, "%.*s", (int)bytes, text); + next->display_duration = display_duration; if (!*head) { *head = next; @@ -2089,6 +2159,14 @@ void obs_output_output_caption_text1(obs_output_t *output, const char *text) { if (!obs_output_valid(output, "obs_output_output_caption_text1")) return; + obs_output_output_caption_text2(output, text, 2.0f); +} + +void obs_output_output_caption_text2(obs_output_t *output, const char *text, + double display_duration) +{ + if (!obs_output_valid(output, "obs_output_output_caption_text2")) + return; if (!active(output)) return; @@ -2101,7 +2179,8 @@ void obs_output_output_caption_text1(obs_output_t *output, const char *text) output->caption_tail = caption_text_new( text, size, output->caption_tail, - &output->caption_head); + &output->caption_head, + display_duration); pthread_mutex_unlock(&output->caption_mutex); } diff --git a/libobs/obs-output.h b/libobs/obs-output.h index df50fa0..db30fe0 100644 --- a/libobs/obs-output.h +++ b/libobs/obs-output.h @@ -71,6 +71,9 @@ struct obs_output_info { /* only used with encoded outputs, separated with semicolon */ const char *encoded_video_codecs; const char *encoded_audio_codecs; + + /* raw audio callback for multi track outputs */ + void (*raw_audio2)(void *data, size_t idx, struct audio_data *frames); }; EXPORT void obs_register_output_s(const struct obs_output_info *info, diff --git a/libobs/obs-properties.c b/libobs/obs-properties.c index 615882b..c8fcbe5 100644 --- a/libobs/obs-properties.c +++ b/libobs/obs-properties.c @@ -27,11 +27,13 @@ static inline void *get_property_data(struct obs_property *prop); struct float_data { double min, max, step; enum obs_number_type type; + char *suffix; }; struct int_data { int min, max, step; enum obs_number_type type; + char *suffix; }; struct list_item { @@ -86,6 +88,11 @@ struct frame_rate_data { DARRAY(struct frame_rate_range) ranges; }; +struct group_data { + enum obs_group_type type; + obs_properties_t *content; +}; + static inline void path_data_free(struct path_data *data) { bfree(data->default_path); @@ -140,6 +147,20 @@ static inline void frame_rate_data_free(struct frame_rate_data *data) da_free(data->ranges); } +static inline void group_data_free(struct group_data *data) { + obs_properties_destroy(data->content); +} + +static inline void int_data_free(struct int_data *data) { + if (data->suffix) + bfree(data->suffix); +} + +static inline void float_data_free(struct float_data *data) { + if (data->suffix) + bfree(data->suffix); +} + struct obs_properties; struct obs_property { @@ -166,6 +187,7 @@ struct obs_properties { struct obs_property *first_property; struct obs_property **last; + struct obs_property *parent; }; obs_properties_t *obs_properties_create(void) @@ -223,6 +245,12 @@ static void obs_property_destroy(struct obs_property *property) editable_list_data_free(get_property_data(property)); else if (property->type == OBS_PROPERTY_FRAME_RATE) frame_rate_data_free(get_property_data(property)); + else if (property->type == OBS_PROPERTY_GROUP) + group_data_free(get_property_data(property)); + else if (property->type == OBS_PROPERTY_INT) + int_data_free(get_property_data(property)); + else if (property->type == OBS_PROPERTY_FLOAT) + float_data_free(get_property_data(property)); bfree(property->name); bfree(property->desc); @@ -265,12 +293,51 @@ obs_property_t *obs_properties_get(obs_properties_t *props, const char *name) if (strcmp(property->name, name) == 0) return property; + if (property->type == OBS_PROPERTY_GROUP) { + obs_properties_t *group = + obs_property_group_content(property); + obs_property_t *found = obs_properties_get(group, name); + if (found != NULL) { + return found; + } + } + property = property->next; } return NULL; } +obs_properties_t *obs_properties_get_parent(obs_properties_t *props) +{ + return props->parent ? props->parent->parent : NULL; +} + +void obs_properties_remove_by_name(obs_properties_t *props, const char *name) +{ + if (!props) + return; + + /* obs_properties_t is a forward-linked-list, so we need to keep both + * previous and current pointers around. That way we can fix up the + * pointers for the previous element if we find a match. + */ + struct obs_property *cur = props->first_property; + struct obs_property *prev = props->first_property; + + while (cur) { + if (strcmp(cur->name, name) == 0) { + prev->next = cur->next; + cur->next = 0; + obs_property_destroy(cur); + break; + } + + prev = cur; + cur = cur->next; + } +} + void obs_properties_apply_settings(obs_properties_t *props, obs_data_t *settings) { struct obs_property *p; @@ -313,6 +380,7 @@ static inline size_t get_property_size(enum obs_property_type type) case OBS_PROPERTY_EDITABLE_LIST: return sizeof(struct editable_list_data); case OBS_PROPERTY_FRAME_RATE:return sizeof(struct frame_rate_data); + case OBS_PROPERTY_GROUP: return sizeof(struct group_data); } return 0; @@ -337,7 +405,18 @@ static inline struct obs_property *new_prop(struct obs_properties *props, return p; } -static inline bool has_prop(struct obs_properties *props, const char *name) +static inline obs_properties_t *get_topmost_parent(obs_properties_t *props) +{ + obs_properties_t *parent = props; + obs_properties_t *last_parent = parent; + while (parent) { + last_parent = parent; + parent = obs_properties_get_parent(parent); + } + return last_parent; +} + +static inline bool contains_prop(struct obs_properties *props, const char *name) { struct obs_property *p = props->first_property; @@ -347,12 +426,23 @@ static inline bool has_prop(struct obs_properties *props, const char *name) return true; } + if (p->type == OBS_PROPERTY_GROUP) { + if (contains_prop(obs_property_group_content(p), name)) { + return true; + } + } + p = p->next; } return false; } +static inline bool has_prop(struct obs_properties *props, const char *name) +{ + return contains_prop(get_topmost_parent(props), name); +} + static inline void *get_property_data(struct obs_property *prop) { return (uint8_t*)prop + sizeof(struct obs_property); @@ -553,6 +643,70 @@ obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props, return p; } +static bool check_property_group_recursion(obs_properties_t *parent, + obs_properties_t *group) +{ + /* Scan the group for the parent. */ + obs_property_t *current_property = group->first_property; + while (current_property) { + if (current_property->type == OBS_PROPERTY_GROUP) { + obs_properties_t *cprops = + obs_property_group_content(current_property); + if (cprops == parent) { + /* Contains find_props */ + return true; + } else if (cprops == group) { + /* Contains self, shouldn't be possible but + * lets verify anyway. */ + return true; + } + check_property_group_recursion(cprops, group); + } + + current_property = current_property->next; + } + + return false; +} + +static bool check_property_group_duplicates(obs_properties_t *parent, + obs_properties_t *group) +{ + obs_property_t *current_property = group->first_property; + while (current_property) { + if (has_prop(parent, current_property->name)) { + return true; + } + + current_property = current_property->next; + } + + return false; +} + +obs_property_t *obs_properties_add_group(obs_properties_t *props, + const char *name, const char *desc, enum obs_group_type type, + obs_properties_t *group) +{ + if (!props || has_prop(props, name)) return NULL; + if (!group) return NULL; + + /* Prevent recursion. */ + if (props == group) return NULL; + if (check_property_group_recursion(props, group)) return NULL; + + /* Prevent duplicate properties */ + if (check_property_group_duplicates(props, group)) return NULL; + + obs_property_t *p = new_prop(props, name, desc, OBS_PROPERTY_GROUP); + group->parent = p; + + struct group_data *data = get_property_data(p); + data->type = type; + data->content = group; + return p; +} + /* ------------------------------------------------------------------------- */ static inline bool is_combo(struct obs_property *p) @@ -605,9 +759,11 @@ bool obs_property_modified(obs_property_t *p, obs_data_t *settings) { if (p) { if (p->modified) { - return p->modified(p->parent, p, settings); + obs_properties_t *top = get_topmost_parent(p->parent); + return p->modified(top, p, settings); } else if (p->modified2) { - return p->modified2(p->priv, p->parent, p, settings); + obs_properties_t *top = get_topmost_parent(p->parent); + return p->modified2(p->priv, top, p, settings); } } return false; @@ -620,9 +776,10 @@ bool obs_property_button_clicked(obs_property_t *p, void *obj) struct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON); if (data && data->callback) { + obs_properties_t *top = get_topmost_parent(p->parent); if (p->priv) - return data->callback(p->parent, p, p->priv); - return data->callback(p->parent, p, + return data->callback(top, p, p->priv); + return data->callback(top, p, (context ? context->data : NULL)); } } @@ -714,6 +871,12 @@ enum obs_number_type obs_property_int_type(obs_property_t *p) return data ? data->type : OBS_NUMBER_SCROLLER; } +const char *obs_property_int_suffix(obs_property_t *p) +{ + struct int_data *data = get_type_data(p, OBS_PROPERTY_INT); + return data ? data->suffix : NULL; +} + double obs_property_float_min(obs_property_t *p) { struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT); @@ -732,6 +895,12 @@ double obs_property_float_step(obs_property_t *p) return data ? data->step : 0; } +const char *obs_property_float_suffix(obs_property_t *p) +{ + struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT); + return data ? data->suffix : NULL; +} + enum obs_number_type obs_property_float_type(obs_property_t *p) { struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT); @@ -789,7 +958,7 @@ void obs_property_int_set_limits(obs_property_t *p, void obs_property_float_set_limits(obs_property_t *p, double min, double max, double step) { - struct float_data *data = get_type_data(p, OBS_PROPERTY_INT); + struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT); if (!data) return; @@ -798,6 +967,26 @@ void obs_property_float_set_limits(obs_property_t *p, data->step = step; } +void obs_property_int_set_suffix(obs_property_t *p, const char *suffix) +{ + struct int_data *data = get_type_data(p, OBS_PROPERTY_INT); + if (!data) + return; + + bfree(data->suffix); + data->suffix = bstrdup(suffix); +} + +void obs_property_float_set_suffix(obs_property_t *p, const char *suffix) +{ + struct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT); + if (!data) + return; + + bfree(data->suffix); + data->suffix = bstrdup(suffix); +} + void obs_property_list_clear(obs_property_t *p) { struct list_data *data = get_list_data(p); @@ -1112,3 +1301,15 @@ enum obs_text_type obs_proprety_text_type(obs_property_t *p) { return obs_property_text_type(p); } + +enum obs_group_type obs_property_group_type(obs_property_t *p) +{ + struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP); + return data ? data->type : OBS_COMBO_INVALID; +} + +obs_properties_t *obs_property_group_content(obs_property_t *p) +{ + struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP); + return data ? data->content : NULL; +} diff --git a/libobs/obs-properties.h b/libobs/obs-properties.h index 6867063..cf17941 100644 --- a/libobs/obs-properties.h +++ b/libobs/obs-properties.h @@ -55,6 +55,7 @@ enum obs_property_type { OBS_PROPERTY_FONT, OBS_PROPERTY_EDITABLE_LIST, OBS_PROPERTY_FRAME_RATE, + OBS_PROPERTY_GROUP, }; enum obs_combo_format { @@ -93,6 +94,12 @@ enum obs_number_type { OBS_NUMBER_SLIDER }; +enum obs_group_type { + OBS_COMBO_INVALID, + OBS_GROUP_NORMAL, + OBS_GROUP_CHECKABLE, +}; + #define OBS_FONT_BOLD (1<<0) #define OBS_FONT_ITALIC (1<<1) #define OBS_FONT_UNDERLINE (1<<2) @@ -122,6 +129,21 @@ EXPORT obs_property_t *obs_properties_first(obs_properties_t *props); EXPORT obs_property_t *obs_properties_get(obs_properties_t *props, const char *property); +EXPORT obs_properties_t *obs_properties_get_parent(obs_properties_t *props); + +/** Remove a property from a properties list. + * + * Removes a property from a properties list. Only valid in either + * get_properties or modified_callback(2). modified_callback(2) must return + * true so that all UI properties are rebuilt and returning false is undefined + * behavior. + * + * @param props Properties to remove from. + * @param property Name of the property to remove. + */ +EXPORT void obs_properties_remove_by_name(obs_properties_t *props, + const char *property); + /** * Applies settings to the properties by calling all the necessary * modification callbacks @@ -218,6 +240,11 @@ EXPORT obs_property_t *obs_properties_add_editable_list(obs_properties_t *props, EXPORT obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props, const char *name, const char *description); +EXPORT obs_property_t *obs_properties_add_group(obs_properties_t *props, + const char *name, const char *description, enum obs_group_type type, + obs_properties_t *group); + + /* ------------------------------------------------------------------------- */ /** @@ -259,10 +286,12 @@ EXPORT int obs_property_int_min(obs_property_t *p); EXPORT int obs_property_int_max(obs_property_t *p); EXPORT int obs_property_int_step(obs_property_t *p); EXPORT enum obs_number_type obs_property_int_type(obs_property_t *p); +EXPORT const char * obs_property_int_suffix(obs_property_t *p); EXPORT double obs_property_float_min(obs_property_t *p); EXPORT double obs_property_float_max(obs_property_t *p); EXPORT double obs_property_float_step(obs_property_t *p); EXPORT enum obs_number_type obs_property_float_type(obs_property_t *p); +EXPORT const char * obs_property_float_suffix(obs_property_t *p); EXPORT enum obs_text_type obs_property_text_type(obs_property_t *p); EXPORT enum obs_path_type obs_property_path_type(obs_property_t *p); EXPORT const char * obs_property_path_filter(obs_property_t *p); @@ -274,6 +303,8 @@ EXPORT void obs_property_int_set_limits(obs_property_t *p, int min, int max, int step); EXPORT void obs_property_float_set_limits(obs_property_t *p, double min, double max, double step); +EXPORT void obs_property_int_set_suffix(obs_property_t *p, const char *suffix); +EXPORT void obs_property_float_set_suffix(obs_property_t *p, const char *suffix); EXPORT void obs_property_list_clear(obs_property_t *p); @@ -336,6 +367,9 @@ EXPORT struct media_frames_per_second obs_property_frame_rate_fps_range_min( EXPORT struct media_frames_per_second obs_property_frame_rate_fps_range_max( obs_property_t *p, size_t idx); +EXPORT enum obs_group_type obs_property_group_type(obs_property_t *p); +EXPORT obs_properties_t *obs_property_group_content(obs_property_t *p); + #ifndef SWIG DEPRECATED EXPORT enum obs_text_type obs_proprety_text_type(obs_property_t *p); diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index a3ba2d8..f23fd49 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -395,6 +395,8 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) scale.y = (float)height * item->scale.y; } + item->box_scale = scale; + add_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y); matrix4_identity(&item->box_transform); @@ -462,6 +464,8 @@ static inline bool item_texture_enabled(const struct obs_scene_item *item) static void render_item_texture(struct obs_scene_item *item) { + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, "render_item_texture"); + gs_texture_t *tex = gs_texrender_get_texture(item->item_render); gs_effect_t *effect = obs->video.default_effect; enum obs_scale_type type = item->scale_filter; @@ -486,6 +490,8 @@ static void render_item_texture(struct obs_scene_item *item) effect = obs->video.bicubic_effect; } else if (type == OBS_SCALE_LANCZOS) { effect = obs->video.lanczos_effect; + } else if (type == OBS_SCALE_AREA) { + effect = obs->video.area_effect; } scale_param = gs_effect_get_param_by_name(effect, @@ -501,18 +507,29 @@ static void render_item_texture(struct obs_scene_item *item) } } + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); + while (gs_effect_loop(effect, "Draw")) obs_source_draw(tex, 0, 0, 0, 0, 0); + + gs_blend_state_pop(); + + GS_DEBUG_MARKER_END(); } static inline void render_item(struct obs_scene_item *item) { + GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_ITEM, "Item: %s", + obs_source_get_name(item->source)); + if (item->item_render) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); - if (!width || !height) - return; + if (!width || !height) { + goto cleanup; + } uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); @@ -533,10 +550,8 @@ static inline void render_item(struct obs_scene_item *item) -(float)item->crop.top, 0.0f); - gs_blend_state_push(); - gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); obs_source_video_render(item->source); - gs_blend_state_pop(); + gs_texrender_end(item->item_render); } } @@ -549,6 +564,9 @@ static inline void render_item(struct obs_scene_item *item) obs_source_video_render(item->source); } gs_matrix_pop(); + +cleanup: + GS_DEBUG_MARKER_END(); } static void scene_video_tick(void *data, float seconds) @@ -748,6 +766,8 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) item->scale_filter = OBS_SCALE_BICUBIC; else if (astrcmpi(scale_filter_str, "lanczos") == 0) item->scale_filter = OBS_SCALE_LANCZOS; + else if (astrcmpi(scale_filter_str, "area") == 0) + item->scale_filter = OBS_SCALE_AREA; } if (item->item_render && !item_texture_enabled(item)) { @@ -857,6 +877,8 @@ static void scene_save_item(obs_data_array_t *array, scale_filter = "bicubic"; else if (item->scale_filter == OBS_SCALE_LANCZOS) scale_filter = "lanczos"; + else if (item->scale_filter == OBS_SCALE_AREA) + scale_filter = "area"; else scale_filter = "disable"; @@ -1243,6 +1265,7 @@ static inline void duplicate_item_data(struct obs_scene_item *dst, dst->output_scale = src->output_scale; dst->scale_filter = src->scale_filter; dst->box_transform = src->box_transform; + dst->box_scale = src->box_scale; dst->draw_transform = src->draw_transform; dst->bounds_type = src->bounds_type; dst->bounds_align = src->bounds_align; @@ -2030,6 +2053,13 @@ void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item, matrix4_copy(transform, &item->box_transform); } +void obs_sceneitem_get_box_scale(const obs_sceneitem_t *item, + struct vec2 *scale) +{ + if (item) + *scale = item->box_scale; +} + bool obs_sceneitem_visible(const obs_sceneitem_t *item) { return item ? item->user_visible : false; diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 4591db5..6ac57dd 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -65,6 +65,7 @@ struct obs_scene_item { enum obs_scale_type scale_filter; struct matrix4 box_transform; + struct vec2 box_scale; struct matrix4 draw_transform; enum obs_bounds_type bounds_type; diff --git a/libobs/obs-source-deinterlace.c b/libobs/obs-source-deinterlace.c index 1fce870..c3065e9 100644 --- a/libobs/obs-source-deinterlace.c +++ b/libobs/obs-source-deinterlace.c @@ -315,9 +315,6 @@ void deinterlace_render(obs_source_t *s) gs_eparam_t *dimensions = gs_effect_get_param_by_name(effect, "dimensions"); struct vec2 size = {(float)s->async_width, (float)s->async_height}; - bool yuv = format_is_yuv(s->async_format); - bool limited_range = yuv && !s->async_full_range; - const char *tech = yuv ? "DrawMatrix" : "Draw"; gs_texture_t *cur_tex = s->async_texrender ? gs_texrender_get_texture(s->async_texrender) : @@ -334,30 +331,12 @@ void deinterlace_render(obs_source_t *s) gs_effect_set_int(field, s->deinterlace_top_first); gs_effect_set_vec2(dimensions, &size); - if (yuv) { - gs_eparam_t *color_matrix = gs_effect_get_param_by_name( - effect, "color_matrix"); - gs_effect_set_val(color_matrix, s->async_color_matrix, - sizeof(float) * 16); - } - if (limited_range) { - const size_t size = sizeof(float) * 3; - gs_eparam_t *color_range_min = gs_effect_get_param_by_name( - effect, "color_range_min"); - gs_eparam_t *color_range_max = gs_effect_get_param_by_name( - effect, "color_range_max"); - gs_effect_set_val(color_range_min, s->async_color_range_min, - size); - gs_effect_set_val(color_range_max, s->async_color_range_max, - size); - } - frame2_ts = s->deinterlace_frame_ts + s->deinterlace_offset + s->deinterlace_half_duration - TWOX_TOLERANCE; gs_effect_set_bool(frame2, obs->video.video_time >= frame2_ts); - while (gs_effect_loop(effect, tech)) + while (gs_effect_loop(effect, "Draw")) gs_draw_sprite(NULL, s->async_flip ? GS_FLIP_V : 0, s->async_width, s->async_height); } diff --git a/libobs/obs-source-transition.c b/libobs/obs-source-transition.c index 929204f..60db7a6 100644 --- a/libobs/obs-source-transition.c +++ b/libobs/obs-source-transition.c @@ -665,6 +665,11 @@ static inline void handle_stop(obs_source_t *transition) "transition_stop"); } +void obs_transition_force_stop(obs_source_t *transition) +{ + handle_stop(transition); +} + void obs_transition_video_render(obs_source_t *transition, obs_transition_video_render_callback_t callback) { @@ -718,10 +723,16 @@ void obs_transition_video_render(obs_source_t *transition, cx = get_cx(transition); cy = get_cy(transition); - if (cx && cy) + if (cx && cy) { + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); + callback(transition->context.data, tex[0], tex[1], t, cx, cy); + gs_blend_state_pop(); + } + } else if (state.transitioning_audio) { if (state.s[1]) { gs_matrix_push(); diff --git a/libobs/obs-source.c b/libobs/obs-source.c index d77be32..033006a 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -16,6 +16,7 @@ ******************************************************************************/ #include +#include #include "media-io/format-conversion.h" #include "media-io/video-frame.h" @@ -140,6 +141,7 @@ bool obs_source_init(struct obs_source *source) source->user_volume = 1.0f; source->volume = 1.0f; source->sync_offset = 0; + source->balance = 0.5f; pthread_mutex_init_value(&source->filter_mutex); pthread_mutex_init_value(&source->async_mutex); pthread_mutex_init_value(&source->audio_mutex); @@ -1327,15 +1329,21 @@ enum convert_type { CONVERT_420, CONVERT_422_U, CONVERT_422_Y, + CONVERT_444, + CONVERT_800, + CONVERT_RGB_LIMITED, }; -static inline enum convert_type get_convert_type(enum video_format format) +static inline enum convert_type get_convert_type(enum video_format format, + bool full_range) { switch (format) { case VIDEO_FORMAT_I420: return CONVERT_420; case VIDEO_FORMAT_NV12: return CONVERT_NV12; + case VIDEO_FORMAT_I444: + return CONVERT_444; case VIDEO_FORMAT_YVYU: case VIDEO_FORMAT_YUY2: @@ -1344,12 +1352,13 @@ static inline enum convert_type get_convert_type(enum video_format format) return CONVERT_422_U; case VIDEO_FORMAT_Y800: - case VIDEO_FORMAT_I444: + return CONVERT_800; + case VIDEO_FORMAT_NONE: case VIDEO_FORMAT_RGBA: case VIDEO_FORMAT_BGRA: case VIDEO_FORMAT_BGRX: - return CONVERT_NONE; + return full_range ? CONVERT_NONE : CONVERT_RGB_LIMITED; } return CONVERT_NONE; @@ -1358,12 +1367,23 @@ static inline enum convert_type get_convert_type(enum video_format format) static inline bool set_packed422_sizes(struct obs_source *source, const struct obs_source_frame *frame) { - source->async_convert_height = frame->height; source->async_convert_width = frame->width / 2; + source->async_convert_height = frame->height; source->async_texture_format = GS_BGRA; return true; } +static inline bool set_planar444_sizes(struct obs_source *source, + const struct obs_source_frame *frame) +{ + source->async_convert_width = frame->width; + source->async_convert_height = frame->height * 3; + source->async_texture_format = GS_R8; + source->async_plane_offset[0] = (int)(frame->data[1] - frame->data[0]); + source->async_plane_offset[1] = (int)(frame->data[2] - frame->data[0]); + return true; +} + static inline bool set_planar420_sizes(struct obs_source *source, const struct obs_source_frame *frame) { @@ -1391,10 +1411,28 @@ static inline bool set_nv12_sizes(struct obs_source *source, return true; } +static inline bool set_y800_sizes(struct obs_source *source, + const struct obs_source_frame *frame) +{ + source->async_convert_width = frame->width; + source->async_convert_height = frame->height; + source->async_texture_format = GS_R8; + return true; +} + +static inline bool set_rgb_limited_sizes(struct obs_source *source, + const struct obs_source_frame *frame) +{ + source->async_convert_width = frame->width; + source->async_convert_height = frame->height; + source->async_texture_format = convert_video_format(frame->format); + return true; +} + static inline bool init_gpu_conversion(struct obs_source *source, const struct obs_source_frame *frame) { - switch (get_convert_type(frame->format)) { + switch (get_convert_type(frame->format, frame->full_range)) { case CONVERT_422_Y: case CONVERT_422_U: return set_packed422_sizes(source, frame); @@ -1404,7 +1442,15 @@ static inline bool init_gpu_conversion(struct obs_source *source, case CONVERT_NV12: return set_nv12_sizes(source, frame); - break; + + case CONVERT_444: + return set_planar444_sizes(source, frame); + + case CONVERT_800: + return set_y800_sizes(source, frame); + + case CONVERT_RGB_LIMITED: + return set_rgb_limited_sizes(source, frame); case CONVERT_NONE: assert(false && "No conversion requested"); @@ -1417,16 +1463,19 @@ static inline bool init_gpu_conversion(struct obs_source *source, bool set_async_texture_size(struct obs_source *source, const struct obs_source_frame *frame) { - enum convert_type cur = get_convert_type(frame->format); + enum convert_type cur = get_convert_type(frame->format, + frame->full_range); - if (source->async_width == frame->width && - source->async_height == frame->height && - source->async_format == frame->format) + if (source->async_width == frame->width && + source->async_height == frame->height && + source->async_format == frame->format && + source->async_full_range == frame->full_range) return true; - source->async_width = frame->width; - source->async_height = frame->height; - source->async_format = frame->format; + source->async_width = frame->width; + source->async_height = frame->height; + source->async_format = frame->format; + source->async_full_range = frame->full_range; gs_enter_context(obs->video.graphics); @@ -1442,8 +1491,10 @@ bool set_async_texture_size(struct obs_source *source, if (cur != CONVERT_NONE && init_gpu_conversion(source, frame)) { source->async_gpu_conversion = true; + enum gs_color_format format = CONVERT_RGB_LIMITED ? + convert_video_format(frame->format) : GS_BGRX; source->async_texrender = - gs_texrender_create(GS_BGRX, GS_ZS_NONE); + gs_texrender_create(format, GS_ZS_NONE); source->async_texture = gs_texture_create( source->async_convert_width, @@ -1472,19 +1523,18 @@ bool set_async_texture_size(struct obs_source *source, static void upload_raw_frame(gs_texture_t *tex, const struct obs_source_frame *frame) { - switch (get_convert_type(frame->format)) { + switch (get_convert_type(frame->format, frame->full_range)) { case CONVERT_422_U: case CONVERT_422_Y: + case CONVERT_800: + case CONVERT_RGB_LIMITED: gs_texture_set_image(tex, frame->data[0], frame->linesize[0], false); break; case CONVERT_420: - gs_texture_set_image(tex, frame->data[0], - frame->width, false); - break; - case CONVERT_NV12: + case CONVERT_444: gs_texture_set_image(tex, frame->data[0], frame->width, false); break; @@ -1495,7 +1545,8 @@ static void upload_raw_frame(gs_texture_t *tex, } } -static const char *select_conversion_technique(enum video_format format) +static const char *select_conversion_technique(enum video_format format, + bool full_range) { switch (format) { case VIDEO_FORMAT_UYVY: @@ -1512,15 +1563,21 @@ static const char *select_conversion_technique(enum video_format format) case VIDEO_FORMAT_NV12: return "NV12_Reverse"; - break; + + case VIDEO_FORMAT_I444: + return "I444_Reverse"; case VIDEO_FORMAT_Y800: + return full_range ? "Y800_Full" : "Y800_Limited"; + case VIDEO_FORMAT_BGRA: case VIDEO_FORMAT_BGRX: case VIDEO_FORMAT_RGBA: case VIDEO_FORMAT_NONE: - case VIDEO_FORMAT_I444: - assert(false && "No conversion requested"); + if (full_range) + assert(false && "No conversion requested"); + else + return "RGB_Limited"; break; } return NULL; @@ -1542,6 +1599,8 @@ static bool update_async_texrender(struct obs_source *source, const struct obs_source_frame *frame, gs_texture_t *tex, gs_texrender_t *texrender) { + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_CONVERT_FORMAT, "Convert Format"); + gs_texrender_reset(texrender); upload_raw_frame(tex, frame); @@ -1552,11 +1611,16 @@ static bool update_async_texrender(struct obs_source *source, float convert_width = (float)source->async_convert_width; gs_effect_t *conv = obs->video.conversion_effect; - gs_technique_t *tech = gs_effect_get_technique(conv, - select_conversion_technique(frame->format)); + const char *tech_name = select_conversion_technique(frame->format, + frame->full_range); + gs_technique_t *tech = gs_effect_get_technique(conv, tech_name); - if (!gs_texrender_begin(texrender, cx, cy)) + if (!gs_texrender_begin(texrender, cx, cy)) { + GS_DEBUG_MARKER_END(); return false; + } + + gs_enable_blending(false); gs_technique_begin(tech); gs_technique_begin_pass(tech, 0); @@ -1575,6 +1639,19 @@ static bool update_async_texrender(struct obs_source *source, set_eparami(conv, "int_v_plane_offset", (int)source->async_plane_offset[1]); + gs_effect_set_val(gs_effect_get_param_by_name(conv, "color_matrix"), + frame->color_matrix, sizeof(float) * 16); + if (!frame->full_range) { + gs_eparam_t *min_param = gs_effect_get_param_by_name( + conv, "color_range_min"); + gs_effect_set_val(min_param, frame->color_range_min, + sizeof(float) * 3); + gs_eparam_t *max_param = gs_effect_get_param_by_name( + conv, "color_range_max"); + gs_effect_set_val(max_param, frame->color_range_max, + sizeof(float) * 3); + } + gs_ortho(0.f, (float)cx, 0.f, (float)cy, -100.f, 100.f); gs_draw_sprite(tex, 0, cx, cy); @@ -1582,8 +1659,11 @@ static bool update_async_texrender(struct obs_source *source, gs_technique_end_pass(tech); gs_technique_end(tech); + gs_enable_blending(true); + gs_texrender_end(texrender); + GS_DEBUG_MARKER_END(); return true; } @@ -1591,56 +1671,25 @@ bool update_async_texture(struct obs_source *source, const struct obs_source_frame *frame, gs_texture_t *tex, gs_texrender_t *texrender) { - enum convert_type type = get_convert_type(frame->format); - uint8_t *ptr; - uint32_t linesize; + enum convert_type type; source->async_flip = frame->flip; - source->async_full_range = frame->full_range; - memcpy(source->async_color_matrix, frame->color_matrix, - sizeof(frame->color_matrix)); - memcpy(source->async_color_range_min, frame->color_range_min, - sizeof frame->color_range_min); - memcpy(source->async_color_range_max, frame->color_range_max, - sizeof frame->color_range_max); if (source->async_gpu_conversion && texrender) return update_async_texrender(source, frame, tex, texrender); + type = get_convert_type(frame->format, frame->full_range); if (type == CONVERT_NONE) { gs_texture_set_image(tex, frame->data[0], frame->linesize[0], false); return true; } - if (!gs_texture_map(tex, &ptr, &linesize)) - return false; - - if (type == CONVERT_420) - decompress_420((const uint8_t* const*)frame->data, - frame->linesize, - 0, frame->height, ptr, linesize); - - else if (type == CONVERT_NV12) - decompress_nv12((const uint8_t* const*)frame->data, - frame->linesize, - 0, frame->height, ptr, linesize); - - else if (type == CONVERT_422_Y) - decompress_422(frame->data[0], frame->linesize[0], - 0, frame->height, ptr, linesize, true); - - else if (type == CONVERT_422_U) - decompress_422(frame->data[0], frame->linesize[0], - 0, frame->height, ptr, linesize, false); - - gs_texture_unmap(tex); - return true; + return false; } static inline void obs_source_draw_texture(struct obs_source *source, - gs_effect_t *effect, float *color_matrix, - float const *color_range_min, float const *color_range_max) + gs_effect_t *effect) { gs_texture_t *tex = source->async_texture; gs_eparam_t *param; @@ -1648,23 +1697,6 @@ static inline void obs_source_draw_texture(struct obs_source *source, if (source->async_texrender) tex = gs_texrender_get_texture(source->async_texrender); - if (color_range_min) { - size_t const size = sizeof(float) * 3; - param = gs_effect_get_param_by_name(effect, "color_range_min"); - gs_effect_set_val(param, color_range_min, size); - } - - if (color_range_max) { - size_t const size = sizeof(float) * 3; - param = gs_effect_get_param_by_name(effect, "color_range_max"); - gs_effect_set_val(param, color_range_max, size); - } - - if (color_matrix) { - param = gs_effect_get_param_by_name(effect, "color_matrix"); - gs_effect_set_val(param, color_matrix, sizeof(float) * 16); - } - param = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(param, tex); @@ -1673,24 +1705,18 @@ static inline void obs_source_draw_texture(struct obs_source *source, static void obs_source_draw_async_texture(struct obs_source *source) { - gs_effect_t *effect = gs_get_effect(); - bool yuv = format_is_yuv(source->async_format); - bool limited_range = yuv && !source->async_full_range; - const char *type = yuv ? "DrawMatrix" : "Draw"; + gs_effect_t *effect = gs_get_effect(); bool def_draw = (!effect); - gs_technique_t *tech = NULL; + gs_technique_t *tech = NULL; if (def_draw) { effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); - tech = gs_effect_get_technique(effect, type); + tech = gs_effect_get_technique(effect, "Draw"); gs_technique_begin(tech); gs_technique_begin_pass(tech, 0); } - obs_source_draw_texture(source, effect, - yuv ? source->async_color_matrix : NULL, - limited_range ? source->async_color_range_min : NULL, - limited_range ? source->async_color_range_max : NULL); + obs_source_draw_texture(source, effect); if (def_draw) { gs_technique_end_pass(tech); @@ -1783,6 +1809,24 @@ static inline void obs_source_main_render(obs_source_t *source) static bool ready_async_frame(obs_source_t *source, uint64_t sys_time); +#if GS_USE_DEBUG_MARKERS +static const char *get_type_format(enum obs_source_type type) +{ + switch (type) { + case OBS_SOURCE_TYPE_INPUT: + return "Input: %s"; + case OBS_SOURCE_TYPE_FILTER: + return "Filter: %s"; + case OBS_SOURCE_TYPE_TRANSITION: + return "Transition: %s"; + case OBS_SOURCE_TYPE_SCENE: + return "Scene: %s"; + default: + return "[Unknown]: %s"; + } +} +#endif + static inline void render_video(obs_source_t *source) { if (source->info.type != OBS_SOURCE_TYPE_FILTER && @@ -1806,6 +1850,10 @@ static inline void render_video(obs_source_t *source) return; } + GS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_SOURCE, + get_type_format(source->info.type), + obs_source_get_name(source)); + if (source->filters.num && !source->rendering_filter) obs_source_render_filters(source); @@ -1820,6 +1868,8 @@ static inline void render_video(obs_source_t *source) else obs_source_render_async_video(source); + + GS_DEBUG_MARKER_END(); } void obs_source_video_render(obs_source_t *source) @@ -1835,11 +1885,12 @@ void obs_source_video_render(obs_source_t *source) static uint32_t get_base_width(const obs_source_t *source) { bool is_filter = !!source->filter_parent; + bool func_valid = source->context.data && source->info.get_width; if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) { return source->enabled ? source->transition_actual_cx : 0; - } else if (source->info.get_width && (!is_filter || source->enabled)) { + } else if (func_valid && (!is_filter || source->enabled)) { return source->info.get_width(source->context.data); } else if (is_filter) { @@ -1852,11 +1903,12 @@ static uint32_t get_base_width(const obs_source_t *source) static uint32_t get_base_height(const obs_source_t *source) { bool is_filter = !!source->filter_parent; + bool func_valid = source->context.data && source->info.get_height; if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) { return source->enabled ? source->transition_actual_cy : 0; - } else if (source->info.get_height && (!is_filter || source->enabled)) { + } else if (func_valid && (!is_filter || source->enabled)) { return source->info.get_height(source->context.data); } else if (is_filter) { @@ -2213,41 +2265,6 @@ static inline void copy_frame_data_plane(struct obs_source_frame *dst, dst->linesize[plane] * lines); } -static void copy_frame_data_line_y800(uint32_t *dst, uint8_t *src, uint8_t *end) -{ - while (src < end) { - register uint32_t val = *(src++); - val |= (val << 8); - val |= (val << 16); - *(dst++) = val; - } -} - -static inline void copy_frame_data_y800(struct obs_source_frame *dst, - const struct obs_source_frame *src) -{ - uint32_t *ptr_dst; - uint8_t *ptr_src; - uint8_t *src_end; - - if ((src->linesize[0] * 4) != dst->linesize[0]) { - for (uint32_t cy = 0; cy < src->height; cy++) { - ptr_dst = (uint32_t*) - (dst->data[0] + cy * dst->linesize[0]); - ptr_src = (src->data[0] + cy * src->linesize[0]); - src_end = ptr_src + src->width; - - copy_frame_data_line_y800(ptr_dst, ptr_src, src_end); - } - } else { - ptr_dst = (uint32_t*)dst->data[0]; - ptr_src = (uint8_t *)src->data[0]; - src_end = ptr_src + src->height * src->linesize[0]; - - copy_frame_data_line_y800(ptr_dst, ptr_src, src_end); - } -} - static void copy_frame_data(struct obs_source_frame *dst, const struct obs_source_frame *src) { @@ -2286,11 +2303,8 @@ static void copy_frame_data(struct obs_source_frame *dst, case VIDEO_FORMAT_RGBA: case VIDEO_FORMAT_BGRA: case VIDEO_FORMAT_BGRX: - copy_frame_data_plane(dst, src, 0, dst->height); - break; - case VIDEO_FORMAT_Y800: - copy_frame_data_y800(dst, src); + copy_frame_data_plane(dst, src, 0, dst->height); break; } } @@ -2305,8 +2319,9 @@ static inline bool async_texture_changed(struct obs_source *source, const struct obs_source_frame *frame) { enum convert_type prev, cur; - prev = get_convert_type(source->async_cache_format); - cur = get_convert_type(frame->format); + prev = get_convert_type(source->async_cache_format, + source->async_cache_full_range); + cur = get_convert_type(frame->format, frame->full_range); return source->async_cache_width != frame->width || source->async_cache_height != frame->height || @@ -2342,7 +2357,7 @@ static void clean_cache(obs_source_t *source) } #define MAX_ASYNC_FRAMES 30 - +//if return value is not null then do (os_atomic_dec_long(&output->refs) == 0) && obs_source_frame_destroy(output) static inline struct obs_source_frame *cache_video(struct obs_source *source, const struct obs_source_frame *frame) { @@ -2359,9 +2374,10 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source, if (async_texture_changed(source, frame)) { free_async_cache(source); - source->async_cache_width = frame->width; - source->async_cache_height = frame->height; - source->async_cache_format = frame->format; + source->async_cache_width = frame->width; + source->async_cache_height = frame->height; + source->async_cache_format = frame->format; + source->async_cache_full_range = frame->full_range; } for (size_t i = 0; i < source->async_cache.num; i++) { @@ -2380,9 +2396,6 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source, struct async_frame new_af; enum video_format format = frame->format; - if (format == VIDEO_FORMAT_Y800) - format = VIDEO_FORMAT_BGRX; - new_frame = obs_source_frame_create(format, frame->width, frame->height); new_af.frame = new_frame; @@ -2399,15 +2412,10 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source, copy_frame_data(new_frame, frame); - if (os_atomic_dec_long(&new_frame->refs) == 0) { - obs_source_frame_destroy(new_frame); - new_frame = NULL; - } - return new_frame; } -void obs_source_output_video(obs_source_t *source, +static void obs_source_output_video_internal(obs_source_t *source, const struct obs_source_frame *frame) { if (!obs_source_valid(source, "obs_source_output_video")) @@ -2422,13 +2430,67 @@ void obs_source_output_video(obs_source_t *source, cache_video(source, frame) : NULL; /* ------------------------------------------- */ - + pthread_mutex_lock(&source->async_mutex); if (output) { - pthread_mutex_lock(&source->async_mutex); - da_push_back(source->async_frames, &output); - pthread_mutex_unlock(&source->async_mutex); - source->async_active = true; + if (os_atomic_dec_long(&output->refs) == 0) { + obs_source_frame_destroy(output); + output = NULL; + } else { + da_push_back(source->async_frames, &output); + source->async_active = true; + } } + pthread_mutex_unlock(&source->async_mutex); +} + +void obs_source_output_video(obs_source_t *source, + const struct obs_source_frame *frame) +{ + if (!frame) { + obs_source_output_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame = *frame; + new_frame.full_range = format_is_yuv(frame->format) + ? new_frame.full_range + : true; + + obs_source_output_video_internal(source, &new_frame); +} + +void obs_source_output_video2(obs_source_t *source, + const struct obs_source_frame2 *frame) +{ + if (!frame) { + obs_source_output_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame; + enum video_range_type range = resolve_video_range(frame->format, + frame->range); + + for (size_t i = 0; i < MAX_AV_PLANES; i++) { + new_frame.data[i] = frame->data[i]; + new_frame.linesize[i] = frame->linesize[i]; + } + + new_frame.width = frame->width; + new_frame.height = frame->height; + new_frame.timestamp = frame->timestamp; + new_frame.format = frame->format; + new_frame.full_range = range == VIDEO_RANGE_FULL; + new_frame.flip = frame->flip; + + memcpy(&new_frame.color_matrix, &frame->color_matrix, + sizeof(frame->color_matrix)); + memcpy(&new_frame.color_range_min, &frame->color_range_min, + sizeof(frame->color_range_min)); + memcpy(&new_frame.color_range_max, &frame->color_range_max, + sizeof(frame->color_range_max)); + + obs_source_output_video_internal(source, &new_frame); } static inline bool preload_frame_changed(obs_source_t *source, @@ -2442,7 +2504,7 @@ static inline bool preload_frame_changed(obs_source_t *source, in->format != source->async_preload_frame->format; } -void obs_source_preload_video(obs_source_t *source, +static void obs_source_preload_video_internal(obs_source_t *source, const struct obs_source_frame *frame) { if (!obs_source_valid(source, "obs_source_preload_video")) @@ -2471,6 +2533,56 @@ void obs_source_preload_video(obs_source_t *source, obs_leave_graphics(); } +void obs_source_preload_video(obs_source_t *source, + const struct obs_source_frame *frame) +{ + if (!frame) { + obs_source_preload_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame = *frame; + new_frame.full_range = format_is_yuv(frame->format) + ? new_frame.full_range + : true; + + obs_source_preload_video_internal(source, &new_frame); +} + +void obs_source_preload_video2(obs_source_t *source, + const struct obs_source_frame2 *frame) +{ + if (!frame) { + obs_source_preload_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame; + enum video_range_type range = resolve_video_range(frame->format, + frame->range); + + for (size_t i = 0; i < MAX_AV_PLANES; i++) { + new_frame.data[i] = frame->data[i]; + new_frame.linesize[i] = frame->linesize[i]; + } + + new_frame.width = frame->width; + new_frame.height = frame->height; + new_frame.timestamp = frame->timestamp; + new_frame.format = frame->format; + new_frame.full_range = range == VIDEO_RANGE_FULL; + new_frame.flip = frame->flip; + + memcpy(&new_frame.color_matrix, &frame->color_matrix, + sizeof(frame->color_matrix)); + memcpy(&new_frame.color_range_min, &frame->color_range_min, + sizeof(frame->color_range_min)); + memcpy(&new_frame.color_range_max, &frame->color_range_max, + sizeof(frame->color_range_max)); + + obs_source_preload_video_internal(source, &new_frame); +} + void obs_source_show_preloaded_video(obs_source_t *source) { uint64_t sys_ts; @@ -2481,7 +2593,9 @@ void obs_source_show_preloaded_video(obs_source_t *source) source->async_active = true; pthread_mutex_lock(&source->audio_buf_mutex); - sys_ts = os_gettime_ns(); + sys_ts = (source->monitoring_type != OBS_MONITORING_TYPE_MONITOR_ONLY) + ? os_gettime_ns() + : 0; reset_audio_timing(source, source->last_frame_ts, sys_ts); reset_audio_data(source, sys_ts); pthread_mutex_unlock(&source->audio_buf_mutex); @@ -2589,6 +2703,37 @@ static void downmix_to_mono_planar(struct obs_source *source, uint32_t frames) } } +static void process_audio_balancing(struct obs_source *source, uint32_t frames, + float balance, enum obs_balance_type type) +{ + float **data = (float**)source->audio_data.data; + + switch(type) { + case OBS_BALANCE_TYPE_SINE_LAW: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * + sinf((1.0f - balance) * (M_PI/2.0f)); + data[1][frame] = data[1][frame] * + sinf(balance * (M_PI/2.0f)); + } + break; + case OBS_BALANCE_TYPE_SQUARE_LAW: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * sqrtf(1.0f - balance); + data[1][frame] = data[1][frame] * sqrtf(balance); + } + break; + case OBS_BALANCE_TYPE_LINEAR: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * (1.0f - balance); + data[1][frame] = data[1][frame] * balance; + } + break; + default: + break; + } +} + /* resamples/remixes new audio to the designated main audio output format */ static void process_audio(obs_source_t *source, const struct obs_source_audio *audio) @@ -2622,6 +2767,12 @@ static void process_audio(obs_source_t *source, mono_output = audio_output_get_channels(obs->audio.audio) == 1; + if (!mono_output && source->sample_info.speakers == SPEAKERS_STEREO && + (source->balance > 0.51f || source->balance < 0.49f)) { + process_audio_balancing(source, frames, source->balance, + OBS_BALANCE_TYPE_SINE_LAW); + } + if (!mono_output && (source->flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0) downmix_to_mono_planar(source, frames); } @@ -3503,12 +3654,25 @@ void obs_source_inc_showing(obs_source_t *source) obs_source_activate(source, AUX_VIEW); } +void obs_source_inc_active(obs_source_t *source) +{ + if (obs_source_valid(source, "obs_source_inc_active")) + obs_source_activate(source, MAIN_VIEW); +} + void obs_source_dec_showing(obs_source_t *source) { if (obs_source_valid(source, "obs_source_dec_showing")) obs_source_deactivate(source, AUX_VIEW); } +void obs_source_dec_active(obs_source_t *source) +{ + if (obs_source_valid(source, "obs_source_dec_active")) + obs_source_deactivate(source, MAIN_VIEW); +} + + void obs_source_enum_filters(obs_source_t *source, obs_source_enum_proc_t callback, void *param) { @@ -4165,3 +4329,25 @@ EXPORT void obs_enable_source_type(const char *name, bool enable) else info->output_flags |= OBS_SOURCE_CAP_DISABLED; } + +enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source) +{ + if (!obs_source_valid(source, "obs_source_get_audio_channels")) + return SPEAKERS_UNKNOWN; + + return source->sample_info.speakers; +} + +void obs_source_set_balance_value(obs_source_t *source, float balance) +{ + if (!obs_source_valid(source, "obs_source_set_balance_value")) + return; + + source->balance = balance; +} + +float obs_source_get_balance_value(const obs_source_t *source) +{ + return obs_source_valid(source, "obs_source_get_balance_value") ? + source->balance : 0.5f; +} diff --git a/libobs/obs-source.h b/libobs/obs-source.h index 04215e9..fdda90d 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -38,6 +38,11 @@ enum obs_source_type { OBS_SOURCE_TYPE_SCENE, }; +enum obs_balance_type { + OBS_BALANCE_TYPE_SINE_LAW, + OBS_BALANCE_TYPE_SQUARE_LAW, + OBS_BALANCE_TYPE_LINEAR, +}; /** * @name Source output flags diff --git a/libobs/obs-video-gpu-encode.c b/libobs/obs-video-gpu-encode.c new file mode 100644 index 0000000..aca8dfd --- /dev/null +++ b/libobs/obs-video-gpu-encode.c @@ -0,0 +1,228 @@ +/****************************************************************************** + Copyright (C) 2018 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 "obs-internal.h" + +static void *gpu_encode_thread(void *unused) +{ + struct obs_core_video *video = &obs->video; + uint64_t interval = video_output_get_frame_time(obs->video.video); + DARRAY(obs_encoder_t *) encoders; + int wait_frames = NUM_ENCODE_TEXTURE_FRAMES_TO_WAIT; + + UNUSED_PARAMETER(unused); + da_init(encoders); + + os_set_thread_name("obs gpu encode thread"); + + while (os_sem_wait(video->gpu_encode_semaphore) == 0) { + struct obs_tex_frame tf; + uint64_t timestamp; + uint64_t lock_key; + uint64_t next_key; + size_t lock_count = 0; + + if (os_atomic_load_bool(&video->gpu_encode_stop)) + break; + + if (wait_frames) { + wait_frames--; + continue; + } + + os_event_reset(video->gpu_encode_inactive); + + /* -------------- */ + + pthread_mutex_lock(&video->gpu_encoder_mutex); + + circlebuf_pop_front(&video->gpu_encoder_queue, &tf, sizeof(tf)); + timestamp = tf.timestamp; + lock_key = tf.lock_key; + next_key = tf.lock_key; + + video_output_inc_texture_frames(video->video); + + for (size_t i = 0; i < video->gpu_encoders.num; i++) { + obs_encoder_t *encoder = video->gpu_encoders.array[i]; + da_push_back(encoders, &encoder); + obs_encoder_addref(encoder); + } + + pthread_mutex_unlock(&video->gpu_encoder_mutex); + + /* -------------- */ + + for (size_t i = 0; i < encoders.num; i++) { + struct encoder_packet pkt = {0}; + bool received = false; + bool success; + + obs_encoder_t *encoder = encoders.array[i]; + struct obs_encoder *pair = encoder->paired_encoder; + + pkt.timebase_num = encoder->timebase_num; + pkt.timebase_den = encoder->timebase_den; + pkt.encoder = encoder; + + if (!encoder->first_received && pair) { + if (!pair->first_received || + pair->first_raw_ts > timestamp) { + continue; + } + } + + if (!encoder->start_ts) + encoder->start_ts = timestamp; + + if (++lock_count == encoders.num) + next_key = 0; + else + next_key++; + + success = encoder->info.encode_texture( + encoder->context.data, tf.handle, + encoder->cur_pts, lock_key, &next_key, + &pkt, &received); + send_off_encoder_packet(encoder, success, received, + &pkt); + + lock_key = next_key; + + encoder->cur_pts += encoder->timebase_num; + } + + /* -------------- */ + + pthread_mutex_lock(&video->gpu_encoder_mutex); + + tf.lock_key = next_key; + + if (--tf.count) { + tf.timestamp += interval; + circlebuf_push_front(&video->gpu_encoder_queue, + &tf, sizeof(tf)); + + video_output_inc_texture_skipped_frames(video->video); + } else { + circlebuf_push_back( + &video->gpu_encoder_avail_queue, + &tf, sizeof(tf)); + } + + pthread_mutex_unlock(&video->gpu_encoder_mutex); + + /* -------------- */ + + os_event_signal(video->gpu_encode_inactive); + + for (size_t i = 0; i < encoders.num; i++) + obs_encoder_release(encoders.array[i]); + + da_resize(encoders, 0); + } + + da_free(encoders); + return NULL; +} + +bool init_gpu_encoding(struct obs_core_video *video) +{ +#ifdef _WIN32 + struct obs_video_info *ovi = &video->ovi; + + video->gpu_encode_stop = false; + + circlebuf_reserve(&video->gpu_encoder_avail_queue, NUM_ENCODE_TEXTURES); + for (size_t i = 0; i < NUM_ENCODE_TEXTURES; i++) { + gs_texture_t *tex; + gs_texture_t *tex_uv; + + gs_texture_create_nv12( + &tex, &tex_uv, + ovi->output_width, ovi->output_height, + GS_RENDER_TARGET | GS_SHARED_KM_TEX); + if (!tex) { + return false; + } + + uint32_t handle = gs_texture_get_shared_handle(tex); + + struct obs_tex_frame frame = { + .tex = tex, + .tex_uv = tex_uv, + .handle = handle + }; + + circlebuf_push_back(&video->gpu_encoder_avail_queue, &frame, + sizeof(frame)); + } + + if (os_sem_init(&video->gpu_encode_semaphore, 0) != 0) + return false; + if (os_event_init(&video->gpu_encode_inactive, OS_EVENT_TYPE_MANUAL) != 0) + return false; + if (pthread_create(&video->gpu_encode_thread, NULL, + gpu_encode_thread, NULL) != 0) + return false; + + os_event_signal(video->gpu_encode_inactive); + + video->gpu_encode_thread_initialized = true; + return true; +#else + UNUSED_PARAMETER(video); + return false; +#endif +} + +void stop_gpu_encoding_thread(struct obs_core_video *video) +{ + if (video->gpu_encode_thread_initialized) { + os_atomic_set_bool(&video->gpu_encode_stop, true); + os_sem_post(video->gpu_encode_semaphore); + pthread_join(video->gpu_encode_thread, NULL); + video->gpu_encode_thread_initialized = false; + } +} + +void free_gpu_encoding(struct obs_core_video *video) +{ + if (video->gpu_encode_semaphore) { + os_sem_destroy(video->gpu_encode_semaphore); + video->gpu_encode_semaphore = NULL; + } + if (video->gpu_encode_inactive) { + os_event_destroy(video->gpu_encode_inactive); + video->gpu_encode_inactive = NULL; + } + +#define free_circlebuf(x) \ + do { \ + while (x.size) { \ + struct obs_tex_frame frame; \ + circlebuf_pop_front(&x, &frame, sizeof(frame)); \ + gs_texture_destroy(frame.tex); \ + gs_texture_destroy(frame.tex_uv); \ + } \ + circlebuf_free(&x); \ + } while (false) + + free_circlebuf(video->gpu_encoder_queue); + free_circlebuf(video->gpu_encoder_avail_queue); +#undef free_circlebuf +} diff --git a/libobs/obs-video.c b/libobs/obs-video.c index aaae79a..4df10d0 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -58,8 +58,13 @@ static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time) source = data->first_source; while (source) { - obs_source_video_tick(source, seconds); + struct obs_source *cur_source = obs_source_get_ref(source); source = (struct obs_source*)source->context.next; + + if (cur_source) { + obs_source_video_tick(cur_source, seconds); + obs_source_release(cur_source); + } } pthread_mutex_unlock(&data->sources_mutex); @@ -115,9 +120,11 @@ static inline void render_main_texture(struct obs_core_video *video, int cur_texture) { profile_start(render_main_texture_name); + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_MAIN_TEXTURE, + render_main_texture_name); struct vec4 clear_color; - vec4_set(&clear_color, 0.0f, 0.0f, 0.0f, 1.0f); + vec4_set(&clear_color, 0.0f, 0.0f, 0.0f, 0.0f); gs_set_render_target(video->render_textures[cur_texture], NULL); gs_clear(GS_CLEAR_COLOR, &clear_color, 1.0f, 0); @@ -140,6 +147,7 @@ static inline void render_main_texture(struct obs_core_video *video, video->textures_rendered[cur_texture] = true; + GS_DEBUG_MARKER_END(); profile_end(render_main_texture_name); } @@ -207,7 +215,14 @@ static inline void render_output_texture(struct obs_core_video *video, 1.0f / (float)video->base_height); gs_effect_t *effect = get_scale_effect(video, width, height); - gs_technique_t *tech = gs_effect_get_technique(effect, "DrawMatrix"); + gs_technique_t *tech; + + if (video->ovi.output_format == VIDEO_FORMAT_RGBA) { + tech = gs_effect_get_technique(effect, "DrawAlphaDivide"); + } else { + tech = gs_effect_get_technique(effect, "DrawMatrix"); + } + gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_eparam_t *matrix = gs_effect_get_param_by_name(effect, "color_matrix"); @@ -303,6 +318,57 @@ end: profile_end(render_convert_texture_name); } +static void render_nv12(struct obs_core_video *video, gs_texture_t *target, + int cur_texture, int prev_texture, const char *tech_name, + uint32_t width, uint32_t height) +{ + gs_texture_t *texture = video->output_textures[prev_texture]; + + gs_effect_t *effect = video->conversion_effect; + gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); + gs_technique_t *tech = gs_effect_get_technique(effect, tech_name); + size_t passes, i; + + gs_effect_set_texture(image, texture); + + gs_set_render_target(target, NULL); + set_render_size(width, height); + + gs_enable_blending(false); + passes = gs_technique_begin(tech); + for (i = 0; i < passes; i++) { + gs_technique_begin_pass(tech, i); + gs_draw_sprite(texture, 0, width, height); + gs_technique_end_pass(tech); + } + gs_technique_end(tech); + gs_enable_blending(true); + + UNUSED_PARAMETER(cur_texture); +} + +static const char *render_convert_nv12_name = "render_convert_texture_nv12"; +static void render_convert_texture_nv12(struct obs_core_video *video, + int cur_texture, int prev_texture) +{ + profile_start(render_convert_nv12_name); + + if (!video->textures_output[prev_texture]) + goto end; + + render_nv12(video, video->convert_textures[cur_texture], + cur_texture, prev_texture, "NV12_Y", + video->output_width, video->output_height); + render_nv12(video, video->convert_uv_textures[cur_texture], + cur_texture, prev_texture, "NV12_UV", + video->output_width / 2, video->output_height / 2); + + video->textures_converted[cur_texture] = true; + +end: + profile_end(render_convert_nv12_name); +} + static const char *stage_output_texture_name = "stage_output_texture"; static inline void stage_output_texture(struct obs_core_video *video, int cur_texture, int prev_texture) @@ -334,7 +400,100 @@ end: profile_end(stage_output_texture_name); } -static inline void render_video(struct obs_core_video *video, bool raw_active, +#ifdef _WIN32 +static inline bool queue_frame(struct obs_core_video *video, bool raw_active, + struct obs_vframe_info *vframe_info, int prev_texture) +{ + bool duplicate = !video->gpu_encoder_avail_queue.size || + (video->gpu_encoder_queue.size && vframe_info->count > 1); + + if (duplicate) { + struct obs_tex_frame *tf = circlebuf_data( + &video->gpu_encoder_queue, + video->gpu_encoder_queue.size - sizeof(*tf)); + + /* texture-based encoding is stopping */ + if (!tf) { + return false; + } + + tf->count++; + os_sem_post(video->gpu_encode_semaphore); + goto finish; + } + + struct obs_tex_frame tf; + circlebuf_pop_front(&video->gpu_encoder_avail_queue, &tf, sizeof(tf)); + + if (tf.released) { + gs_texture_acquire_sync(tf.tex, tf.lock_key, GS_WAIT_INFINITE); + tf.released = false; + } + + /* the vframe_info->count > 1 case causing a copy can only happen if by + * some chance the very first frame has to be duplicated for whatever + * reason. otherwise, it goes to the 'duplicate' case above, which + * will ensure better performance. */ + if (raw_active || vframe_info->count > 1) { + gs_copy_texture(tf.tex, video->convert_textures[prev_texture]); + } else { + gs_texture_t *tex = video->convert_textures[prev_texture]; + gs_texture_t *tex_uv = video->convert_uv_textures[prev_texture]; + + video->convert_textures[prev_texture] = tf.tex; + video->convert_uv_textures[prev_texture] = tf.tex_uv; + + tf.tex = tex; + tf.tex_uv = tex_uv; + } + + tf.count = 1; + tf.timestamp = vframe_info->timestamp; + tf.released = true; + tf.handle = gs_texture_get_shared_handle(tf.tex); + gs_texture_release_sync(tf.tex, ++tf.lock_key); + circlebuf_push_back(&video->gpu_encoder_queue, &tf, sizeof(tf)); + + os_sem_post(video->gpu_encode_semaphore); + +finish: + return --vframe_info->count; +} + +extern void full_stop(struct obs_encoder *encoder); + +static inline void encode_gpu(struct obs_core_video *video, bool raw_active, + struct obs_vframe_info *vframe_info, int prev_texture) +{ + while (queue_frame(video, raw_active, vframe_info, prev_texture)); +} + +static const char *output_gpu_encoders_name = "output_gpu_encoders"; +static void output_gpu_encoders(struct obs_core_video *video, bool raw_active, + int prev_texture) +{ + profile_start(output_gpu_encoders_name); + + if (!video->textures_converted[prev_texture]) + goto end; + if (!video->vframe_info_buffer_gpu.size) + goto end; + + struct obs_vframe_info vframe_info; + circlebuf_pop_front(&video->vframe_info_buffer_gpu, &vframe_info, + sizeof(vframe_info)); + + pthread_mutex_lock(&video->gpu_encoder_mutex); + encode_gpu(video, raw_active, &vframe_info, prev_texture); + pthread_mutex_unlock(&video->gpu_encoder_mutex); + +end: + profile_end(output_gpu_encoders_name); +} +#endif + +static inline void render_video(struct obs_core_video *video, + bool raw_active, const bool gpu_active, int cur_texture, int prev_texture) { gs_begin_scene(); @@ -344,12 +503,34 @@ static inline void render_video(struct obs_core_video *video, bool raw_active, render_main_texture(video, cur_texture); - if (raw_active) { + if (raw_active || gpu_active) { render_output_texture(video, cur_texture, prev_texture); - if (video->gpu_conversion) - render_convert_texture(video, cur_texture, prev_texture); - stage_output_texture(video, cur_texture, prev_texture); +#ifdef _WIN32 + if (gpu_active) { + gs_flush(); + } +#endif + } + + if (raw_active || gpu_active) { + if (video->gpu_conversion) { + if (video->using_nv12_tex) + render_convert_texture_nv12(video, + cur_texture, prev_texture); + else + render_convert_texture(video, + cur_texture, prev_texture); + } + +#ifdef _WIN32 + if (gpu_active) { + gs_flush(); + output_gpu_encoders(video, raw_active, prev_texture); + } +#endif + if (raw_active) + stage_output_texture(video, cur_texture, prev_texture); } gs_set_render_target(NULL, NULL); @@ -448,6 +629,25 @@ static void set_gpu_converted_data(struct obs_core_video *video, video_frame_copy(output, &frame, info->format, info->height); + } else if (video->using_nv12_tex) { + size_t width = info->width; + size_t height = info->height; + size_t height_d2 = height / 2; + uint8_t *out_y = output->data[0]; + uint8_t *out_uv = output->data[1]; + uint8_t *in = input->data[0]; + + for (size_t y = 0; y < height; y++) { + memcpy(out_y, in, width); + out_y += output->linesize[0]; + in += input->linesize[0]; + } + for (size_t y = 0; y < height_d2; y++) { + memcpy(out_uv, in, width); + out_uv += output->linesize[0]; + in += input->linesize[0]; + } + } else { fix_gpu_converted_alignment(video, output, input); } @@ -525,7 +725,8 @@ static inline void output_video_data(struct obs_core_video *video, } } -static inline void video_sleep(struct obs_core_video *video, bool active, +static inline void video_sleep(struct obs_core_video *video, + bool raw_active, const bool gpu_active, uint64_t *p_time, uint64_t interval_ns) { struct obs_vframe_info vframe_info; @@ -546,9 +747,13 @@ static inline void video_sleep(struct obs_core_video *video, bool active, vframe_info.timestamp = cur_time; vframe_info.count = count; - if (active) + + if (raw_active) circlebuf_push_back(&video->vframe_info_buffer, &vframe_info, sizeof(vframe_info)); + if (gpu_active) + circlebuf_push_back(&video->vframe_info_buffer_gpu, + &vframe_info, sizeof(vframe_info)); } static const char *output_frame_gs_context_name = "gs_context(video->graphics)"; @@ -556,13 +761,13 @@ static const char *output_frame_render_video_name = "render_video"; static const char *output_frame_download_frame_name = "download_frame"; static const char *output_frame_gs_flush_name = "gs_flush"; static const char *output_frame_output_video_data_name = "output_video_data"; -static inline void output_frame(bool raw_active) +static inline void output_frame(bool raw_active, const bool gpu_active) { struct obs_core_video *video = &obs->video; int cur_texture = video->cur_texture; int prev_texture = cur_texture == 0 ? NUM_TEXTURES-1 : cur_texture-1; struct video_data frame; - bool frame_ready; + bool frame_ready = 0; memset(&frame, 0, sizeof(struct video_data)); @@ -570,7 +775,10 @@ static inline void output_frame(bool raw_active) gs_enter_context(video->graphics); profile_start(output_frame_render_video_name); - render_video(video, raw_active, cur_texture, prev_texture); + GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_RENDER_VIDEO, + output_frame_render_video_name); + render_video(video, raw_active, gpu_active, cur_texture, prev_texture); + GS_DEBUG_MARKER_END(); profile_end(output_frame_render_video_name); if (raw_active) { @@ -603,17 +811,31 @@ static inline void output_frame(bool raw_active) #define NBSP "\xC2\xA0" -static void clear_frame_data(void) +static void clear_base_frame_data(void) { struct obs_core_video *video = &obs->video; memset(video->textures_rendered, 0, sizeof(video->textures_rendered)); memset(video->textures_output, 0, sizeof(video->textures_output)); - memset(video->textures_copied, 0, sizeof(video->textures_copied)); memset(video->textures_converted, 0, sizeof(video->textures_converted)); circlebuf_free(&video->vframe_info_buffer); video->cur_texture = 0; } +static void clear_raw_frame_data(void) +{ + struct obs_core_video *video = &obs->video; + memset(video->textures_copied, 0, sizeof(video->textures_copied)); + circlebuf_free(&video->vframe_info_buffer); +} + +#ifdef _WIN32 +static void clear_gpu_frame_data(void) +{ + struct obs_core_video *video = &obs->video; + circlebuf_free(&video->vframe_info_buffer_gpu); +} +#endif + static const char *tick_sources_name = "tick_sources"; static const char *render_displays_name = "render_displays"; static const char *output_frame_name = "output_frame"; @@ -624,7 +846,11 @@ void *obs_graphics_thread(void *param) uint64_t frame_time_total_ns = 0; uint64_t fps_total_ns = 0; uint32_t fps_total_frames = 0; +#ifdef _WIN32 + bool gpu_was_active = false; +#endif bool raw_was_active = false; + bool was_active = false; obs->video.video_time = os_gettime_ns(); @@ -641,10 +867,26 @@ void *obs_graphics_thread(void *param) uint64_t frame_start = os_gettime_ns(); uint64_t frame_time_ns; bool raw_active = obs->video.raw_active > 0; +#ifdef _WIN32 + const bool gpu_active = obs->video.gpu_encoder_active > 0; + const bool active = raw_active || gpu_active; +#else + const bool gpu_active = 0; + const bool active = raw_active; +#endif + if (!was_active && active) + clear_base_frame_data(); if (!raw_was_active && raw_active) - clear_frame_data(); + clear_raw_frame_data(); +#ifdef _WIN32 + if (!gpu_was_active && gpu_active) + clear_gpu_frame_data(); + + gpu_was_active = gpu_active; +#endif raw_was_active = raw_active; + was_active = active; profile_start(video_thread_name); @@ -653,7 +895,7 @@ void *obs_graphics_thread(void *param) profile_end(tick_sources_name); profile_start(output_frame_name); - output_frame(raw_active); + output_frame(raw_active, gpu_active); profile_end(output_frame_name); profile_start(render_displays_name); @@ -666,8 +908,8 @@ void *obs_graphics_thread(void *param) profile_reenable_thread(); - video_sleep(&obs->video, raw_active, &obs->video.video_time, - interval); + video_sleep(&obs->video, raw_active, gpu_active, + &obs->video.video_time, interval); frame_time_total_ns += frame_time_ns; fps_total_ns += (obs->video.video_time - last_time); diff --git a/libobs/obs-windows.c b/libobs/obs-windows.c index e6bbcb0..d2a5bde 100644 --- a/libobs/obs-windows.c +++ b/libobs/obs-windows.c @@ -82,7 +82,7 @@ static void log_processor_info(void) DWORD size, speed; LSTATUS status; - memset(data, 0, 1024); + memset(data, 0, sizeof(data)); status = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", @@ -90,7 +90,7 @@ static void log_processor_info(void) if (status != ERROR_SUCCESS) return; - size = 1024; + size = sizeof(data); status = RegQueryValueExW(key, L"ProcessorNameString", NULL, NULL, (LPBYTE)data, &size); if (status == ERROR_SUCCESS) { @@ -220,6 +220,10 @@ static void log_gaming_features(void) L"HistoricalCaptureEnabled", &game_dvr_bg_recording); get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY, L"AllowAutoGameMode", &game_mode_enabled); + if (game_mode_enabled.status != ERROR_SUCCESS) { + get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY, + L"AutoGameModeEnabled", &game_mode_enabled); + } blog(LOG_INFO, "Windows 10 Gaming Features:"); if (game_bar_enabled.status == ERROR_SUCCESS) { diff --git a/libobs/obs.c b/libobs/obs.c index acd51bb..acefb3e 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -164,17 +164,42 @@ static bool obs_init_gpu_conversion(struct obs_video_info *ovi) calc_gpu_conversion_sizes(ovi); + video->using_nv12_tex = ovi->output_format == VIDEO_FORMAT_NV12 + ? gs_nv12_available() : false; + if (!video->conversion_height) { blog(LOG_INFO, "GPU conversion not available for format: %u", (unsigned int)ovi->output_format); video->gpu_conversion = false; + video->using_nv12_tex = false; + blog(LOG_INFO, "NV12 texture support not available"); return true; } + if (video->using_nv12_tex) + blog(LOG_INFO, "NV12 texture support enabled"); + else + blog(LOG_INFO, "NV12 texture support not available"); + for (size_t i = 0; i < NUM_TEXTURES; i++) { - video->convert_textures[i] = gs_texture_create( - ovi->output_width, video->conversion_height, - GS_RGBA, 1, NULL, GS_RENDER_TARGET); +#ifdef _WIN32 + if (video->using_nv12_tex) { + gs_texture_create_nv12( + &video->convert_textures[i], + &video->convert_uv_textures[i], + ovi->output_width, ovi->output_height, + GS_RENDER_TARGET | GS_SHARED_KM_TEX); + if (!video->convert_uv_textures[i]) + return false; + } else { +#endif + video->convert_textures[i] = gs_texture_create( + ovi->output_width, + video->conversion_height, + GS_RGBA, 1, NULL, GS_RENDER_TARGET); +#ifdef _WIN32 + } +#endif if (!video->convert_textures[i]) return false; @@ -191,11 +216,23 @@ static bool obs_init_textures(struct obs_video_info *ovi) size_t i; for (i = 0; i < NUM_TEXTURES; i++) { - video->copy_surfaces[i] = gs_stagesurface_create( - ovi->output_width, output_height, GS_RGBA); +#ifdef _WIN32 + if (video->using_nv12_tex) { + video->copy_surfaces[i] = gs_stagesurface_create_nv12( + ovi->output_width, ovi->output_height); + if (!video->copy_surfaces[i]) + return false; - if (!video->copy_surfaces[i]) - return false; + } else { +#endif + video->copy_surfaces[i] = gs_stagesurface_create( + ovi->output_width, output_height, + GS_RGBA); + if (!video->copy_surfaces[i]) + return false; +#ifdef _WIN32 + } +#endif video->render_textures[i] = gs_texture_create( ovi->base_width, ovi->base_height, @@ -272,6 +309,11 @@ static int obs_init_graphics(struct obs_video_info *ovi) NULL); bfree(filename); + filename = obs_find_data_file("repeat.effect"); + video->repeat_effect = gs_effect_create_from_file(filename, + NULL); + bfree(filename); + filename = obs_find_data_file("format_conversion.effect"); video->conversion_effect = gs_effect_create_from_file(filename, NULL); @@ -287,6 +329,11 @@ static int obs_init_graphics(struct obs_video_info *ovi) NULL); bfree(filename); + filename = obs_find_data_file("area.effect"); + video->area_effect = gs_effect_create_from_file(filename, + NULL); + bfree(filename); + filename = obs_find_data_file("bilinear_lowres_scale.effect"); video->bilinear_lowres_effect = gs_effect_create_from_file(filename, NULL); @@ -297,6 +344,7 @@ static int obs_init_graphics(struct obs_video_info *ovi) NULL); bfree(filename); + point_sampler.max_anisotropy = 1; video->point_sampler = gs_samplerstate_create(&point_sampler); obs->video.transparent_texture = gs_texture_create(2, 2, GS_RGBA, 1, @@ -351,6 +399,7 @@ static int obs_init_video(struct obs_video_info *ovi) { struct obs_core_video *video = &obs->video; struct video_output_info vi; + pthread_mutexattr_t attr; int errorcode; make_video_info(&vi, ovi); @@ -384,6 +433,13 @@ static int obs_init_video(struct obs_video_info *ovi) gs_leave_context(); + if (pthread_mutexattr_init(&attr) != 0) + return OBS_VIDEO_FAIL; + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) + return OBS_VIDEO_FAIL; + if (pthread_mutex_init(&video->gpu_encoder_mutex, NULL) < 0) + return OBS_VIDEO_FAIL; + errorcode = pthread_create(&video->video_thread, NULL, obs_graphics_thread, obs); if (errorcode != 0) @@ -431,17 +487,20 @@ static void obs_free_video(void) gs_stagesurface_destroy(video->copy_surfaces[i]); gs_texture_destroy(video->render_textures[i]); gs_texture_destroy(video->convert_textures[i]); + gs_texture_destroy(video->convert_uv_textures[i]); gs_texture_destroy(video->output_textures[i]); - video->copy_surfaces[i] = NULL; - video->render_textures[i] = NULL; - video->convert_textures[i] = NULL; - video->output_textures[i] = NULL; + video->copy_surfaces[i] = NULL; + video->render_textures[i] = NULL; + video->convert_textures[i] = NULL; + video->convert_uv_textures[i] = NULL; + video->output_textures[i] = NULL; } gs_leave_context(); circlebuf_free(&video->vframe_info_buffer); + circlebuf_free(&video->vframe_info_buffer_gpu); memset(&video->textures_rendered, 0, sizeof(video->textures_rendered)); @@ -452,6 +511,11 @@ static void obs_free_video(void) memset(&video->textures_converted, 0, sizeof(video->textures_converted)); + pthread_mutex_destroy(&video->gpu_encoder_mutex); + pthread_mutex_init_value(&video->gpu_encoder_mutex); + da_free(video->gpu_encoders); + + video->gpu_encoder_active = 0; video->cur_texture = 0; } } @@ -473,7 +537,9 @@ static void obs_free_graphics(void) gs_effect_destroy(video->solid_effect); gs_effect_destroy(video->conversion_effect); gs_effect_destroy(video->bicubic_effect); + gs_effect_destroy(video->repeat_effect); gs_effect_destroy(video->lanczos_effect); + gs_effect_destroy(video->area_effect); gs_effect_destroy(video->bilinear_lowres_effect); video->default_effect = NULL; @@ -753,6 +819,7 @@ static bool obs_init(const char *locale, const char *module_config_path, obs = bzalloc(sizeof(struct obs_core)); pthread_mutex_init_value(&obs->audio.monitoring_mutex); + pthread_mutex_init_value(&obs->video.gpu_encoder_mutex); obs->name_store_owned = !store; obs->name_store = store ? store : profiler_name_store_create(); @@ -853,6 +920,42 @@ bool obs_startup(const char *locale, const char *module_config_path, return success; } +static struct obs_cmdline_args cmdline_args = {0, NULL}; +void obs_set_cmdline_args(int argc, const char * const *argv) +{ + char *data; + size_t len; + int i; + + /* Once argc is set (non-zero) we shouldn't call again */ + if (cmdline_args.argc) + return; + + cmdline_args.argc = argc; + + /* Safely copy over argv */ + len = 0; + for (i = 0; i < argc; i++) + len += strlen(argv[i]) + 1; + + cmdline_args.argv = bmalloc(sizeof(char *) * (argc + 1) + len); + data = (char *) cmdline_args.argv + sizeof(char *) * (argc + 1); + + for (i = 0; i < argc; i++) { + cmdline_args.argv[i] = data; + len = strlen(argv[i]) + 1; + memcpy(data, argv[i], len); + data += len; + } + + cmdline_args.argv[argc] = NULL; +} + +struct obs_cmdline_args obs_get_cmdline_args(void) +{ + return cmdline_args; +} + void obs_shutdown(void) { struct obs_module *module; @@ -918,6 +1021,7 @@ void obs_shutdown(void) bfree(core->module_config_path); bfree(core->locale); bfree(core); + bfree(cmdline_args.argv); #ifdef _WIN32 uninitialize_com(); @@ -977,7 +1081,7 @@ int obs_reset_video(struct obs_video_info *ovi) if (!obs) return OBS_VIDEO_FAIL; /* don't allow changing of video settings if active. */ - if (obs->video.video && video_output_active(obs->video.video)) + if (obs->video.video && obs_video_active()) return OBS_VIDEO_CURRENTLY_ACTIVE; if (!size_valid(ovi->output_width, ovi->output_height) || @@ -1018,11 +1122,15 @@ int obs_reset_video(struct obs_video_info *ovi) case OBS_SCALE_LANCZOS: scale_type_name = "Lanczos"; break; + case OBS_SCALE_AREA: + scale_type_name = "Area"; + break; } bool yuv = format_is_yuv(ovi->output_format); const char *yuv_format = get_video_colorspace_name(ovi->colorspace); - const char *yuv_range = get_video_range_name(ovi->range); + const char *yuv_range = get_video_range_name(ovi->output_format, + ovi->range); blog(LOG_INFO, "---------------------------------"); blog(LOG_INFO, "video settings reset:\n" @@ -1323,6 +1431,31 @@ void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param) pthread_mutex_unlock(&obs->data.sources_mutex); } +void obs_enum_scenes(bool (*enum_proc)(void*, obs_source_t*), void *param) +{ + obs_source_t *source; + + if (!obs) return; + + pthread_mutex_lock(&obs->data.sources_mutex); + source = obs->data.first_source; + + while (source) { + obs_source_t *next_source = + (obs_source_t*)source->context.next; + + if (source->info.type == OBS_SOURCE_TYPE_SCENE && + !source->context.private && + !enum_proc(param, source)) { + break; + } + + source = next_source; + } + + pthread_mutex_unlock(&obs->data.sources_mutex); +} + static inline void obs_enum(void *pstart, pthread_mutex_t *mutex, void *proc, void *param) { @@ -1454,10 +1587,14 @@ gs_effect_t *obs_get_base_effect(enum obs_base_effect effect) return obs->video.opaque_effect; case OBS_EFFECT_SOLID: return obs->video.solid_effect; + case OBS_EFFECT_REPEAT: + return obs->video.repeat_effect; case OBS_EFFECT_BICUBIC: return obs->video.bicubic_effect; case OBS_EFFECT_LANCZOS: return obs->video.lanczos_effect; + case OBS_EFFECT_AREA: + return obs->video.area_effect; case OBS_EFFECT_BILINEAR_LOWRES: return obs->video.bilinear_lowres_effect; case OBS_EFFECT_PREMULTIPLIED_ALPHA: @@ -1514,8 +1651,13 @@ void obs_render_main_texture(void) param = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(param, tex); + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); + while (gs_effect_loop(effect, "Draw")) gs_draw_sprite(tex, 0, 0, 0); + + gs_blend_state_pop(); } gs_texture_t *obs_get_main_texture(void) @@ -1563,6 +1705,7 @@ static obs_source_t *obs_load_source_type(obs_data_t *source_data) obs_data_t *settings = obs_data_get_obj(source_data, "settings"); obs_data_t *hotkeys = obs_data_get_obj(source_data, "hotkeys"); double volume; + double balance; int64_t sync; uint32_t flags; uint32_t mixers; @@ -1578,6 +1721,10 @@ static obs_source_t *obs_load_source_type(obs_data_t *source_data) volume = obs_data_get_double(source_data, "volume"); obs_source_set_volume(source, (float)volume); + obs_data_set_default_double(source_data, "balance", 0.5); + balance = obs_data_get_double(source_data, "balance"); + obs_source_set_balance_value(source, (float)balance); + sync = obs_data_get_int(source_data, "sync"); obs_source_set_sync_offset(source, sync); @@ -1694,6 +1841,11 @@ void obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb, if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) obs_transition_load(source, source_data); obs_source_load(source); + for (size_t i = source->filters.num; i > 0; i--) { + obs_source_t *filter = + source->filters.array[i - 1]; + obs_source_load(filter); + } if (cb) cb(private_data, source); } @@ -1716,6 +1868,7 @@ obs_data_t *obs_save_source(obs_source_t *source) obs_data_t *hotkey_data = source->context.hotkey_data; obs_data_t *hotkeys; float volume = obs_source_get_volume(source); + float balance = obs_source_get_balance_value(source); uint32_t mixers = obs_source_get_audio_mixers(source); int64_t sync = obs_source_get_sync_offset(source); uint32_t flags = obs_source_get_flags(source); @@ -1748,6 +1901,7 @@ obs_data_t *obs_save_source(obs_source_t *source) obs_data_set_int (source_data, "sync", sync); obs_data_set_int (source_data, "flags", flags); obs_data_set_double(source_data, "volume", volume); + obs_data_set_double(source_data, "balance", balance); obs_data_set_bool (source_data, "enabled", enabled); obs_data_set_bool (source_data, "muted", muted); obs_data_set_bool (source_data, "push-to-mute", push_to_mute); @@ -2013,6 +2167,15 @@ bool obs_obj_invalid(void *obj) return !context->data; } +void *obs_obj_get_data(void *obj) +{ + struct obs_context_data *context = obj; + if (!context) + return NULL; + + return context->data; +} + bool obs_set_audio_monitoring_device(const char *name, const char *id) { if (!obs || !name || !id || !*name || !*id) @@ -2189,3 +2352,79 @@ obs_data_t *obs_get_private_data(void) obs_data_addref(private_data); return private_data; } + +extern bool init_gpu_encoding(struct obs_core_video *video); +extern void stop_gpu_encoding_thread(struct obs_core_video *video); +extern void free_gpu_encoding(struct obs_core_video *video); + +bool start_gpu_encode(obs_encoder_t *encoder) +{ + struct obs_core_video *video = &obs->video; + bool success = true; + + obs_enter_graphics(); + pthread_mutex_lock(&video->gpu_encoder_mutex); + + if (!video->gpu_encoders.num) + success = init_gpu_encoding(video); + if (success) + da_push_back(video->gpu_encoders, &encoder); + else + free_gpu_encoding(video); + + pthread_mutex_unlock(&video->gpu_encoder_mutex); + obs_leave_graphics(); + + if (success) { + os_atomic_inc_long(&video->gpu_encoder_active); + video_output_inc_texture_encoders(video->video); + } + + return success; +} + +void stop_gpu_encode(obs_encoder_t *encoder) +{ + struct obs_core_video *video = &obs->video; + bool call_free = false; + + os_atomic_dec_long(&video->gpu_encoder_active); + video_output_dec_texture_encoders(video->video); + + pthread_mutex_lock(&video->gpu_encoder_mutex); + da_erase_item(video->gpu_encoders, &encoder); + if (!video->gpu_encoders.num) + call_free = true; + pthread_mutex_unlock(&video->gpu_encoder_mutex); + + os_event_wait(video->gpu_encode_inactive); + + if (call_free) { + stop_gpu_encoding_thread(video); + + obs_enter_graphics(); + pthread_mutex_lock(&video->gpu_encoder_mutex); + free_gpu_encoding(video); + pthread_mutex_unlock(&video->gpu_encoder_mutex); + obs_leave_graphics(); + } +} + +bool obs_video_active(void) +{ + struct obs_core_video *video = &obs->video; + if (!obs) + return false; + + return os_atomic_load_long(&video->raw_active) > 0 || + os_atomic_load_long(&video->gpu_encoder_active) > 0; +} + +bool obs_nv12_tex_active(void) +{ + struct obs_core_video *video = &obs->video; + if (!obs) + return false; + + return video->using_nv12_tex; +} diff --git a/libobs/obs.h b/libobs/obs.h index 181bd6f..f6e76ca 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -116,7 +116,8 @@ enum obs_scale_type { OBS_SCALE_POINT, OBS_SCALE_BICUBIC, OBS_SCALE_BILINEAR, - OBS_SCALE_LANCZOS + OBS_SCALE_LANCZOS, + OBS_SCALE_AREA, }; /** @@ -219,6 +220,10 @@ struct obs_source_audio { * * If a YUV format is specified, it will be automatically upsampled and * converted to RGB via shader on the graphics processor. + * + * NOTE: Non-YUV formats will always be treated as full range with this + * structure! Use obs_source_frame2 along with obs_source_output_video2 + * instead if partial range support is desired for non-YUV video formats. */ struct obs_source_frame { uint8_t *data[MAX_AV_PLANES]; @@ -239,6 +244,27 @@ struct obs_source_frame { bool prev_frame; }; +struct obs_source_frame2 { + uint8_t *data[MAX_AV_PLANES]; + uint32_t linesize[MAX_AV_PLANES]; + uint32_t width; + uint32_t height; + uint64_t timestamp; + + enum video_format format; + enum video_range_type range; + float color_matrix[16]; + float color_range_min[3]; + float color_range_max[3]; + bool flip; +}; + +/** Access to the argc/argv used to start OBS. What you see is what you get. */ +struct obs_cmdline_args { + int argc; + char **argv; +}; + /* ------------------------------------------------------------------------- */ /* OBS context */ @@ -291,6 +317,24 @@ EXPORT uint32_t obs_get_version(void); /** @return The current core version string */ EXPORT const char *obs_get_version_string(void); +/** + * Sets things up for calls to obs_get_cmdline_args. Called onl yonce at startup + * and safely copies argv/argc from main(). Subsequent calls do nothing. + * + * @param argc The count of command line arguments, from main() + * @param argv An array of command line arguments, copied from main() and ends + * with NULL. + */ +EXPORT void obs_set_cmdline_args(int argc, const char * const *argv); + +/** + * Get the argc/argv used to start OBS + * + * @return The command line arguments used for main(). Don't modify this or + * you'll mess things up for other callers. + */ +EXPORT struct obs_cmdline_args obs_get_cmdline_args(void); + /** * Sets a new locale to use for modules. This will call obs_module_set_locale * for each module with the new locale. @@ -511,6 +555,9 @@ EXPORT audio_t *obs_get_audio(void); /** Gets the main video output handler for this OBS context */ EXPORT video_t *obs_get_video(void); +/** Returns true if video is active, false otherwise */ +EXPORT bool obs_video_active(void); + /** Sets the primary output source for a channel. */ EXPORT void obs_set_output_source(uint32_t channel, obs_source_t *source); @@ -532,6 +579,10 @@ EXPORT obs_source_t *obs_get_output_source(uint32_t channel); EXPORT void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param); +/** Enumerates scenes */ +EXPORT void obs_enum_scenes(bool (*enum_proc)(void*, obs_source_t*), + void *param); + /** Enumerates outputs */ EXPORT void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t*), void *param); @@ -570,6 +621,8 @@ enum obs_base_effect { OBS_EFFECT_LANCZOS, /**< Lanczos downscale */ OBS_EFFECT_BILINEAR_LOWRES, /**< Bilinear low resolution downscale */ OBS_EFFECT_PREMULTIPLIED_ALPHA,/**< Premultiplied alpha */ + OBS_EFFECT_REPEAT, /**< RGB/YUV (repeating) */ + OBS_EFFECT_AREA, /**< Area rescale */ }; /** Returns a commonly used base effect */ @@ -642,6 +695,7 @@ enum obs_obj_type { EXPORT enum obs_obj_type obs_obj_get_type(void *obj); EXPORT const char *obs_obj_get_id(void *obj); EXPORT bool obs_obj_invalid(void *obj); +EXPORT void *obs_obj_get_data(void *obj); typedef bool (*obs_enum_audio_device_cb)(void *data, const char *name, const char *id); @@ -682,6 +736,8 @@ EXPORT uint64_t obs_get_average_frame_time_ns(void); EXPORT uint32_t obs_get_total_frames(void); EXPORT uint32_t obs_get_lagged_frames(void); +EXPORT bool obs_nv12_tex_active(void); + EXPORT void obs_apply_private_data(obs_data_t *settings); EXPORT void obs_set_private_data(obs_data_t *settings); EXPORT obs_data_t *obs_get_private_data(void); @@ -724,7 +780,8 @@ EXPORT void obs_view_render(obs_view_t *view); * @return The new display context, or NULL if failed. */ EXPORT obs_display_t *obs_display_create( - const struct gs_init_data *graphics_data); + const struct gs_init_data *graphics_data, + uint32_t backround_color); /** Destroys a display context */ EXPORT void obs_display_destroy(obs_display_t *display); @@ -756,6 +813,9 @@ EXPORT bool obs_display_enabled(obs_display_t *display); EXPORT void obs_display_set_background_color(obs_display_t *display, uint32_t color); +EXPORT void obs_display_size(obs_display_t *display, + uint32_t *width, uint32_t *height); + /* ------------------------------------------------------------------------- */ /* Sources */ @@ -893,6 +953,15 @@ EXPORT void obs_source_set_volume(obs_source_t *source, float volume); /** Gets the user volume for a source that has audio output */ EXPORT float obs_source_get_volume(const obs_source_t *source); +/* Gets speaker layout of a source */ +EXPORT enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source); + +/** Sets the balance value for a stereo audio source */ +EXPORT void obs_source_set_balance_value(obs_source_t *source, float balance); + +/** Gets the balance value for a stereo audio source */ +EXPORT float obs_source_get_balance_value(const obs_source_t *source); + /** Sets the audio sync offset (in nanoseconds) for a source */ EXPORT void obs_source_set_sync_offset(obs_source_t *source, int64_t offset); @@ -948,6 +1017,18 @@ EXPORT uint32_t obs_source_get_audio_mixers(const obs_source_t *source); */ EXPORT void obs_source_inc_showing(obs_source_t *source); +/** + * Increments the 'active' reference counter to indicate that the source is + * fully active. If the reference counter was 0, will call the 'activate' + * callback. + * + * Unlike obs_source_inc_showing, this will cause children of this source to be + * considered showing as well (currently used by transition previews to make + * the stinger transition show correctly). obs_source_inc_showing should + * generally be used instead. + */ +EXPORT void obs_source_inc_active(obs_source_t *source); + /** * Decrements the 'showing' reference counter to indicate that the source is * no longer being shown somewhere. If the reference counter is set to 0, @@ -955,6 +1036,17 @@ EXPORT void obs_source_inc_showing(obs_source_t *source); */ EXPORT void obs_source_dec_showing(obs_source_t *source); +/** + * Decrements the 'active' reference counter to indicate that the source is no + * longer fully active. If the reference counter is set to 0, will call the + * 'deactivate' callback + * + * Unlike obs_source_dec_showing, this will cause children of this source to be + * considered not showing as well. obs_source_dec_showing should generally be + * used instead. + */ +EXPORT void obs_source_dec_active(obs_source_t *source); + /** Enumerates filters assigned to the source */ EXPORT void obs_source_enum_filters(obs_source_t *source, obs_source_enum_proc_t callback, void *param); @@ -1070,13 +1162,29 @@ EXPORT void obs_source_draw_set_color_matrix( EXPORT void obs_source_draw(gs_texture_t *image, int x, int y, uint32_t cx, uint32_t cy, bool flip); -/** Outputs asynchronous video data. Set to NULL to deactivate the texture */ +/** + * Outputs asynchronous video data. Set to NULL to deactivate the texture + * + * NOTE: Non-YUV formats will always be treated as full range with this + * function! Use obs_source_output_video2 instead if partial range support is + * desired for non-YUV video formats. + */ EXPORT void obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame); +EXPORT void obs_source_output_video2(obs_source_t *source, + const struct obs_source_frame2 *frame); -/** Preloads asynchronous video data to allow instantaneous playback */ +/** + * Preloads asynchronous video data to allow instantaneous playback + * + * NOTE: Non-YUV formats will always be treated as full range with this + * function! Use obs_source_preload_video2 instead if partial range support is + * desired for non-YUV video formats. + */ EXPORT void obs_source_preload_video(obs_source_t *source, const struct obs_source_frame *frame); +EXPORT void obs_source_preload_video2(obs_source_t *source, + const struct obs_source_frame2 *frame); /** Shows any preloaded video data */ EXPORT void obs_source_show_preloaded_video(obs_source_t *source); @@ -1257,6 +1365,8 @@ typedef float (*obs_transition_audio_mix_callback_t)(void *data, float t); EXPORT float obs_transition_get_time(obs_source_t *transition); +EXPORT void obs_transition_force_stop(obs_source_t *transition); + EXPORT void obs_transition_video_render(obs_source_t *transition, obs_transition_video_render_callback_t callback); @@ -1406,6 +1516,8 @@ EXPORT void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform); EXPORT void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item, struct matrix4 *transform); +EXPORT void obs_sceneitem_get_box_scale(const obs_sceneitem_t *item, + struct vec2 *scale); EXPORT bool obs_sceneitem_visible(const obs_sceneitem_t *item); EXPORT bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible); @@ -1594,6 +1706,12 @@ EXPORT void obs_output_set_mixer(obs_output_t *output, size_t mixer_idx); /** Gets the current audio mixer for non-encoded outputs */ EXPORT size_t obs_output_get_mixer(const obs_output_t *output); +/** Sets the current audio mixes (mask) for a non-encoded multi-track output */ +EXPORT void obs_output_set_mixers(obs_output_t *output, size_t mixers); + +/** Gets the current audio mixes (mask) for a non-encoded multi-track output */ +EXPORT size_t obs_output_get_mixers(const obs_output_t *output); + /** * Sets the current video encoder associated with this output, * required for encoded outputs @@ -1664,6 +1782,8 @@ EXPORT const char *obs_output_get_id(const obs_output_t *output); #if BUILD_CAPTIONS EXPORT void obs_output_output_caption_text1(obs_output_t *output, const char *text); +EXPORT void obs_output_output_caption_text2(obs_output_t *output, + const char *text, double display_duration); #endif EXPORT float obs_output_get_congestion(obs_output_t *output); @@ -1871,6 +1991,7 @@ EXPORT void *obs_encoder_get_type_data(obs_encoder_t *encoder); EXPORT const char *obs_encoder_get_id(const obs_encoder_t *encoder); EXPORT uint32_t obs_get_encoder_caps(const char *encoder_id); +EXPORT uint32_t obs_encoder_get_caps(const obs_encoder_t *encoder); #ifndef SWIG /** Duplicates an encoder packet */ @@ -1886,6 +2007,9 @@ EXPORT void obs_encoder_packet_ref(struct encoder_packet *dst, struct encoder_packet *src); EXPORT void obs_encoder_packet_release(struct encoder_packet *packet); +EXPORT void *obs_encoder_create_rerouted(obs_encoder_t *encoder, + const char *reroute_id); + /* ------------------------------------------------------------------------- */ /* Stream Services */ diff --git a/libobs/util/apple/cfstring-utils.h b/libobs/util/apple/cfstring-utils.h new file mode 100644 index 0000000..d131fbb --- /dev/null +++ b/libobs/util/apple/cfstring-utils.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../c99defs.h" +#include "../dstr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +EXPORT char *cfstr_copy_cstr(CFStringRef cfstr, CFStringEncoding cfstr_enc); + +EXPORT bool cfstr_copy_dstr(CFStringRef cfstr, CFStringEncoding cfstr_enc, + struct dstr *str); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/util/circlebuf.h b/libobs/util/circlebuf.h index e6eaca4..b991612 100644 --- a/libobs/util/circlebuf.h +++ b/libobs/util/circlebuf.h @@ -292,7 +292,7 @@ static inline void circlebuf_pop_front(struct circlebuf *cb, void *data, static inline void circlebuf_pop_back(struct circlebuf *cb, void *data, size_t size) { - circlebuf_peek_front(cb, data, size); + circlebuf_peek_back(cb, data, size); cb->size -= size; if (!cb->size) { @@ -311,7 +311,7 @@ static inline void *circlebuf_data(struct circlebuf *cb, size_t idx) uint8_t *ptr = (uint8_t*)cb->data; size_t offset = cb->start_pos + idx; - if (idx > cb->size) + if (idx >= cb->size) return NULL; if (offset >= cb->capacity) diff --git a/libobs/util/config-file.c b/libobs/util/config-file.c index 8040c50..317f810 100644 --- a/libobs/util/config-file.c +++ b/libobs/util/config-file.c @@ -359,6 +359,7 @@ int config_save(config_t *config) FILE *f; struct dstr str, tmp; size_t i, j; + int ret = CONFIG_ERROR; if (!config) return CONFIG_ERROR; @@ -405,9 +406,15 @@ int config_save(config_t *config) } #ifdef _WIN32 - fwrite("\xEF\xBB\xBF", 1, 3, f); + if (fwrite("\xEF\xBB\xBF", 3, 1, f) != 1) + goto cleanup; #endif - fwrite(str.array, 1, str.len, f); + if (fwrite(str.array, str.len, 1, f) != 1) + goto cleanup; + + ret = CONFIG_SUCCESS; + +cleanup: fclose(f); pthread_mutex_unlock(&config->mutex); @@ -415,7 +422,7 @@ int config_save(config_t *config) dstr_free(&tmp); dstr_free(&str); - return CONFIG_SUCCESS; + return ret; } int config_save_safe(config_t *config, const char *temp_ext, @@ -444,6 +451,8 @@ int config_save_safe(config_t *config, const char *temp_ext, config->file = file; if (ret != CONFIG_SUCCESS) { + blog(LOG_ERROR, "config_save_safe: failed to " + "write to %s", temp_file.array); goto cleanup; } diff --git a/libobs/util/pipe-posix.c b/libobs/util/pipe-posix.c index 195f6e0..3d77686 100644 --- a/libobs/util/pipe-posix.c +++ b/libobs/util/pipe-posix.c @@ -73,6 +73,15 @@ size_t os_process_pipe_read(os_process_pipe_t *pp, uint8_t *data, size_t len) return fread(data, 1, len, pp->file); } +size_t os_process_pipe_read_err(os_process_pipe_t *pp, uint8_t *data, size_t len) +{ + /* XXX: unsupported on posix */ + UNUSED_PARAMETER(pp); + UNUSED_PARAMETER(data); + UNUSED_PARAMETER(len); + return 0; +} + size_t os_process_pipe_write(os_process_pipe_t *pp, const uint8_t *data, size_t len) { diff --git a/libobs/util/pipe-windows.c b/libobs/util/pipe-windows.c index d3d35e2..5510072 100644 --- a/libobs/util/pipe-windows.c +++ b/libobs/util/pipe-windows.c @@ -24,6 +24,7 @@ struct os_process_pipe { bool read_pipe; HANDLE handle; + HANDLE handle_err; HANDLE process; }; @@ -42,7 +43,7 @@ static bool create_pipe(HANDLE *input, HANDLE *output) } static inline bool create_process(const char *cmd_line, HANDLE stdin_handle, - HANDLE stdout_handle, HANDLE *process) + HANDLE stdout_handle, HANDLE stderr_handle, HANDLE *process) { PROCESS_INFORMATION pi = {0}; wchar_t *cmd_line_w = NULL; @@ -53,6 +54,7 @@ static inline bool create_process(const char *cmd_line, HANDLE stdin_handle, si.dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK; si.hStdInput = stdin_handle; si.hStdOutput = stdout_handle; + si.hStdError = stderr_handle; os_utf8_to_wcs_ptr(cmd_line, 0, &cmd_line_w); if (cmd_line_w) { @@ -77,6 +79,7 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, bool read_pipe; HANDLE process; HANDLE output; + HANDLE err_input, err_output; HANDLE input; bool success; @@ -90,26 +93,38 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, return NULL; } + if (!create_pipe(&err_input, &err_output)) { + return NULL; + } + read_pipe = *type == 'r'; success = !!SetHandleInformation(read_pipe ? input : output, - HANDLE_FLAG_INHERIT, false); + HANDLE_FLAG_INHERIT, false); + if (!success) { + goto error; + } + + success = !!SetHandleInformation(err_input, HANDLE_FLAG_INHERIT, false); if (!success) { goto error; } success = create_process(cmd_line, read_pipe ? NULL : input, - read_pipe ? output : NULL, &process); + read_pipe ? output : NULL, err_output, &process); if (!success) { goto error; } pp = bmalloc(sizeof(*pp)); + pp->handle = read_pipe ? input : output; pp->read_pipe = read_pipe; pp->process = process; + pp->handle_err = err_input; CloseHandle(read_pipe ? output : input); + CloseHandle(err_output); return pp; error: @@ -126,6 +141,7 @@ int os_process_pipe_destroy(os_process_pipe_t *pp) DWORD code; CloseHandle(pp->handle); + CloseHandle(pp->handle_err); WaitForSingleObject(pp->process, INFINITE); if (GetExitCodeProcess(pp->process, &code)) @@ -158,6 +174,25 @@ size_t os_process_pipe_read(os_process_pipe_t *pp, uint8_t *data, size_t len) return 0; } +size_t os_process_pipe_read_err(os_process_pipe_t *pp, uint8_t *data, size_t len) +{ + DWORD bytes_read; + bool success; + + if (!pp || !pp->handle_err) { + return 0; + } + + success = !!ReadFile(pp->handle_err, data, (DWORD)len, &bytes_read, NULL); + if (success && bytes_read) { + return bytes_read; + } + else + bytes_read = GetLastError(); + + return 0; +} + size_t os_process_pipe_write(os_process_pipe_t *pp, const uint8_t *data, size_t len) { diff --git a/libobs/util/pipe.h b/libobs/util/pipe.h index cb595fd..5c44514 100644 --- a/libobs/util/pipe.h +++ b/libobs/util/pipe.h @@ -27,5 +27,7 @@ EXPORT int os_process_pipe_destroy(os_process_pipe_t *pp); EXPORT size_t os_process_pipe_read(os_process_pipe_t *pp, uint8_t *data, size_t len); +EXPORT size_t os_process_pipe_read_err(os_process_pipe_t *pp, uint8_t *data, + size_t len); EXPORT size_t os_process_pipe_write(os_process_pipe_t *pp, const uint8_t *data, size_t len); diff --git a/libobs/util/platform-cocoa.m b/libobs/util/platform-cocoa.m index f93341c..ff434d6 100644 --- a/libobs/util/platform-cocoa.m +++ b/libobs/util/platform-cocoa.m @@ -1,6 +1,7 @@ /* - * Copyright (c) 2013-2014 Ruwen Hahn + * Copyright (c) 2013-2018 Ruwen Hahn * Hugh "Jim" Bailey + * Marvin Scholz * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,16 +24,20 @@ #include #include #include +#include #include #include #include #include +#include #include #import +#include "apple/cfstring-utils.h" + /* clock function selection taken from libc++ */ static uint64_t ns_time_simple() { @@ -137,6 +142,35 @@ char *os_get_program_data_path_ptr(const char *name) return os_get_path_ptr_internal(name, NSLocalDomainMask); } +char *os_get_executable_path_ptr(const char *name) +{ + char exe[PATH_MAX]; + char abs_path[PATH_MAX]; + uint32_t size = sizeof(exe); + struct dstr path; + char *slash; + + if (_NSGetExecutablePath(exe, &size) != 0) { + return NULL; + } + + if (!realpath(exe, abs_path)) { + return NULL; + } + + dstr_init_copy(&path, abs_path); + slash = strrchr(path.array, '/'); + if (slash) { + size_t len = slash - path.array + 1; + dstr_resize(&path, len); + } + + if (name && *name) { + dstr_cat(&path, name); + } + return path.array; +} + struct os_cpu_usage_info { int64_t last_cpu_time; int64_t last_sys_time; @@ -417,3 +451,90 @@ uint64_t os_get_proc_virtual_size(void) return 0; return taskinfo.virtual_size; } + +/* Obtains a copy of the contents of a CFString in specified encoding. + * Returns char* (must be bfree'd by caller) or NULL on failure. + */ +char *cfstr_copy_cstr(CFStringRef cfstring, CFStringEncoding cfstring_encoding) +{ + if (!cfstring) + return NULL; + + // Try the quick way to obtain the buffer + const char *tmp_buffer = CFStringGetCStringPtr(cfstring, + cfstring_encoding); + + if (tmp_buffer != NULL) + return bstrdup(tmp_buffer); + + // The quick way did not work, try the more expensive one + CFIndex length = CFStringGetLength(cfstring); + CFIndex max_size = + CFStringGetMaximumSizeForEncoding(length, cfstring_encoding); + + // If result would exceed LONG_MAX, kCFNotFound is returned + if (max_size == kCFNotFound) + return NULL; + + // Account for the null terminator + max_size++; + + char *buffer = bmalloc(max_size); + + if (buffer == NULL) { + return NULL; + } + + // Copy CFString in requested encoding to buffer + Boolean success = + CFStringGetCString(cfstring, buffer, max_size, cfstring_encoding); + + if (!success) { + bfree(buffer); + buffer = NULL; + } + return buffer; +} + +/* Copies the contents of a CFString in specified encoding to a given dstr. + * Returns true on success or false on failure. + * In case of failure, the dstr capacity but not size is changed. + */ +bool cfstr_copy_dstr(CFStringRef cfstring, + CFStringEncoding cfstring_encoding, struct dstr *str) +{ + if (!cfstring) + return false; + + // Try the quick way to obtain the buffer + const char *tmp_buffer = CFStringGetCStringPtr(cfstring, + cfstring_encoding); + + if (tmp_buffer != NULL) { + dstr_copy(str, tmp_buffer); + return true; + } + + // The quick way did not work, try the more expensive one + CFIndex length = CFStringGetLength(cfstring); + CFIndex max_size = + CFStringGetMaximumSizeForEncoding(length, cfstring_encoding); + + // If result would exceed LONG_MAX, kCFNotFound is returned + if (max_size == kCFNotFound) + return NULL; + + // Account for the null terminator + max_size++; + + dstr_ensure_capacity(str, max_size); + + // Copy CFString in requested encoding to dstr buffer + Boolean success = CFStringGetCString( + cfstring, str->array, max_size, cfstring_encoding); + + if (success) + dstr_resize(str, max_size); + + return (bool)success; +} diff --git a/libobs/util/platform-nix.c b/libobs/util/platform-nix.c index b303e30..2fcdd43 100644 --- a/libobs/util/platform-nix.c +++ b/libobs/util/platform-nix.c @@ -33,6 +33,7 @@ #if !defined(__APPLE__) #include #include +#include #ifdef __FreeBSD__ #include #include @@ -66,7 +67,11 @@ void *os_dlopen(const char *path) #endif dstr_cat(&dylib_name, ".so"); +#ifdef __APPLE__ + void *res = dlopen(dylib_name.array, RTLD_LAZY | RTLD_FIRST); +#else void *res = dlopen(dylib_name.array, RTLD_LAZY); +#endif if (!res) blog(LOG_ERROR, "os_dlopen(%s->%s): %s\n", path, dylib_name.array, dlerror()); @@ -265,6 +270,32 @@ char *os_get_program_data_path_ptr(const char *name) return str; } +char *os_get_executable_path_ptr(const char *name) +{ + char exe[PATH_MAX]; + ssize_t count = readlink("/proc/self/exe", exe, PATH_MAX); + const char *path_out = NULL; + struct dstr path; + + if (count == -1) { + return NULL; + } + + path_out = dirname(exe); + if (!path_out) { + return NULL; + } + + dstr_init_copy(&path, path_out); + dstr_cat(&path, "/"); + + if (name && *name) { + dstr_cat(&path, name); + } + + return path.array; +} + #endif bool os_file_exists(const char *path) @@ -848,7 +879,7 @@ static inline bool os_get_proc_memory_usage_internal(statm_t *statm) if (!f) return false; - if (fscanf(f, "%ld %ld %ld %ld %ld %ld %ld", + if (fscanf(f, "%lu %lu %lu %lu %lu %lu %lu", &statm->virtual_size, &statm->resident_size, &statm->share_pages, diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index d0c1464..08c9e5b 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -50,7 +50,7 @@ static inline uint32_t get_winver(void) if (!winver) { struct win_version_info ver; get_win_ver(&ver); - winver = (ver.major << 16) | ver.minor; + winver = (ver.major << 8) | ver.minor; } return winver; @@ -92,6 +92,12 @@ void *os_dlopen(const char *path) if (!h_library) { DWORD error = GetLastError(); + + /* don't print error for libraries that aren't meant to be + * dynamically linked */ + if (error == ERROR_PROC_NOT_FOUND) + return NULL; + char *message = NULL; FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | @@ -289,6 +295,31 @@ char *os_get_program_data_path_ptr(const char *name) return os_get_path_ptr_internal(name, CSIDL_COMMON_APPDATA); } +char *os_get_executable_path_ptr(const char *name) +{ + char *ptr; + char *slash; + wchar_t path_utf16[MAX_PATH]; + struct dstr path; + + GetModuleFileNameW(NULL, path_utf16, MAX_PATH); + + os_wcs_to_utf8_ptr(path_utf16, 0, &ptr); + dstr_init_move_array(&path, ptr); + dstr_replace(&path, "\\", "/"); + slash = strrchr(path.array, '/'); + if (slash) { + size_t len = slash - path.array + 1; + dstr_resize(&path, len); + } + + if (name && *name) { + dstr_cat(&path, name); + } + + return path.array; +} + bool os_file_exists(const char *path) { WIN32_FIND_DATAW wfd; @@ -869,6 +900,11 @@ void get_win_ver(struct win_version_info *info) *info = ver; } +uint32_t get_win_ver_int(void) +{ + return get_winver(); +} + struct os_inhibit_info { bool active; }; diff --git a/libobs/util/platform.c b/libobs/util/platform.c index 6826ea4..a8d036e 100644 --- a/libobs/util/platform.c +++ b/libobs/util/platform.c @@ -262,10 +262,19 @@ bool os_quick_write_utf8_file(const char *path, const char *str, size_t len, if (!f) return false; - if (marker) - fwrite("\xEF\xBB\xBF", 1, 3, f); - if (len) - fwrite(str, 1, len, f); + if (marker) { + if (fwrite("\xEF\xBB\xBF", 3, 1, f) != 1) { + fclose(f); + return false; + } + } + + if (len) { + if (fwrite(str, len, 1, f) != 1) { + fclose(f); + return false; + } + } fflush(f); fclose(f); @@ -292,6 +301,8 @@ bool os_quick_write_utf8_file_safe(const char *path, const char *str, dstr_cat(&temp_path, temp_ext); if (!os_quick_write_utf8_file(temp_path.array, str, len, marker)) { + blog(LOG_ERROR, "os_quick_write_utf8_file_safe: failed to " + "write to %s", temp_path.array); goto cleanup; } diff --git a/libobs/util/platform.h b/libobs/util/platform.h index cd5eea2..ed21b01 100644 --- a/libobs/util/platform.h +++ b/libobs/util/platform.h @@ -111,6 +111,8 @@ EXPORT char *os_get_config_path_ptr(const char *name); EXPORT int os_get_program_data_path(char *dst, size_t size, const char *name); EXPORT char *os_get_program_data_path_ptr(const char *name); +EXPORT char *os_get_executable_path_ptr(const char *name); + EXPORT bool os_file_exists(const char *path); EXPORT size_t os_get_abs_path(const char *path, char *abspath, size_t size); diff --git a/libobs/util/threading-posix.c b/libobs/util/threading-posix.c index 435ff1b..d02e3d3 100644 --- a/libobs/util/threading-posix.c +++ b/libobs/util/threading-posix.c @@ -253,6 +253,12 @@ void os_set_thread_name(const char *name) #elif defined(__FreeBSD__) pthread_set_name_np(pthread_self(), name); #elif defined(__GLIBC__) && !defined(__MINGW32__) - pthread_setname_np(pthread_self(), name); + if (strlen(name) <= 15) { + pthread_setname_np(pthread_self(), name); + } else { + char *thread_name = bstrdup_n(name, 15); + pthread_setname_np(pthread_self(), thread_name); + bfree(thread_name); + } #endif } diff --git a/libobs/util/utf8.c b/libobs/util/utf8.c index d0b5c34..0c96de1 100644 --- a/libobs/util/utf8.c +++ b/libobs/util/utf8.c @@ -41,7 +41,7 @@ size_t utf8_to_wchar(const char *in, size_t insize, wchar_t *out, if (has_utf8_bom(in)) { if (i_insize >= 3) { in += 3; - insize -= 3; + i_insize -= 3; } } diff --git a/libobs/util/util_uint128.h b/libobs/util/util_uint128.h index 511ab0a..ac96f3e 100644 --- a/libobs/util/util_uint128.h +++ b/libobs/util/util_uint128.h @@ -51,11 +51,19 @@ static inline util_uint128_t util_add128(util_uint128_t a, util_uint128_t b) return out; } -static inline util_uint128_t util_lshift64(uint64_t a, int num) +static inline util_uint128_t util_lshift64_internal_32(uint64_t a) { util_uint128_t val; - val.low = a << num; - val.high = a >> (64 - num); + val.low = a << 32; + val.high = a >> 32; + return val; +} + +static inline util_uint128_t util_lshift64_internal_64(uint64_t a) +{ + util_uint128_t val; + val.low = 0; + val.high = a; return val; } @@ -69,13 +77,13 @@ static inline util_uint128_t util_mul64_64(uint64_t a, uint64_t b) out.high = 0; m = (a >> 32) * (b & 0xFFFFFFFFULL); - out = util_add128(out, util_lshift64(m, 32)); + out = util_add128(out, util_lshift64_internal_32(m)); m = (a & 0xFFFFFFFFULL) * (b >> 32); - out = util_add128(out, util_lshift64(m, 32)); + out = util_add128(out, util_lshift64_internal_32(m)); m = (a >> 32) * (b >> 32); - out = util_add128(out, util_lshift64(m, 64)); + out = util_add128(out, util_lshift64_internal_64(m)); return out; } diff --git a/libobs/util/windows/ComPtr.hpp b/libobs/util/windows/ComPtr.hpp index 6b320df..2415240 100644 --- a/libobs/util/windows/ComPtr.hpp +++ b/libobs/util/windows/ComPtr.hpp @@ -68,7 +68,7 @@ public: inline ComPtr &operator=(ComPtr &&c) { - if (this != &c) { + if (&ptr != &c.ptr) { Kill(); ptr = c.ptr; c.ptr = nullptr; diff --git a/libobs/util/windows/WinHandle.hpp b/libobs/util/windows/WinHandle.hpp index 708c07b..4ae3812 100644 --- a/libobs/util/windows/WinHandle.hpp +++ b/libobs/util/windows/WinHandle.hpp @@ -17,7 +17,7 @@ #pragma once class WinHandle { - HANDLE handle; + HANDLE handle = INVALID_HANDLE_VALUE; inline void Clear() { @@ -26,7 +26,7 @@ class WinHandle { } public: - inline WinHandle() : handle(NULL) {} + inline WinHandle() {} inline WinHandle(HANDLE handle_) : handle(handle_) {} inline ~WinHandle() {Clear();} diff --git a/libobs/util/windows/win-version.h b/libobs/util/windows/win-version.h index c855448..106cd80 100644 --- a/libobs/util/windows/win-version.h +++ b/libobs/util/windows/win-version.h @@ -32,6 +32,7 @@ struct win_version_info { EXPORT bool is_64_bit_windows(void); EXPORT bool get_dll_ver(const wchar_t *lib, struct win_version_info *info); EXPORT void get_win_ver(struct win_version_info *info); +EXPORT uint32_t get_win_ver_int(void); #ifdef __cplusplus } diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e6fa3e5..537ed21 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -53,9 +53,13 @@ if(WIN32 OR APPLE) if (BUILD_BROWSER) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt") add_subdirectory(obs-browser) + set(BROWSER_AVAILABLE_INTERNAL ON CACHE BOOL "Interal global cmake variable" FORCE) else() message(STATUS "obs-browser submodule not found! Please fetch submodules. obs-browser plugin disabled.") + set(BROWSER_AVAILABLE_INTERNAL OFF CACHE BOOL "Interal global cmake variable" FORCE) endif() + else() + set(BROWSER_AVAILABLE_INTERNAL OFF CACHE BOOL "Interal global cmake variable" FORCE) endif() if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-vst/CMakeLists.txt") diff --git a/plugins/coreaudio-encoder/data/locale/bg-BG.ini b/plugins/coreaudio-encoder/data/locale/bg-BG.ini index c786d39..6f7c204 100644 --- a/plugins/coreaudio-encoder/data/locale/bg-BG.ini +++ b/plugins/coreaudio-encoder/data/locale/bg-BG.ini @@ -1,3 +1,4 @@ Bitrate="Битрейт" AllowHEAAC="Позволи HE-AAC" +OutputSamplerate="Изходна честота на дискретизация" diff --git a/plugins/coreaudio-encoder/data/locale/da-DK.ini b/plugins/coreaudio-encoder/data/locale/da-DK.ini index 64cdc29..03bcd28 100644 --- a/plugins/coreaudio-encoder/data/locale/da-DK.ini +++ b/plugins/coreaudio-encoder/data/locale/da-DK.ini @@ -1,6 +1,6 @@ -CoreAudioAAC="CoreAudio AAC encoder" -Bitrate="Bitrate" +CoreAudioAAC="CoreAudio AAC-encoder" +Bitrate="Bit-hastighed" AllowHEAAC="Tillad HE-AAC" -OutputSamplerate="Output Sample Rate" -UseInputSampleRate="Brug Input (OBS) Sample Rate (kan vise ikke-understøttede bithastigheder)" +OutputSamplerate="Output Samplingsfrekvens" +UseInputSampleRate="Benyt Input (OBS) Samplingsfrekvens (kan vise uunderstøttede bit-hastigheder)" diff --git a/plugins/coreaudio-encoder/data/locale/de-DE.ini b/plugins/coreaudio-encoder/data/locale/de-DE.ini index 44895bd..0e6245d 100644 --- a/plugins/coreaudio-encoder/data/locale/de-DE.ini +++ b/plugins/coreaudio-encoder/data/locale/de-DE.ini @@ -1,6 +1,6 @@ -CoreAudioAAC="CoreAudio AAC Codierer" +CoreAudioAAC="CoreAudio AAC Kodierer" Bitrate="Bitrate" AllowHEAAC="Erlaube HE-AAC" -OutputSamplerate="Ausgabe-Abtastrate" +OutputSamplerate="Ausgabeabtastrate" UseInputSampleRate="Verwenden Sie Eingabe (OBS) Abtastrate (kann nicht unterstützte Bitraten auflisten)" diff --git a/plugins/coreaudio-encoder/data/locale/eu-ES.ini b/plugins/coreaudio-encoder/data/locale/eu-ES.ini index c1cb04e..1549f03 100644 --- a/plugins/coreaudio-encoder/data/locale/eu-ES.ini +++ b/plugins/coreaudio-encoder/data/locale/eu-ES.ini @@ -1,6 +1,6 @@ CoreAudioAAC="CoreAudio AAC kodetzailea" Bitrate="Bit-tasa" -AllowHEAAC="Ahalbidetu HE-AAC" +AllowHEAAC="Baimendu HE-AAC" OutputSamplerate="Irteera lagin-emaria" UseInputSampleRate="Erabili sarrerako (OBS) lagin-emaria (sostengurik gabeko bit-tasak ager daitezke zerrendan)" diff --git a/plugins/coreaudio-encoder/data/locale/fa-IR.ini b/plugins/coreaudio-encoder/data/locale/fa-IR.ini new file mode 100644 index 0000000..62fb27b --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/fa-IR.ini @@ -0,0 +1,4 @@ +Bitrate="نرخ بیت" +OutputSamplerate="نرخ صدای نمونه" +UseInputSampleRate="استفاده از ورودی (OBS) میزان نمونه (پشتیبانی نشده bitrates لیست ممکن است)" + diff --git a/plugins/coreaudio-encoder/data/locale/fr-FR.ini b/plugins/coreaudio-encoder/data/locale/fr-FR.ini index 1980507..988e4dc 100644 --- a/plugins/coreaudio-encoder/data/locale/fr-FR.ini +++ b/plugins/coreaudio-encoder/data/locale/fr-FR.ini @@ -1,6 +1,6 @@ CoreAudioAAC="Encodeur AAC CoreAudio" Bitrate="Débit" -AllowHEAAC="Autoriser le HE-AAC" +AllowHEAAC="Autoriser HE-AAC" OutputSamplerate="Fréquence d'échantillonnage de sortie" -UseInputSampleRate="Utiliser la fréquence d’échantillonnage d'entrée (OBS) (pourrait lister des débits non-supportés)" +UseInputSampleRate="Utiliser la fréquence d’échantillonnage d'OBS (pourrait lister des débits non-supportés)" diff --git a/plugins/coreaudio-encoder/data/locale/it-IT.ini b/plugins/coreaudio-encoder/data/locale/it-IT.ini index dc4ea1e..c629344 100644 --- a/plugins/coreaudio-encoder/data/locale/it-IT.ini +++ b/plugins/coreaudio-encoder/data/locale/it-IT.ini @@ -1,6 +1,6 @@ -CoreAudioAAC="Encoder CoreAudio AAC" -Bitrate="Bitrate" +CoreAudioAAC="Codifica CoreAudio AAC" +Bitrate="Velocità in bit" AllowHEAAC="Consenti l'HE-AAC" -OutputSamplerate="Output Sample Rate" -UseInputSampleRate="Usa frequenza di campionamento input (OBS) (potrebbe elencare bitrate non supportati)" +OutputSamplerate="Frequenza di campionamento in uscita" +UseInputSampleRate="Usa la frequenza di campionamento in entrata di OBS (potrebbe elencare velocità in bit non supportate)" diff --git a/plugins/coreaudio-encoder/data/locale/mn-MN.ini b/plugins/coreaudio-encoder/data/locale/mn-MN.ini new file mode 100644 index 0000000..d0becb4 --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/mn-MN.ini @@ -0,0 +1,6 @@ +CoreAudioAAC="CoreAudio AAC кодлогч" +Bitrate="Битийн хурд" +AllowHEAAC="HE-AAC-ийг зөвшөөрөх" +OutputSamplerate="Гаралын түүврийн хурд" +UseInputSampleRate="(OBS-ийн) Оролтын түүврийн хурдыг хэрэглэх (дэмжигдээгүй битийн хурдны жагсаалтны хуудсанд байж магадгүй)" + diff --git a/plugins/coreaudio-encoder/data/locale/pt-PT.ini b/plugins/coreaudio-encoder/data/locale/pt-PT.ini index e2b503c..1debe60 100644 --- a/plugins/coreaudio-encoder/data/locale/pt-PT.ini +++ b/plugins/coreaudio-encoder/data/locale/pt-PT.ini @@ -2,5 +2,5 @@ CoreAudioAAC="Codificador CoreAudio AAC" Bitrate="Bitrate" AllowHEAAC="Permitir HE-AAC" OutputSamplerate="Taxa de amostragem de saída" -UseInputSampleRate="Taxa de amostragem de entrada (OBS) Taxa Amostra (pode listar taxas de bit sem suporte)" +UseInputSampleRate="Taxa de amostragem de entrada (OBS) Taxa Amostra (pode listar taxas de bit sem suporte)" diff --git a/plugins/coreaudio-encoder/data/locale/ro-RO.ini b/plugins/coreaudio-encoder/data/locale/ro-RO.ini index c3f3246..a2df720 100644 --- a/plugins/coreaudio-encoder/data/locale/ro-RO.ini +++ b/plugins/coreaudio-encoder/data/locale/ro-RO.ini @@ -1,4 +1,4 @@ -CoreAudioAAC="Cofificator AAC CoreAudio" +CoreAudioAAC="Codificator AAC CoreAudio" Bitrate="Rată de biți" AllowHEAAC="Permite HE-AAC" OutputSamplerate="Rată de eșantionare a ieșirii" diff --git a/plugins/coreaudio-encoder/data/locale/sr-CS.ini b/plugins/coreaudio-encoder/data/locale/sr-CS.ini index cf7f107..8faa28d 100644 --- a/plugins/coreaudio-encoder/data/locale/sr-CS.ini +++ b/plugins/coreaudio-encoder/data/locale/sr-CS.ini @@ -1,6 +1,6 @@ -CoreAudioAAC="CoreAudio AAC nekoder" +CoreAudioAAC="CoreAudio AAC enkoder" Bitrate="Protok" AllowHEAAC="Dozvoli HE-AAC" -OutputSamplerate="Izlazni sample-rate" -UseInputSampleRate="Koristi ulazni (OBS) sample-rate (mogu biti prikazani nepodržani bitrate-ovi)" +OutputSamplerate="Brzina semplovanja na izlazu" +UseInputSampleRate="Koristi brzinu semplovanja sa ulaza (OBS) (mogu biti zabeleženi protoci koji nisu podržani)" diff --git a/plugins/coreaudio-encoder/data/locale/sr-SP.ini b/plugins/coreaudio-encoder/data/locale/sr-SP.ini index 4617ee6..70a7734 100644 --- a/plugins/coreaudio-encoder/data/locale/sr-SP.ini +++ b/plugins/coreaudio-encoder/data/locale/sr-SP.ini @@ -1,6 +1,6 @@ CoreAudioAAC="CoreAudio AAC енкодер" Bitrate="Проток" AllowHEAAC="Дозволи HE-AAC" -OutputSamplerate="Излазни sample-rate" -UseInputSampleRate="Користи улазни sample-rate (OBS) (могу бити приказани неподржани bitrate-ови)" +OutputSamplerate="Брзина семпловања на излазу" +UseInputSampleRate="Користи брзину семпловања са улаза (OBS) (могу бити забележени протоци који нису подржани)" diff --git a/plugins/coreaudio-encoder/data/locale/ur-PK.ini b/plugins/coreaudio-encoder/data/locale/ur-PK.ini new file mode 100644 index 0000000..dd43963 --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/ur-PK.ini @@ -0,0 +1,2 @@ +Bitrate="بٹ ریٹ" + diff --git a/plugins/coreaudio-encoder/encoder.cpp b/plugins/coreaudio-encoder/encoder.cpp index 972376d..0affe6e 100644 --- a/plugins/coreaudio-encoder/encoder.cpp +++ b/plugins/coreaudio-encoder/encoder.cpp @@ -10,6 +10,7 @@ #ifndef _WIN32 #include +#include #endif #define CA_LOG(level, format, ...) \ @@ -257,17 +258,8 @@ static DStr osstatus_to_dstr(OSStatus code) kCFErrorDomainOSStatus, code, NULL)}; cf_ptr str{CFErrorCopyDescription(err.get())}; - CFIndex length = CFStringGetLength(str.get()); - CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, - kCFStringEncodingUTF8); - - dstr_ensure_capacity(result, max_size); - - if (result->array && CFStringGetCString(str.get(), result->array, - max_size, kCFStringEncodingUTF8)) { - dstr_resize(result, strlen(result->array)); + if (cfstr_copy_dstr(str.get(), kCFStringEncodingUTF8, result)) return result; - } #endif const char *code_str = code_to_str(code); @@ -1374,6 +1366,10 @@ static obs_properties_t *aac_properties(void *data) OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("coreaudio-encoder", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Apple CoreAudio based encoder"; +} bool obs_module_load(void) { diff --git a/plugins/decklink/DecklinkBase.cpp b/plugins/decklink/DecklinkBase.cpp new file mode 100644 index 0000000..e916a24 --- /dev/null +++ b/plugins/decklink/DecklinkBase.cpp @@ -0,0 +1,20 @@ +#include "DecklinkBase.h" + +DecklinkBase::DecklinkBase(DeckLinkDeviceDiscovery *discovery_) + : discovery(discovery_) +{ +} + +DeckLinkDevice *DecklinkBase::GetDevice() const +{ + return instance ? instance->GetDevice() : nullptr; +} + +bool DecklinkBase::Activate(DeckLinkDevice*, long long) +{ + return false; +} + +void DecklinkBase::Deactivate() +{ +} diff --git a/plugins/decklink/DecklinkBase.h b/plugins/decklink/DecklinkBase.h new file mode 100644 index 0000000..a337092 --- /dev/null +++ b/plugins/decklink/DecklinkBase.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include + +#include "platform.hpp" + +#include "decklink-device-discovery.hpp" +#include "decklink-device-instance.hpp" +#include "decklink-device-mode.hpp" + +class DecklinkBase { + +protected: + DecklinkBase(DeckLinkDeviceDiscovery *discovery_); + ComPtr instance; + DeckLinkDeviceDiscovery *discovery; + std::recursive_mutex deviceMutex; + volatile long activateRefs = 0; + BMDPixelFormat pixelFormat = bmdFormat8BitYUV; + video_colorspace colorSpace = VIDEO_CS_DEFAULT; + video_range_type colorRange = VIDEO_RANGE_DEFAULT; + speaker_layout channelFormat = SPEAKERS_STEREO; + +public: + virtual bool Activate(DeckLinkDevice *device, long long modeId); + virtual void Deactivate(); + + DeckLinkDevice *GetDevice() const; +}; diff --git a/plugins/decklink/decklink.cpp b/plugins/decklink/DecklinkInput.cpp similarity index 57% rename from plugins/decklink/decklink.cpp rename to plugins/decklink/DecklinkInput.cpp index ea0ec96..0f74aa6 100644 --- a/plugins/decklink/decklink.cpp +++ b/plugins/decklink/DecklinkInput.cpp @@ -1,30 +1,23 @@ -#include "decklink.hpp" -#include "decklink-device-discovery.hpp" -#include "decklink-device-instance.hpp" -#include "decklink-device-mode.hpp" +#include "DecklinkInput.hpp" #include -DeckLink::DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_) : - discovery(discovery_), source(source) +DeckLinkInput::DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_) + : DecklinkBase(discovery_), + source(source) { - discovery->AddCallback(DeckLink::DevicesChanged, this); + discovery->AddCallback(DeckLinkInput::DevicesChanged, this); } -DeckLink::~DeckLink(void) +DeckLinkInput::~DeckLinkInput(void) { - discovery->RemoveCallback(DeckLink::DevicesChanged, this); + discovery->RemoveCallback(DeckLinkInput::DevicesChanged, this); Deactivate(); } -DeckLinkDevice *DeckLink::GetDevice() const +void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device, bool added) { - return instance ? instance->GetDevice() : nullptr; -} - -void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added) -{ - DeckLink *decklink = reinterpret_cast(param); + DeckLinkInput *decklink = reinterpret_cast(param); std::lock_guard lock(decklink->deviceMutex); obs_source_update_properties(decklink->source); @@ -33,16 +26,20 @@ void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added) const char *hash; long long mode; obs_data_t *settings; + BMDVideoConnection videoConnection; + BMDAudioConnection audioConnection; settings = obs_source_get_settings(decklink->source); hash = obs_data_get_string(settings, "device_hash"); + videoConnection = (BMDVideoConnection) obs_data_get_int(settings, "video_connection"); + audioConnection = (BMDAudioConnection) obs_data_get_int(settings, "audio_connection"); mode = obs_data_get_int(settings, "mode_id"); obs_data_release(settings); if (device->GetHash().compare(hash) == 0) { if (!decklink->activateRefs) return; - if (decklink->Activate(device, mode)) + if (decklink->Activate(device, mode, videoConnection, audioConnection)) os_atomic_dec_long(&decklink->activateRefs); } @@ -54,7 +51,9 @@ void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added) } } -bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) +bool DeckLinkInput::Activate(DeckLinkDevice *device, long long modeId, + BMDVideoConnection bmdVideoConnection, + BMDAudioConnection bmdAudioConnection) { std::lock_guard lock(deviceMutex); DeckLinkDevice *curDevice = GetDevice(); @@ -65,49 +64,66 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) if (!isActive) return false; if (instance->GetActiveModeId() == modeId && + instance->GetVideoConnection() == bmdVideoConnection && + instance->GetAudioConnection() == bmdAudioConnection && instance->GetActivePixelFormat() == pixelFormat && instance->GetActiveColorSpace() == colorSpace && instance->GetActiveColorRange() == colorRange && - instance->GetActiveChannelFormat() == channelFormat) + instance->GetActiveChannelFormat() == channelFormat && + instance->GetActiveSwapState() == swap) return false; } if (isActive) instance->StopCapture(); + isCapturing = false; if (!same) instance.Set(new DeckLinkDeviceInstance(this, device)); if (instance == nullptr) return false; - DeckLinkDeviceMode *mode = GetDevice()->FindMode(modeId); + if (GetDevice() == nullptr) { + LOG(LOG_ERROR, "Tried to activate an input with nullptr device."); + return false; + } + + DeckLinkDeviceMode *mode = GetDevice()->FindInputMode(modeId); if (mode == nullptr) { instance = nullptr; return false; } - if (!instance->StartCapture(mode)) { + if (!instance->StartCapture(mode, bmdVideoConnection, bmdAudioConnection)) { instance = nullptr; return false; } os_atomic_inc_long(&activateRefs); SaveSettings(); + id = modeId; + isCapturing = true; return true; } -void DeckLink::Deactivate(void) +void DeckLinkInput::Deactivate(void) { std::lock_guard lock(deviceMutex); if (instance) instance->StopCapture(); + isCapturing = false; instance = nullptr; os_atomic_dec_long(&activateRefs); } -void DeckLink::SaveSettings() +bool DeckLinkInput::Capturing(void) +{ + return isCapturing; +} + +void DeckLinkInput::SaveSettings() { if (!instance) return; @@ -127,7 +143,7 @@ void DeckLink::SaveSettings() obs_data_release(settings); } -obs_source_t *DeckLink::GetSource(void) const +obs_source_t *DeckLinkInput::GetSource(void) const { return source; } diff --git a/plugins/decklink/decklink.hpp b/plugins/decklink/DecklinkInput.hpp similarity index 51% rename from plugins/decklink/decklink.hpp rename to plugins/decklink/DecklinkInput.hpp index 23f8b59..c90dd7e 100644 --- a/plugins/decklink/decklink.hpp +++ b/plugins/decklink/DecklinkInput.hpp @@ -1,40 +1,19 @@ #pragma once -#include "platform.hpp" +#include "DecklinkBase.h" -#include - -#include -#include -#include - -class DeckLinkDeviceDiscovery; -class DeckLinkDeviceInstance; -class DeckLinkDevice; -class DeckLinkDeviceMode; - -class DeckLink { +class DeckLinkInput : public DecklinkBase { protected: - ComPtr instance; - DeckLinkDeviceDiscovery *discovery; bool isCapturing = false; obs_source_t *source; - volatile long activateRefs = 0; - std::recursive_mutex deviceMutex; - BMDPixelFormat pixelFormat = bmdFormat8BitYUV; - video_colorspace colorSpace = VIDEO_CS_DEFAULT; - video_range_type colorRange = VIDEO_RANGE_DEFAULT; - speaker_layout channelFormat = SPEAKERS_STEREO; void SaveSettings(); static void DevicesChanged(void *param, DeckLinkDevice *device, bool added); public: - DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery); - virtual ~DeckLink(void); - - DeckLinkDevice *GetDevice() const; + DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery); + virtual ~DeckLinkInput(void); long long GetActiveModeId(void) const; obs_source_t *GetSource(void) const; @@ -60,8 +39,17 @@ public: channelFormat = format; } - bool Activate(DeckLinkDevice *device, long long modeId); + bool Activate(DeckLinkDevice *device, long long modeId, + BMDVideoConnection bmdVideoConnection, + BMDAudioConnection bmdAudioConnection); void Deactivate(); + bool Capturing(); bool buffering = false; + bool dwns = false; + std::string hash; + long long id; + bool swap = false; + BMDVideoConnection videoConnection; + BMDAudioConnection audioConnection; }; diff --git a/plugins/decklink/DecklinkOutput.cpp b/plugins/decklink/DecklinkOutput.cpp new file mode 100644 index 0000000..40eaf33 --- /dev/null +++ b/plugins/decklink/DecklinkOutput.cpp @@ -0,0 +1,110 @@ +#include "DecklinkOutput.hpp" + +#include + +DeckLinkOutput::DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery_) + : DecklinkBase(discovery_), + output(output) +{ + discovery->AddCallback(DeckLinkOutput::DevicesChanged, this); +} + +DeckLinkOutput::~DeckLinkOutput(void) +{ + discovery->RemoveCallback(DeckLinkOutput::DevicesChanged, this); + Deactivate(); +} + +void DeckLinkOutput::DevicesChanged(void *param, DeckLinkDevice *device, bool) +{ + auto *decklink = reinterpret_cast(param); + std::lock_guard lock(decklink->deviceMutex); + + blog(LOG_DEBUG, "%s", device->GetHash().c_str()); +} + +bool DeckLinkOutput::Activate(DeckLinkDevice *device, long long modeId) +{ + std::lock_guard lock(deviceMutex); + DeckLinkDevice *curDevice = GetDevice(); + const bool same = device == curDevice; + const bool isActive = instance != nullptr; + + if (same) { + if (!isActive) + return false; + + if (instance->GetActiveModeId() == modeId && + instance->GetActivePixelFormat() == pixelFormat && + instance->GetActiveColorSpace() == colorSpace && + instance->GetActiveColorRange() == colorRange && + instance->GetActiveChannelFormat() == channelFormat) + return false; + } + + if (isActive) + instance->StopOutput(); + + if (!same) + instance.Set(new DeckLinkDeviceInstance(this, device)); + + if (instance == nullptr) + return false; + + DeckLinkDeviceMode *mode = GetDevice()->FindOutputMode(modeId); + if (mode == nullptr) { + instance = nullptr; + return false; + } + + + if (!instance->StartOutput(mode)) { + instance = nullptr; + return false; + } + + os_atomic_inc_long(&activateRefs); + return true; +} + +void DeckLinkOutput::Deactivate(void) +{ + std::lock_guard lock(deviceMutex); + if (instance) + instance->StopOutput(); + + instance = nullptr; + + os_atomic_dec_long(&activateRefs); +} + +obs_output_t *DeckLinkOutput::GetOutput(void) const +{ + return output; +} + +void DeckLinkOutput::DisplayVideoFrame(video_data *frame) +{ + instance->DisplayVideoFrame(frame); +} + +void DeckLinkOutput::WriteAudio(audio_data *frames) +{ + instance->WriteAudio(frames); +} + +void DeckLinkOutput::SetSize(int width, int height) +{ + this->width = width; + this->height = height; +} + +int DeckLinkOutput::GetWidth() +{ + return width; +} + +int DeckLinkOutput::GetHeight() +{ + return height; +} diff --git a/plugins/decklink/DecklinkOutput.hpp b/plugins/decklink/DecklinkOutput.hpp new file mode 100644 index 0000000..0a4fe85 --- /dev/null +++ b/plugins/decklink/DecklinkOutput.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "DecklinkBase.h" + +#include "../../libobs/media-io/video-scaler.h" + +class DeckLinkOutput : public DecklinkBase { +protected: + obs_output_t *output; + int width; + int height; + + static void DevicesChanged(void *param, DeckLinkDevice *device, bool added); + +public: + const char *deviceHash; + long long modeID; + uint64_t start_timestamp; + uint32_t audio_samplerate; + size_t audio_planes; + size_t audio_size; + int keyerMode; + + DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery); + virtual ~DeckLinkOutput(void); + obs_output_t *GetOutput(void) const; + bool Activate(DeckLinkDevice *device, long long modeId) override; + void Deactivate() override; + void DisplayVideoFrame(video_data *pData); + void WriteAudio(audio_data *frames); + void SetSize(int width, int height); + int GetWidth(); + int GetHeight(); +}; diff --git a/plugins/decklink/audio-repack.c b/plugins/decklink/audio-repack.c index 02d0879..ec92f76 100644 --- a/plugins/decklink/audio-repack.c +++ b/plugins/decklink/audio-repack.c @@ -21,38 +21,16 @@ int check_buffer(struct audio_repack *repack, } /* - * Swap channel 3 & 4, 5 & 7, 6 & 8 and squash arrays + * Squash arrays. + * For instance: * 2.1: * * | FL | FR | LFE | emp | emp | emp |emp |emp | * | | | * | FL | FR | LFE | - * 4.0 (quad): - * - * | FL | FR | BR | BL | emp | emp |emp |emp | - * | | x | - * | FL | FR | BL | BC | - * - * 4.1: - * - * | FL | FR |LFE | FC | BC | emp |emp |emp | - * | | x | | - * | FL | FR | FC |LFE | BC | - * - * 5.1: - * - * | FL | FR |LFE | FC |(emp|emp)|(BL|BR)| - * | | x x - * | FL | FR | FC |LFE | BL | BR | - * - * 7.1: - * - * | FL | FR |LFE | FC |( SL | SR )|(BL |BR )| - * | | x X - * | FL | FR | FC |LFE |( BL | BR )|(SL |SR )| - */ +*/ -int repack_squash_swap(struct audio_repack *repack, +int repack_squash(struct audio_repack *repack, const uint8_t *bsrc, uint32_t frame_count) { if (check_buffer(repack, frame_count) < 0) @@ -62,28 +40,39 @@ int repack_squash_swap(struct audio_repack *repack, const __m128i *src = (__m128i *)bsrc; const __m128i *esrc = src + frame_count; uint16_t *dst = (uint16_t *)repack->packet_buffer; - /* 2.1 audio does not require re-ordering but still needs squashing - * in order to avoid sampling issues. + + /* Audio needs squashing in order to avoid resampling issues. + * The condition checks for 7.1 audio for which no squash is needed. */ - if (squash == 5) { + if (squash > 0) { while (src != esrc) { __m128i target = _mm_load_si128(src++); _mm_storeu_si128((__m128i *)dst, target); dst += 8 - squash; } - } else { - while (src != esrc) { - __m128i target = _mm_load_si128(src++); - __m128i buf = _mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0)); - __m128i buf2 = _mm_shufflehi_epi16(buf, _MM_SHUFFLE(1, 0, 3, 2)); - _mm_storeu_si128((__m128i *)dst, buf2); - dst += 8 - squash; - } } return 0; } +int repack_squash_swap(struct audio_repack *repack, + const uint8_t *bsrc, uint32_t frame_count) +{ + if (check_buffer(repack, frame_count) < 0) + return -1; + int squash = repack->extra_dst_size; + const __m128i *src = (__m128i *)bsrc; + const __m128i *esrc = src + frame_count; + uint16_t *dst = (uint16_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 += 8 - squash; + } + return 0; +} + int audio_repack_init(struct audio_repack *repack, audio_repack_mode_t repack_mode, uint8_t sample_bit) { @@ -91,44 +80,15 @@ int audio_repack_init(struct audio_repack *repack, if (sample_bit != 16) return -1; - - switch (repack_mode) { - case repack_mode_8to3ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 3 * (16 / 8); - repack->extra_dst_size = 5; + int _audio_repack_ch[8] = { 3, 4, 5, 6, 5, 6, 8, 8 }; + repack->base_src_size = 8 * (16 / 8); + repack->base_dst_size = _audio_repack_ch[repack_mode] * (16 / 8); + repack->extra_dst_size = 8 - _audio_repack_ch[repack_mode]; + repack->repack_func = &repack_squash; + if (repack_mode == repack_mode_8to5ch_swap || + repack_mode == repack_mode_8to6ch_swap || + repack_mode == repack_mode_8ch_swap) repack->repack_func = &repack_squash_swap; - break; - case repack_mode_8to4ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 4 * (16 / 8); - repack->extra_dst_size = 4; - repack->repack_func = &repack_squash_swap; - break; - - case repack_mode_8to5ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 5 * (16 / 8); - repack->extra_dst_size = 3; - repack->repack_func = &repack_squash_swap; - break; - - case repack_mode_8to6ch_swap23: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 6 * (16 / 8); - repack->extra_dst_size = 2; - repack->repack_func = &repack_squash_swap; - break; - - case repack_mode_8ch_swap23_swap46_swap57: - repack->base_src_size = 8 * (16 / 8); - repack->base_dst_size = 8 * (16 / 8); - repack->extra_dst_size = 0; - repack->repack_func = &repack_squash_swap; - break; - - default: return -1; - } return 0; } diff --git a/plugins/decklink/audio-repack.h b/plugins/decklink/audio-repack.h index e063e13..448cd99 100644 --- a/plugins/decklink/audio-repack.h +++ b/plugins/decklink/audio-repack.h @@ -26,11 +26,14 @@ struct audio_repack { }; enum _audio_repack_mode { - repack_mode_8to3ch_swap23, - repack_mode_8to4ch_swap23, - repack_mode_8to5ch_swap23, - repack_mode_8to6ch_swap23, - repack_mode_8ch_swap23_swap46_swap57, + repack_mode_8to3ch=0, + repack_mode_8to4ch, + repack_mode_8to5ch, + repack_mode_8to6ch, + repack_mode_8to5ch_swap, + repack_mode_8to6ch_swap, + repack_mode_8ch_swap, + repack_mode_8ch, }; typedef enum _audio_repack_mode audio_repack_mode_t; diff --git a/plugins/decklink/const.h b/plugins/decklink/const.h new file mode 100644 index 0000000..4f67a77 --- /dev/null +++ b/plugins/decklink/const.h @@ -0,0 +1,41 @@ +#define DEVICE_HASH "device_hash" +#define DEVICE_NAME "device_name" +#define VIDEO_CONNECTION "video_connection" +#define AUDIO_CONNECTION "audio_connection" +#define MODE_ID "mode_id" +#define MODE_NAME "mode_name" +#define CHANNEL_FORMAT "channel_format" +#define PIXEL_FORMAT "pixel_format" +#define COLOR_SPACE "color_space" +#define COLOR_RANGE "color_range" +#define BUFFERING "buffering" +#define DEACTIVATE_WNS "deactivate_when_not_showing" +#define AUTO_START "auto_start" +#define KEYER "keyer" +#define SWAP "swap" + +#define TEXT_DEVICE obs_module_text("Device") +#define TEXT_VIDEO_CONNECTION obs_module_text("VideoConnection") +#define TEXT_AUDIO_CONNECTION obs_module_text("AudioConnection") +#define TEXT_MODE obs_module_text("Mode") +#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat") +#define TEXT_COLOR_SPACE obs_module_text("ColorSpace") +#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default") +#define TEXT_COLOR_RANGE obs_module_text("ColorRange") +#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default") +#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial") +#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full") +#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat") +#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None") +#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch") +#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch") +#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch") +#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch") +#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch") +#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch") +#define TEXT_BUFFERING obs_module_text("Buffering") +#define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing") +#define TEXT_AUTO_START obs_module_text("AutoStart") +#define TEXT_ENABLE_KEYER obs_module_text("Keyer") +#define TEXT_SWAP obs_module_text("SwapFC-LFE") +#define TEXT_SWAP_TOOLTIP obs_module_text("SwapFC-LFE.Tooltip") diff --git a/plugins/decklink/data/locale/ar-SA.ini b/plugins/decklink/data/locale/ar-SA.ini index fdaf139..f26cc16 100644 --- a/plugins/decklink/data/locale/ar-SA.ini +++ b/plugins/decklink/data/locale/ar-SA.ini @@ -3,4 +3,18 @@ Device="الجهاز" Mode="الوضع" Buffering="استخدام التخزين المؤقت" PixelFormat="صيغة البكسل" +ColorSpace.Default="الافتراضي" +ColorRange.Default="الافتراضي" +ColorRange.Partial="جزئي" +ColorRange.Full="كامل" +ChannelFormat="قناة" +ChannelFormat.None="بلا" +ChannelFormat.2_0ch="2ch" +ChannelFormat.2_1ch="2.1ch" +ChannelFormat.4_0ch="4ch" +ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="التعطيل عندما لا يكون ظاهراً" +AutoStart="البدء تلقائياً مع التشغيل" diff --git a/plugins/decklink/data/locale/bn-BD.ini b/plugins/decklink/data/locale/bn-BD.ini index f13caa4..657e1ea 100644 --- a/plugins/decklink/data/locale/bn-BD.ini +++ b/plugins/decklink/data/locale/bn-BD.ini @@ -1,4 +1,7 @@ BlackmagicDevice="Blackmagic যন্ত্র" Device="ডিভাইস" PixelFormat="পিক্সেল বিন্যাস" +ColorRange.Partial="আংশিক" +ColorRange.Full="পূর্ণ" +ChannelFormat="চ্যানেল" diff --git a/plugins/decklink/data/locale/ca-ES.ini b/plugins/decklink/data/locale/ca-ES.ini index c6df023..bacbb37 100644 --- a/plugins/decklink/data/locale/ca-ES.ini +++ b/plugins/decklink/data/locale/ca-ES.ini @@ -3,9 +3,9 @@ Device="Dispositiu" Mode="Mode" Buffering="Usa memòria intermèdia" PixelFormat="Format de píxel" -ColorSpace="Espai de color YUV" +ColorSpace="Espai de color" ColorSpace.Default="Per defecte" -ColorRange="Gamma de color YUV" +ColorRange="Gamma de colors" ColorRange.Default="Per defecte" ColorRange.Partial="Parcial" ColorRange.Full="Complet" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4" ChannelFormat.4_1ch="4.1" ChannelFormat.5_1ch="Canals 5.1" ChannelFormat.7_1ch="Canals 7.1" +DeactivateWhenNotShowing="Desactiva quan no es mostra" +AutoStart="Executa automàticament a l'inici" +SwapFC-LFE="Commuta FC i LFE" +SwapFC-LFE.Tooltip="Commuta el centre del canal frontal i el subwoofer" +VideoConnection="Connexió de vídeo" +AudioConnection="Connexió d'àudio" diff --git a/plugins/decklink/data/locale/cs-CZ.ini b/plugins/decklink/data/locale/cs-CZ.ini index 081819c..4d1661b 100644 --- a/plugins/decklink/data/locale/cs-CZ.ini +++ b/plugins/decklink/data/locale/cs-CZ.ini @@ -3,9 +3,9 @@ Device="Zařízení" Mode="Mód" Buffering="Použít vyrovnávací paměť" PixelFormat="Formát pixelů" -ColorSpace="Prostor barev YUV" +ColorSpace="Barevný prostor" ColorSpace.Default="Výchozí" -ColorRange="Rozsah barev YUV" +ColorRange="Rozsah barev" ColorRange.Default="Výchozí" ColorRange.Partial="Částečný" ColorRange.Full="Plný" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Deaktivovat při skrytém" +AutoStart="Spustit při startu" +SwapFC-LFE="Prohodit FC a LFE" +SwapFC-LFE.Tooltip="Prohodí přední prostřední kanál a LFE kanál (Subwoofer)" +VideoConnection="Obrazové spojení" +AudioConnection="Zvukové spojení" diff --git a/plugins/decklink/data/locale/da-DK.ini b/plugins/decklink/data/locale/da-DK.ini index 8ccdb60..5a8a0ae 100644 --- a/plugins/decklink/data/locale/da-DK.ini +++ b/plugins/decklink/data/locale/da-DK.ini @@ -1,20 +1,26 @@ BlackmagicDevice="Blackmagic-enhed" Device="Enhed" Mode="Tilstand" -Buffering="Brug buffering" +Buffering="Benyt buffering" PixelFormat="Pixelformat" -ColorSpace="YUV farverum" +ColorSpace="Farverum" ColorSpace.Default="Standard" -ColorRange="YUV-farveområde" +ColorRange="Farveområde" ColorRange.Default="Standard" ColorRange.Partial="Delvis" ColorRange.Full="Fuld" ChannelFormat="Kanal" ChannelFormat.None="Intet" -ChannelFormat.2_0ch="2kan" -ChannelFormat.2_1ch="2.1 kasnals" +ChannelFormat.2_0ch="2 kanals" +ChannelFormat.2_1ch="2.1 kanals" ChannelFormat.4_0ch="4 kanals" ChannelFormat.4_1ch="4.1 kanals" ChannelFormat.5_1ch="5.1 kanals" ChannelFormat.7_1ch="7.1 kanals" +DeactivateWhenNotShowing="Deaktivér, hvis ikke synlig" +AutoStart="Autostart ved opstart" +SwapFC-LFE="Ombyt FC og LFE" +SwapFC-LFE.Tooltip="Byt Front Center-kanal og LFE-kanal" +VideoConnection="Videoforbindelse" +AudioConnection="Lydforbindelse" diff --git a/plugins/decklink/data/locale/de-DE.ini b/plugins/decklink/data/locale/de-DE.ini index 5694853..3bdbd6e 100644 --- a/plugins/decklink/data/locale/de-DE.ini +++ b/plugins/decklink/data/locale/de-DE.ini @@ -1,11 +1,11 @@ -BlackmagicDevice="Blackmagic Gerät" +BlackmagicDevice="Blackmagic-Gerät" Device="Gerät" Mode="Modus" -Buffering="Buffering benutzen" +Buffering="Puffern benutzen" PixelFormat="Pixelformat" -ColorSpace="YUV-Farbmatrix" +ColorSpace="Farbraum" ColorSpace.Default="Standard" -ColorRange="YUV-Farbbereich" +ColorRange="Farbbereich" ColorRange.Default="Standard" ColorRange.Partial="Begrenzt" ColorRange.Full="Voll" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4 Kanal" ChannelFormat.4_1ch="4.1 Kanal" ChannelFormat.5_1ch="5.1 Kanal" ChannelFormat.7_1ch="7.1 Kanal" +DeactivateWhenNotShowing="Deaktivieren, wenn die Quelle nicht angezeigt wird" +AutoStart="Automatisch beim Öffnen starten" +SwapFC-LFE="FC und LFE tauschen" +SwapFC-LFE.Tooltip="Vorderen Front-Center-Kanal und LFE-Kanal tauschen" +VideoConnection="Videoverbindung" +AudioConnection="Audioverbindung" diff --git a/plugins/decklink/data/locale/el-GR.ini b/plugins/decklink/data/locale/el-GR.ini index 26fa84c..b4f167a 100644 --- a/plugins/decklink/data/locale/el-GR.ini +++ b/plugins/decklink/data/locale/el-GR.ini @@ -3,9 +3,7 @@ Device="Συσκευή" Mode="Λειτουργία" Buffering="Χρήση ενδιάμεσης μνήμης" PixelFormat="Μορφή pixel" -ColorSpace="Χώρος Χρωμάτων YUV" ColorSpace.Default="Προεπιλογή" -ColorRange="Έκταση Χρωμάτων YUV" ColorRange.Default="Προεπιλογή" ColorRange.Partial="Μερική" ColorRange.Full="Πλήρης" @@ -17,4 +15,6 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Απενεργοποίηση όταν δεν εμφανίζεται" +AutoStart="Αυτόματη εκκίνηση" diff --git a/plugins/decklink/data/locale/en-US.ini b/plugins/decklink/data/locale/en-US.ini index a694a06..a73f79d 100644 --- a/plugins/decklink/data/locale/en-US.ini +++ b/plugins/decklink/data/locale/en-US.ini @@ -3,9 +3,9 @@ Device="Device" Mode="Mode" Buffering="Use Buffering" PixelFormat="Pixel Format" -ColorSpace="YUV Color Space" +ColorSpace="Color Space" ColorSpace.Default="Default" -ColorRange="YUV Color Range" +ColorRange="Color Range" ColorRange.Default="Default" ColorRange.Partial="Partial" ColorRange.Full="Full" @@ -17,3 +17,9 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Deactivate when not showing" +AutoStart="Auto start on launch" +SwapFC-LFE="Swap FC and LFE" +SwapFC-LFE.Tooltip="Swap Front Center Channel and LFE Channel" +VideoConnection="Video Connection" +AudioConnection="Audio Connection" diff --git a/plugins/decklink/data/locale/es-ES.ini b/plugins/decklink/data/locale/es-ES.ini index a4d8415..f1b7abf 100644 --- a/plugins/decklink/data/locale/es-ES.ini +++ b/plugins/decklink/data/locale/es-ES.ini @@ -3,9 +3,9 @@ Device="Dispositivo" Mode="Modo" Buffering="Utilizar el almacenamiento en búfer" PixelFormat="Formato de píxel" -ColorSpace="Espacio de color YUV" +ColorSpace="Espacio de color" ColorSpace.Default="Predeterminado" -ColorRange="Rango de color YUV" +ColorRange="Rango de color" ColorRange.Default="Predeterminado" ColorRange.Partial="Parcial" ColorRange.Full="Completo" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4 canales" ChannelFormat.4_1ch="4.1 canales" ChannelFormat.5_1ch="5.1 canales" ChannelFormat.7_1ch="7.1 canales" +DeactivateWhenNotShowing="Desactivar cuando no se muestre" +AutoStart="Comenzar al iniciar" +SwapFC-LFE="Intercambiar FC y LFE" +SwapFC-LFE.Tooltip="Intercambiar Canal Central Frontal y Canal LFE" +VideoConnection="Conexión de vídeo" +AudioConnection="Conexión de audio" diff --git a/plugins/decklink/data/locale/eu-ES.ini b/plugins/decklink/data/locale/eu-ES.ini index eb5aee2..a64ec00 100644 --- a/plugins/decklink/data/locale/eu-ES.ini +++ b/plugins/decklink/data/locale/eu-ES.ini @@ -3,18 +3,24 @@ Device="Gailua" Mode="Modua" Buffering="Erabili Bufferreratzea" PixelFormat="Pixel formatua" -ColorSpace="YUV kolore espazioa" +ColorSpace="Kolore-espazioa" ColorSpace.Default="Lehenetsia" -ColorRange="YUV kolore tartea" +ColorRange="Kolore tartea" ColorRange.Default="Lehenetsia" ColorRange.Partial="Partziala" ColorRange.Full="Osoa" ChannelFormat="Kanala" ChannelFormat.None="Ezer ez" -ChannelFormat.2_0ch="2k" +ChannelFormat.2_0ch="2 kanala" ChannelFormat.2_1ch="2.1 kanala" -ChannelFormat.4_0ch="4kanala" -ChannelFormat.4_1ch="4.1kanala" +ChannelFormat.4_0ch="4 kanala" +ChannelFormat.4_1ch="4.1 kanala" ChannelFormat.5_1ch="5.1 kanala" ChannelFormat.7_1ch="7.1 kanala" +DeactivateWhenNotShowing="Desaktibatu ikusten ez denean" +AutoStart="Hasi automatikoki abiatzean" +SwapFC-LFE="Aldatu FC <-> LFE" +SwapFC-LFE.Tooltip="Aldatu Aurreko Kanal Zentrala eta LFE kanala (subwooferra)" +VideoConnection="Bideo-konexioa" +AudioConnection="Audio-konexioa" diff --git a/plugins/decklink/data/locale/fa-IR.ini b/plugins/decklink/data/locale/fa-IR.ini new file mode 100644 index 0000000..89b7ab6 --- /dev/null +++ b/plugins/decklink/data/locale/fa-IR.ini @@ -0,0 +1,22 @@ +BlackmagicDevice="Blackmagic دستگاه" +Device="دستگاه" +Mode="نوع" +Buffering="استفاده از بافرینگ" +PixelFormat="فرمت پیکسل" +ColorSpace.Default="پیش فرض" +ColorRange.Default="پیش فرض" +ColorRange.Partial="جزئی" +ColorRange.Full="کامل" +ChannelFormat="کانال" +ChannelFormat.None="هیچ‌کدام" +ChannelFormat.2_0ch="2ch" +ChannelFormat.2_1ch="2.1ch" +ChannelFormat.4_0ch="4ch" +ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="غیر فعال کردن زمانی که نمایش داده نشود" +AutoStart="شروع خودکار در راه اندازی" +VideoConnection="اتصال صوتی" +AudioConnection="اتصال صوتی" + diff --git a/plugins/decklink/data/locale/fi-FI.ini b/plugins/decklink/data/locale/fi-FI.ini index 7439eb2..007cb01 100644 --- a/plugins/decklink/data/locale/fi-FI.ini +++ b/plugins/decklink/data/locale/fi-FI.ini @@ -3,9 +3,9 @@ Device="Laite" Mode="Tila" Buffering="Käytä puskurointia" PixelFormat="Pikselimuoto" -ColorSpace="YUV väriavaruus" +ColorSpace="Väriavaruus" ColorSpace.Default="Oletusarvo" -ColorRange="YUV värialue" +ColorRange="Värialue" ColorRange.Default="Oletusarvo" ColorRange.Partial="Osittainen" ColorRange.Full="Täysi" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Poista käytöstä piilotettaessa" +AutoStart="Aloita automaattisesti käynnistyksessä" +SwapFC-LFE="Vaihda FC ja LFE" +SwapFC-LFE.Tooltip="Vaihda etummainen keskikanava ja LFE-kanava" +VideoConnection="Kuvayhteys" +AudioConnection="Ääniyhteys" diff --git a/plugins/decklink/data/locale/fil-PH.ini b/plugins/decklink/data/locale/fil-PH.ini index 9f0477c..e7c0399 100644 --- a/plugins/decklink/data/locale/fil-PH.ini +++ b/plugins/decklink/data/locale/fil-PH.ini @@ -3,9 +3,7 @@ Device="Kagamitan" Mode="Mode" Buffering="Gamitin ang Buffering" PixelFormat="Format ng Pixel" -ColorSpace="YUV Kulay Space" ColorSpace.Default="Pangunahin" -ColorRange="Saklaw ng Kulay ng YUV" ColorRange.Default="Pangunahin" ColorRange.Partial="Bahagyang" ColorRange.Full="Buong" diff --git a/plugins/decklink/data/locale/fr-FR.ini b/plugins/decklink/data/locale/fr-FR.ini index 9248d58..d958ff7 100644 --- a/plugins/decklink/data/locale/fr-FR.ini +++ b/plugins/decklink/data/locale/fr-FR.ini @@ -7,7 +7,7 @@ ColorSpace="Espace de couleurs YUV" ColorSpace.Default="Défaut" ColorRange="Gamme de couleurs YUV" ColorRange.Default="Défaut" -ColorRange.Partial="Partiel" +ColorRange.Partial="Partielle" ColorRange.Full="Complète" ChannelFormat="Canaux audio" ChannelFormat.None="Aucun" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4 canaux" ChannelFormat.4_1ch="4.1 canaux" ChannelFormat.5_1ch="5.1 canaux" ChannelFormat.7_1ch="7.1 canaux" +DeactivateWhenNotShowing="Désactiver si non-affiché" +AutoStart="Démarrer automatiquement au lancement" +SwapFC-LFE="Permuter Canal FC (Centre Avant) et Caisson de Basses" +SwapFC-LFE.Tooltip="Permuter le canal Centre Avant (FC) et le Caisson de Basses (LFE)" +VideoConnection="Connexion vidéo" +AudioConnection="Connexion audio" diff --git a/plugins/decklink/data/locale/gd-GB.ini b/plugins/decklink/data/locale/gd-GB.ini index ecb1814..691e33f 100644 --- a/plugins/decklink/data/locale/gd-GB.ini +++ b/plugins/decklink/data/locale/gd-GB.ini @@ -1,9 +1,7 @@ Device="Uidheam" Mode="Modh" Buffering="Cleachd bufaireadh" -ColorSpace="Spàs dhathan YUV" ColorSpace.Default="Bun-roghainn" -ColorRange="Rainse dhathan YUV" ColorRange.Default="Bun-roghainn" ColorRange.Partial="Leth-phàirteach" ColorRange.Full="Làn" diff --git a/plugins/decklink/data/locale/hu-HU.ini b/plugins/decklink/data/locale/hu-HU.ini index 7ed2de1..3a5b236 100644 --- a/plugins/decklink/data/locale/hu-HU.ini +++ b/plugins/decklink/data/locale/hu-HU.ini @@ -3,9 +3,9 @@ Device="Eszköz" Mode="Mód" Buffering="Pufferelés használata" PixelFormat="Képpont formátum" -ColorSpace="YUV színtér" +ColorSpace="Színtér" ColorSpace.Default="Alapértelmezett" -ColorRange="YUV színtartomány" +ColorRange="Színtartomány" ColorRange.Default="Alapértelmezett" ColorRange.Partial="Részleges" ColorRange.Full="Teljes" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4cs" ChannelFormat.4_1ch="4.1cs" ChannelFormat.5_1ch="5.1cs" ChannelFormat.7_1ch="7.1cs" +DeactivateWhenNotShowing="Kikapcsolás, ha nem jelenik meg" +AutoStart="Automatikus start induláskor" +SwapFC-LFE="FC és LFE Megfordítása" +SwapFC-LFE.Tooltip="Elülső középső csatorna és az LFE csatorna megcserélése" +VideoConnection="Videó kapcsolat" +AudioConnection="Audio kapcsolat" diff --git a/plugins/decklink/data/locale/it-IT.ini b/plugins/decklink/data/locale/it-IT.ini index adac817..6572718 100644 --- a/plugins/decklink/data/locale/it-IT.ini +++ b/plugins/decklink/data/locale/it-IT.ini @@ -1,11 +1,11 @@ BlackmagicDevice="Dispositivo Blackmagic" Device="Dispositivo" Mode="Modalità" -Buffering="Usa buffer" +Buffering="Utilizza il buffering" PixelFormat="Formato pixel" -ColorSpace="Spazio colore YUV" +ColorSpace="Spazio colore" ColorSpace.Default="Predefinito" -ColorRange="Gamma di colore YUV" +ColorRange="Gamma di colori" ColorRange.Default="Predefinito" ColorRange.Partial="Parziale" ColorRange.Full="Intero" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4 canali" ChannelFormat.4_1ch="4.1 canali" ChannelFormat.5_1ch="5.1 canali" ChannelFormat.7_1ch="7.1 canali" +DeactivateWhenNotShowing="Disattiva quando non visibile" +AutoStart="Avvia assieme a OBS" +SwapFC-LFE="Scambia FC e LFE" +SwapFC-LFE.Tooltip="Scambia il canale centrale davanti con quello LFE" +VideoConnection="Connessione video" +AudioConnection="Connessione audio" diff --git a/plugins/decklink/data/locale/ja-JP.ini b/plugins/decklink/data/locale/ja-JP.ini index a519119..896ff32 100644 --- a/plugins/decklink/data/locale/ja-JP.ini +++ b/plugins/decklink/data/locale/ja-JP.ini @@ -3,9 +3,9 @@ Device="デバイス" Mode="モード" Buffering="バッファリングを使用する" PixelFormat="ピクセルフォーマット" -ColorSpace="YUV 色空間" +ColorSpace="色空間" ColorSpace.Default="既定" -ColorRange="YUV 色範囲" +ColorRange="色範囲" ColorRange.Default="既定" ColorRange.Partial="一部" ColorRange.Full="全部" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4 ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="表示中でない場合非アクティブ化する" +AutoStart="起動時に自動的に開始" +SwapFC-LFE="FC と LFE を入れ替え" +SwapFC-LFE.Tooltip="フロントセンターチャンネルとサブウーファーチャンネルを入れ替える" +VideoConnection="映像接続" +AudioConnection="音声接続" diff --git a/plugins/decklink/data/locale/ka-GE.ini b/plugins/decklink/data/locale/ka-GE.ini index a655ee4..79f7ed0 100644 --- a/plugins/decklink/data/locale/ka-GE.ini +++ b/plugins/decklink/data/locale/ka-GE.ini @@ -1,20 +1,26 @@ -BlackmagicDevice="Blackmagic მოწყობილობა" +BlackmagicDevice="Blackmagic-მოწყობილობა" Device="მოწყობილობა" Mode="რეჟიმი" Buffering="ბუფერიზაციის გამოყენება" PixelFormat="პიქსელის ფორმატი" -ColorSpace="YUV ფერთა სისტემა" +ColorSpace="ფერთა სივრცე" ColorSpace.Default="ნაგულისხმევი" -ColorRange="YUV ფერთა გამა" +ColorRange="ფერთა გამა" ColorRange.Default="ნაგულისხმევი" ColorRange.Partial="ნაწილობრივი" ColorRange.Full="სრული" ChannelFormat="არხი" ChannelFormat.None="არცერთი" -ChannelFormat.2_0ch="2 არხიანი" -ChannelFormat.2_1ch="2.1 არხიანი" -ChannelFormat.4_0ch="4 არხიანი" -ChannelFormat.4_1ch="4.1 არხიანი" -ChannelFormat.5_1ch="5.1 არხიანი" -ChannelFormat.7_1ch="7.1 არხიანი" +ChannelFormat.2_0ch="2-არხიანი" +ChannelFormat.2_1ch="2.1-არხიანი" +ChannelFormat.4_0ch="4-არხიანი" +ChannelFormat.4_1ch="4.1-არხიანი" +ChannelFormat.5_1ch="5.1-არხიანი" +ChannelFormat.7_1ch="7.1-არხიანი" +DeactivateWhenNotShowing="გაუქმება, როცა არ ჩანს" +AutoStart="ავტომატური ჩართვა გაშვებისას" +SwapFC-LFE="შენაცვლდეს FC და LFE" +SwapFC-LFE.Tooltip="წინა ცენტრის არხისა და დაბალი სიხშირის არხის შენაცვლება" +VideoConnection="ვიდეოკავშირი" +AudioConnection="ხმოვანი კავშირი" diff --git a/plugins/decklink/data/locale/ko-KR.ini b/plugins/decklink/data/locale/ko-KR.ini index e4450d8..e7b9dc6 100644 --- a/plugins/decklink/data/locale/ko-KR.ini +++ b/plugins/decklink/data/locale/ko-KR.ini @@ -3,9 +3,9 @@ Device="장치" Mode="방식" Buffering="버퍼링 사용" PixelFormat="픽셀 형식" -ColorSpace="YUV 색 공간" +ColorSpace="색 공간" ColorSpace.Default="기본값" -ColorRange="YUV 색상 범위" +ColorRange="색상 범위" ColorRange.Default="기본값" ColorRange.Partial="부분" ColorRange.Full="전체" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4채널" ChannelFormat.4_1ch="4.1채널" ChannelFormat.5_1ch="5.1 채널" ChannelFormat.7_1ch="7.1 채널" +DeactivateWhenNotShowing="보이지 않을 경우 비활성화" +AutoStart="OBS Studio 실행 시 자동 시작" +SwapFC-LFE="FC 와 LFE 교환" +SwapFC-LFE.Tooltip="전면 중앙 채널과 저주파 채널 교환" +VideoConnection="비디오 연결" +AudioConnection="오디오 연결" diff --git a/plugins/decklink/data/locale/mn-MN.ini b/plugins/decklink/data/locale/mn-MN.ini new file mode 100644 index 0000000..2bdc181 --- /dev/null +++ b/plugins/decklink/data/locale/mn-MN.ini @@ -0,0 +1,2 @@ +DeactivateWhenNotShowing="Харуулаагүй байга үед идэвхгүй болгох" + diff --git a/plugins/decklink/data/locale/nb-NO.ini b/plugins/decklink/data/locale/nb-NO.ini index d93b304..682d9c9 100644 --- a/plugins/decklink/data/locale/nb-NO.ini +++ b/plugins/decklink/data/locale/nb-NO.ini @@ -3,9 +3,9 @@ Device="Enhet" Mode="Modus" Buffering="Bruk bufring" PixelFormat="Pikselformat" -ColorSpace="YUV fargerom" +ColorSpace="Fargerom" ColorSpace.Default="Standard" -ColorRange="YUV fargerom" +ColorRange="Fargespekter" ColorRange.Default="Standard" ColorRange.Partial="Delvis" ColorRange.Full="Hel" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Deaktiver når denne ikke vises" +AutoStart="Automatisk start ved oppstart" +SwapFC-LFE="Bytt FC og LFE" +SwapFC-LFE.Tooltip="Bytt front-kanal og LFE-kanal" +VideoConnection="Videotilkobling" +AudioConnection="Lydtilkobling" diff --git a/plugins/decklink/data/locale/nl-NL.ini b/plugins/decklink/data/locale/nl-NL.ini index e610e7c..a2be026 100644 --- a/plugins/decklink/data/locale/nl-NL.ini +++ b/plugins/decklink/data/locale/nl-NL.ini @@ -3,9 +3,9 @@ Device="Apparaat" Mode="Modus" Buffering="Buffering Gebruiken" PixelFormat="Pixelindeling" -ColorSpace="YUV-Kleurruimte" +ColorSpace="Kleurruimte" ColorSpace.Default="Standaard" -ColorRange="YUV Kleurbereik" +ColorRange="Kleurbereik" ColorRange.Default="Standaard" ColorRange.Partial="Gedeeltelijk" ColorRange.Full="Volledig" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Deactiveer wanneer niet zichtbaar" +AutoStart="Autostart bij lancering" +SwapFC-LFE="Wissel FC en LFE" +SwapFC-LFE.Tooltip="Verwissel midden voor kanaal en lage tonenkanaal" +VideoConnection="Videoverbinding" +AudioConnection="Audio-verbinding" diff --git a/plugins/decklink/data/locale/pl-PL.ini b/plugins/decklink/data/locale/pl-PL.ini index 92a226a..f2addfc 100644 --- a/plugins/decklink/data/locale/pl-PL.ini +++ b/plugins/decklink/data/locale/pl-PL.ini @@ -3,9 +3,9 @@ Device="Urządzenie" Mode="Tryb" Buffering="Użyj buforowania" PixelFormat="Format pikseli" -ColorSpace="Przestrzeń kolorów YUV" +ColorSpace="Przestrzeń kolorów" ColorSpace.Default="Domyślne" -ColorRange="Zakres kolorów YUV" +ColorRange="Zakres kolorów" ColorRange.Default="Domyślne" ColorRange.Partial="Częściowy" ColorRange.Full="Pełny" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4.0" ChannelFormat.4_1ch="4.1" ChannelFormat.5_1ch="5.1" ChannelFormat.7_1ch="7.1" +DeactivateWhenNotShowing="Wyłącz, gdy nie jest pokazywane" +AutoStart="Uruchamiaj automatycznie przy starcie" +SwapFC-LFE="Zamień FC i LFE" +SwapFC-LFE.Tooltip="Zamień centralny kanał z subwooferem" +VideoConnection="Połączenie wideo" +AudioConnection="Połączenie audio" diff --git a/plugins/decklink/data/locale/pt-BR.ini b/plugins/decklink/data/locale/pt-BR.ini index cabb030..298273f 100644 --- a/plugins/decklink/data/locale/pt-BR.ini +++ b/plugins/decklink/data/locale/pt-BR.ini @@ -3,9 +3,9 @@ Device="Dispositivo" Mode="Modo" Buffering="Utilizar Buffering" PixelFormat="Formato de Pixel" -ColorSpace="Espaço de cor YUV" +ColorSpace="Espaço de cor" ColorSpace.Default="Padrão" -ColorRange="Intervalo de Cores YUV" +ColorRange="Faixa de cores" ColorRange.Default="Padrão" ColorRange.Partial="Parcial" ColorRange.Full="Completo" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4.0" ChannelFormat.4_1ch="4.1" ChannelFormat.5_1ch="5.1" ChannelFormat.7_1ch="7.1" +DeactivateWhenNotShowing="Desativar quando não estiver visível" +AutoStart="Iniciar ao abrir o OBS" +SwapFC-LFE="Trocar C e SUB" +SwapFC-LFE.Tooltip="Trocar canal Central com o Subwoofer" +VideoConnection="Conexão de Vídeo" +AudioConnection="Conexão de Áudio" diff --git a/plugins/decklink/data/locale/pt-PT.ini b/plugins/decklink/data/locale/pt-PT.ini index cc47aa0..e23f4d7 100644 --- a/plugins/decklink/data/locale/pt-PT.ini +++ b/plugins/decklink/data/locale/pt-PT.ini @@ -3,7 +3,18 @@ Device="Dispositivo" Mode="Modo" Buffering="Utilizar Buffering" PixelFormat="Formato de pixel" +ColorSpace.Default="Predefinido" +ColorRange.Default="Predefinido" ColorRange.Partial="Parcial" +ColorRange.Full="Completo" ChannelFormat="Canal" ChannelFormat.None="Nenhum" +ChannelFormat.2_0ch="2ch" +ChannelFormat.2_1ch="2.1ch" +ChannelFormat.4_0ch="4ch" +ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Desativar quando não visível" +AutoStart="Iniciar automaticamente ao arrancar" diff --git a/plugins/decklink/data/locale/ro-RO.ini b/plugins/decklink/data/locale/ro-RO.ini index e3a29fa..24515cf 100644 --- a/plugins/decklink/data/locale/ro-RO.ini +++ b/plugins/decklink/data/locale/ro-RO.ini @@ -1,6 +1,8 @@ BlackmagicDevice="Dispozitiv Blackmagic" Device="Dispozitiv" Mode="Mod" -Buffering="Folosește buffering" +Buffering="Folosește zona tampon" PixelFormat="Formatul pixelilor" +ColorSpace.Default="Implicit" +ColorRange.Default="Implicit" diff --git a/plugins/decklink/data/locale/ru-RU.ini b/plugins/decklink/data/locale/ru-RU.ini index 0dce592..fccde7c 100644 --- a/plugins/decklink/data/locale/ru-RU.ini +++ b/plugins/decklink/data/locale/ru-RU.ini @@ -3,9 +3,9 @@ Device="Устройство" Mode="Режим" Buffering="Использовать буферизацию" PixelFormat="Формат пикселей" -ColorSpace="Цветовое пространство YUV" +ColorSpace="Цветовое пространство" ColorSpace.Default="По умолчанию" -ColorRange="Цветовой диапазон YUV" +ColorRange="Цветовой диапазон" ColorRange.Default="По умолчанию" ColorRange.Partial="Частичный" ColorRange.Full="Полный" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4-канальный" ChannelFormat.4_1ch="4.1-канальный" ChannelFormat.5_1ch="5.1-канальный" ChannelFormat.7_1ch="7.1-канальный" +DeactivateWhenNotShowing="Выключать, когда не показывается" +AutoStart="Авто-старт при запуске" +SwapFC-LFE="Обменять ПЦ и LFE" +SwapFC-LFE.Tooltip="Обменять передний центральный канал и LFE канал" +VideoConnection="Видео соединение" +AudioConnection="Аудио соединение" diff --git a/plugins/decklink/data/locale/sk-SK.ini b/plugins/decklink/data/locale/sk-SK.ini index 439f7dd..be2e463 100644 --- a/plugins/decklink/data/locale/sk-SK.ini +++ b/plugins/decklink/data/locale/sk-SK.ini @@ -3,15 +3,24 @@ Device="Zariadenie" Mode="Mód" Buffering="Použiť vyrovnávaciu pamäť" PixelFormat="Formát pixelov" -ColorSpace="Farebný priestor YUV" +ColorSpace="Farebný priestor" ColorSpace.Default="Predvolený" -ColorRange="Rozsah farieb YUV" +ColorRange="Farebný rozsah" ColorRange.Default="Predvolený" ColorRange.Partial="Čiastočný" ColorRange.Full="Plný" ChannelFormat="Kanál" ChannelFormat.None="Nič" ChannelFormat.2_0ch="2ch" +ChannelFormat.2_1ch="2.1ch" ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" +ChannelFormat.5_1ch="5.1ch" +ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Deaktivovať, keď je skrytý" +AutoStart="Automaticky spustiť pri štarte" +SwapFC-LFE="Vymeniť FC a LFE" +SwapFC-LFE.Tooltip="Vymeniť kanál Front Center a kanál LFE" +VideoConnection="Video pripojenie" +AudioConnection="Audio pripojenie" diff --git a/plugins/decklink/data/locale/sr-CS.ini b/plugins/decklink/data/locale/sr-CS.ini index 54d4274..64b6ff4 100644 --- a/plugins/decklink/data/locale/sr-CS.ini +++ b/plugins/decklink/data/locale/sr-CS.ini @@ -3,4 +3,18 @@ Device="Uređaj" Mode="Režim" Buffering="Koristi baferovanje" PixelFormat="Piksel format" +ColorSpace.Default="Podrazumevan" +ColorRange.Default="Podrazumevan" +ColorRange.Partial="Delimičan" +ColorRange.Full="Potpun" +ChannelFormat="Kanal" +ChannelFormat.None="Nijedan" +ChannelFormat.2_0ch="2k" +ChannelFormat.2_1ch="2.1k" +ChannelFormat.4_0ch="4k" +ChannelFormat.4_1ch="4.1k" +ChannelFormat.5_1ch="5.1k" +ChannelFormat.7_1ch="7.1k" +DeactivateWhenNotShowing="U slučaju da se ne prikazuje - deaktiviraj" +AutoStart="Automatski uključi pri pokretanju programa" diff --git a/plugins/decklink/data/locale/sr-SP.ini b/plugins/decklink/data/locale/sr-SP.ini index 50bd35b..0bf4dcc 100644 --- a/plugins/decklink/data/locale/sr-SP.ini +++ b/plugins/decklink/data/locale/sr-SP.ini @@ -3,4 +3,18 @@ Device="Уређај" Mode="Режим" Buffering="Користи баферовање" PixelFormat="Пиксел формат" +ColorSpace.Default="Подразумеван" +ColorRange.Default="Подразумеван" +ColorRange.Partial="Делимичан" +ColorRange.Full="Потпун" +ChannelFormat="Канал" +ChannelFormat.None="Ниједан" +ChannelFormat.2_0ch="2к" +ChannelFormat.2_1ch="2.1к" +ChannelFormat.4_0ch="4к" +ChannelFormat.4_1ch="4.1к" +ChannelFormat.5_1ch="5.1к" +ChannelFormat.7_1ch="7.1к" +DeactivateWhenNotShowing="У случају да се не приказује - деактивирај" +AutoStart="Аутоматски укључи при покретању програма" diff --git a/plugins/decklink/data/locale/sv-SE.ini b/plugins/decklink/data/locale/sv-SE.ini index 12f6e39..494e677 100644 --- a/plugins/decklink/data/locale/sv-SE.ini +++ b/plugins/decklink/data/locale/sv-SE.ini @@ -3,9 +3,9 @@ Device="Enhet" Mode="Läge" Buffering="Använd buffert" PixelFormat="Bildpunktsformat" -ColorSpace="YUV-färgrymd" +ColorSpace="Färgrymd" ColorSpace.Default="Standard" -ColorRange="YUV-färgområde" +ColorRange="Färgintervall" ColorRange.Default="Standard" ColorRange.Partial="Partiell" ColorRange.Full="Full" @@ -17,4 +17,10 @@ ChannelFormat.4_0ch="4 kanaler" ChannelFormat.4_1ch="4.1 kanaler" ChannelFormat.5_1ch="5.1 kanaler" ChannelFormat.7_1ch="7.1 kanaler" +DeactivateWhenNotShowing="Inaktivera när den inte visas" +AutoStart="Autostarta vid uppstart" +SwapFC-LFE="Byt FC och LFE" +SwapFC-LFE.Tooltip="Byt Front Center-kanal och LFE-kanal" +VideoConnection="Videoanslutning" +AudioConnection="Ljudanslutning" diff --git a/plugins/decklink/data/locale/tl-PH.ini b/plugins/decklink/data/locale/tl-PH.ini index 6eccea9..6cb8bc9 100644 --- a/plugins/decklink/data/locale/tl-PH.ini +++ b/plugins/decklink/data/locale/tl-PH.ini @@ -3,9 +3,7 @@ Device="Aparato" Mode="I-mode" Buffering="Paggamit ng Buffering" PixelFormat="Ang Format ng Pixel" -ColorSpace="Pagitan sa kulay na YUV" ColorSpace.Default="I-default" -ColorRange="Ang Saklaw ng Kulay na YUV" ColorRange.Default="I-default" ColorRange.Partial="Bahagya" ColorRange.Full="Puno" diff --git a/plugins/decklink/data/locale/tr-TR.ini b/plugins/decklink/data/locale/tr-TR.ini index 1626abf..1c6d8b7 100644 --- a/plugins/decklink/data/locale/tr-TR.ini +++ b/plugins/decklink/data/locale/tr-TR.ini @@ -3,9 +3,9 @@ Device="Aygıt" Mode="Mod" Buffering="Arabelleğe Almayı Kullan" PixelFormat="Piksel Biçimi" -ColorSpace="YUV Renk Alanı" +ColorSpace="Renk Uzayı" ColorSpace.Default="Varsayılan" -ColorRange="YUV Renk Aralığı" +ColorRange="Renk Aralığı" ColorRange.Default="Varsayılan" ColorRange.Partial="Kısmi" ColorRange.Full="Tam" @@ -17,4 +17,8 @@ ChannelFormat.4_0ch="4ch" ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" +DeactivateWhenNotShowing="Gösterilmediğinde devre dışı bırak" +AutoStart="Açılışta otomatik olarak başlat" +VideoConnection="Video Bağlantısı" +AudioConnection="Ses Bağlantısı" diff --git a/plugins/decklink/data/locale/uk-UA.ini b/plugins/decklink/data/locale/uk-UA.ini index 9a12869..6e735b6 100644 --- a/plugins/decklink/data/locale/uk-UA.ini +++ b/plugins/decklink/data/locale/uk-UA.ini @@ -3,9 +3,7 @@ Device="Пристрій" Mode="Режим" Buffering="Увімкнути буферизацію" PixelFormat="Формат пікселів" -ColorSpace="Колірний простір YUV" ColorSpace.Default="За замовчуванням" -ColorRange="Колірний діапазон YUV" ColorRange.Default="За замовчуванням" ColorRange.Partial="Частковий" ColorRange.Full="Повний" @@ -17,4 +15,10 @@ ChannelFormat.4_0ch="4-канальний" ChannelFormat.4_1ch="4.1-канальний" ChannelFormat.5_1ch="5.1-канальний" ChannelFormat.7_1ch="7.1-канальний" +DeactivateWhenNotShowing="Деактивувати, коли не виводиться" +AutoStart="Запускати автоматично" +SwapFC-LFE="Поміняти місцями FC та LFE" +SwapFC-LFE.Tooltip="Міняє місцями Front Center та LFE канали" +VideoConnection="Відео вхід" +AudioConnection="Аудіо вхід" diff --git a/plugins/decklink/data/locale/zh-CN.ini b/plugins/decklink/data/locale/zh-CN.ini index e8b179d..7b42f9a 100644 --- a/plugins/decklink/data/locale/zh-CN.ini +++ b/plugins/decklink/data/locale/zh-CN.ini @@ -2,19 +2,25 @@ BlackmagicDevice="Blackmagic设备" Device="设备" Mode="模式" Buffering="使用缓冲" -PixelFormat="像素格式" -ColorSpace="YUV 颜色空间" +PixelFormat="视频格式" +ColorSpace="色彩空间" ColorSpace.Default="默认" -ColorRange="YUV 颜色范围" +ColorRange="色彩范围" ColorRange.Default="默认" ColorRange.Partial="局部" ColorRange.Full="全部" ChannelFormat="频道" ChannelFormat.None="无" -ChannelFormat.2_0ch="2ch" -ChannelFormat.2_1ch="2.1ch" -ChannelFormat.4_0ch="4ch" -ChannelFormat.4_1ch="4.1ch" -ChannelFormat.5_1ch="5.1ch" -ChannelFormat.7_1ch="7.1ch" +ChannelFormat.2_0ch="双声道" +ChannelFormat.2_1ch="2.1声道" +ChannelFormat.4_0ch="4声道" +ChannelFormat.4_1ch="4.1声道" +ChannelFormat.5_1ch="5.1声道" +ChannelFormat.7_1ch="7.1声道" +DeactivateWhenNotShowing="当不显示时禁用" +AutoStart="启动时自动启动" +SwapFC-LFE="交换 FC 和 LFE" +SwapFC-LFE.Tooltip="切换前中(FC)声道与低频效果(LFE)声道" +VideoConnection="视频连接" +AudioConnection="声音连接" diff --git a/plugins/decklink/data/locale/zh-TW.ini b/plugins/decklink/data/locale/zh-TW.ini index ebbfaaa..ab75c32 100644 --- a/plugins/decklink/data/locale/zh-TW.ini +++ b/plugins/decklink/data/locale/zh-TW.ini @@ -3,18 +3,24 @@ Device="裝置" Mode="模式" Buffering="使用緩衝" PixelFormat="像素格式" -ColorSpace="YUV 色彩空間" +ColorSpace="色彩空間" ColorSpace.Default="預設" -ColorRange="YUV 色彩範圍" +ColorRange="顏色範圍" ColorRange.Default="預設" ColorRange.Partial="部分" ColorRange.Full="完整" ChannelFormat="聲道" ChannelFormat.None="無" -ChannelFormat.2_0ch="雙聲道" +ChannelFormat.2_0ch="雙聲道(2ch)" ChannelFormat.2_1ch="2.1聲道" ChannelFormat.4_0ch="4聲道" ChannelFormat.4_1ch="4.1聲道" ChannelFormat.5_1ch="5.1聲道" ChannelFormat.7_1ch="7.1聲道" +DeactivateWhenNotShowing="不顯示時停用" +AutoStart="啟動時自動開啟" +SwapFC-LFE="切換 FC 和 LFE" +SwapFC-LFE.Tooltip="切換 Front Center (FC) 頻道和 LFE 頻道" +VideoConnection="影像連接" +AudioConnection="聲音連接" diff --git a/plugins/decklink/decklink-device-discovery.hpp b/plugins/decklink/decklink-device-discovery.hpp index 3bb6e3c..9f2842c 100644 --- a/plugins/decklink/decklink-device-discovery.hpp +++ b/plugins/decklink/decklink-device-discovery.hpp @@ -1,10 +1,11 @@ #pragma once +#include +#include "platform.hpp" + #include #include -#include "decklink.hpp" - class DeckLinkDevice; typedef void (*DeviceChangeCallback)(void *param, DeckLinkDevice *device, diff --git a/plugins/decklink/decklink-device-instance.cpp b/plugins/decklink/decklink-device-instance.cpp index 5dc674e..e2e13d1 100644 --- a/plugins/decklink/decklink-device-instance.cpp +++ b/plugins/decklink/decklink-device-instance.cpp @@ -1,19 +1,14 @@ #include "decklink-device-instance.hpp" #include "audio-repack.hpp" +#include "DecklinkInput.hpp" +#include "DecklinkOutput.hpp" + #include #include #include - -#define LOG(level, message, ...) blog(level, "%s: " message, \ - obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__) - -#ifdef _WIN32 -#define IS_WIN 1 -#else -#define IS_WIN 0 -#endif +#include static inline enum video_format ConvertPixelFormat(BMDPixelFormat format) { @@ -43,26 +38,26 @@ static inline int ConvertChannelFormat(speaker_layout format) } } -static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format) +static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format, bool swap) { switch (format) { case SPEAKERS_2POINT1: - return repack_mode_8to3ch_swap23; + return repack_mode_8to3ch; case SPEAKERS_4POINT0: - return repack_mode_8to4ch_swap23; + return repack_mode_8to4ch; case SPEAKERS_4POINT1: - return repack_mode_8to5ch_swap23; + return swap? repack_mode_8to5ch_swap:repack_mode_8to5ch; case SPEAKERS_5POINT1: - return repack_mode_8to6ch_swap23; + return swap ? repack_mode_8to6ch_swap : repack_mode_8to6ch; case SPEAKERS_7POINT1: - return repack_mode_8ch_swap23_swap46_swap57; + return swap ? repack_mode_8ch_swap: repack_mode_8ch; default: assert(false && "No repack requested"); return (audio_repack_mode_t)-1; } } -DeckLinkDeviceInstance::DeckLinkDeviceInstance(DeckLink *decklink_, +DeckLinkDeviceInstance::DeckLinkDeviceInstance(DecklinkBase *decklink_, DeckLinkDevice *device_) : currentFrame(), currentPacket(), decklink(decklink_), device(device_) { @@ -92,7 +87,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket( currentPacket.frames = frameCount; currentPacket.timestamp = timestamp; - if (decklink && !decklink->buffering) { + if (decklink && !static_cast(decklink)->buffering) { currentPacket.timestamp = os_gettime_ns(); currentPacket.timestamp -= (uint64_t)frameCount * 1000000000ULL / @@ -100,13 +95,12 @@ void DeckLinkDeviceInstance::HandleAudioPacket( } int maxdevicechannel = device->GetMaxChannel(); - bool isWin = IS_WIN; if (channelFormat != SPEAKERS_UNKNOWN && channelFormat != SPEAKERS_MONO && channelFormat != SPEAKERS_STEREO && - maxdevicechannel >= 8 && - isWin) { + (channelFormat != SPEAKERS_7POINT1 || static_cast(decklink)->swap) + && maxdevicechannel >= 8) { if (audioRepacker->repack((uint8_t *)bytes, frameCount) < 0) { LOG(LOG_ERROR, "Failed to convert audio packet data"); @@ -120,7 +114,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket( nextAudioTS = timestamp + ((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1; - obs_source_output_audio(decklink->GetSource(), ¤tPacket); + obs_source_output_audio(static_cast(decklink)->GetSource(), ¤tPacket); } void DeckLinkDeviceInstance::HandleVideoFrame( @@ -141,7 +135,7 @@ void DeckLinkDeviceInstance::HandleVideoFrame( currentFrame.height = (uint32_t)videoFrame->GetHeight(); currentFrame.timestamp = timestamp; - obs_source_output_video(decklink->GetSource(), ¤tFrame); + obs_source_output_video2(static_cast(decklink)->GetSource(), ¤tFrame); } void DeckLinkDeviceInstance::FinalizeStream() @@ -169,7 +163,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) currentFrame.format = ConvertPixelFormat(pixelFormat); - colorSpace = decklink->GetColorSpace(); + colorSpace = static_cast(decklink)->GetColorSpace(); if (colorSpace == VIDEO_CS_DEFAULT) { const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags(); if (flags & bmdDisplayModeColorspaceRec709) @@ -182,8 +176,8 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) activeColorSpace = colorSpace; } - colorRange = decklink->GetColorRange(); - currentFrame.full_range = colorRange == VIDEO_RANGE_FULL; + colorRange = static_cast(decklink)->GetColorRange(); + currentFrame.range = colorRange; video_format_get_parameters(activeColorSpace, colorRange, currentFrame.color_matrix, currentFrame.color_range_min, @@ -197,7 +191,9 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) #endif } -bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) +bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_, + BMDVideoConnection bmdVideoConnection, + BMDAudioConnection bmdAudioConnection) { if (mode != nullptr) return false; @@ -209,6 +205,40 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) if (!device->GetInput(&input)) return false; + + IDeckLinkConfiguration *deckLinkConfiguration = NULL; + HRESULT result = input->QueryInterface(IID_IDeckLinkConfiguration, + (void**)&deckLinkConfiguration); + if (result != S_OK) + { + LOG(LOG_ERROR, + "Could not obtain the IDeckLinkConfiguration interface: %08x\n", + result); + } else { + if (bmdVideoConnection > 0) { + result = deckLinkConfiguration->SetInt( + bmdDeckLinkConfigVideoInputConnection, bmdVideoConnection); + if (result != S_OK) { + LOG(LOG_ERROR, + "Couldn't set input video port to %d\n\n", + bmdVideoConnection); + } + } + + if (bmdAudioConnection > 0) { + result = deckLinkConfiguration->SetInt( + bmdDeckLinkConfigAudioInputConnection, bmdAudioConnection); + if (result != S_OK) { + LOG(LOG_ERROR, + "Couldn't set input audio port to %d\n\n", + bmdVideoConnection); + } + } + } + + videoConnection = bmdVideoConnection; + audioConnection = bmdAudioConnection; + BMDVideoInputFlags flags; bool isauto = mode_->GetName() == "Auto"; @@ -218,7 +248,7 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) flags = bmdVideoInputEnableFormatDetection; } else { displayMode = mode_->GetDisplayMode(); - pixelFormat = decklink->GetPixelFormat(); + pixelFormat = static_cast(decklink)->GetPixelFormat(); flags = bmdVideoInputFlagDefault; } @@ -231,11 +261,11 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) SetupVideoFormat(mode_); - channelFormat = decklink->GetChannelFormat(); + channelFormat = static_cast(decklink)->GetChannelFormat(); currentPacket.speakers = channelFormat; + swap = static_cast(decklink)->swap; int maxdevicechannel = device->GetMaxChannel(); - bool isWin = IS_WIN; if (channelFormat != SPEAKERS_UNKNOWN) { const int channel = ConvertChannelFormat(channelFormat); @@ -248,11 +278,11 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) if (channelFormat != SPEAKERS_UNKNOWN && channelFormat != SPEAKERS_MONO && channelFormat != SPEAKERS_STEREO && - maxdevicechannel >= 8 && - isWin) { + (channelFormat != SPEAKERS_7POINT1 || swap) + && maxdevicechannel >= 8) { const audio_repack_mode_t repack_mode = ConvertRepackFormat - (channelFormat); + (channelFormat, swap); audioRepacker = new AudioRepacker(repack_mode); } } @@ -288,6 +318,128 @@ bool DeckLinkDeviceInstance::StopCapture(void) return true; } +bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_) +{ + if (mode != nullptr) + return false; + if (mode_ == nullptr) + return false; + + LOG(LOG_INFO, "Starting output..."); + + if (!device->GetOutput(&output)) + return false; + + const HRESULT videoResult = output->EnableVideoOutput( + mode_->GetDisplayMode(), + bmdVideoOutputFlagDefault); + if (videoResult != S_OK) { + LOG(LOG_ERROR, "Failed to enable video output"); + return false; + } + + const HRESULT audioResult = output->EnableAudioOutput( + bmdAudioSampleRate48kHz, + bmdAudioSampleType16bitInteger, + 2, + bmdAudioOutputStreamTimestamped); + if (audioResult != S_OK) { + LOG(LOG_ERROR, "Failed to enable audio output"); + return false; + } + + mode = mode_; + + int keyerMode = device->GetKeyerMode(); + + IDeckLinkKeyer *deckLinkKeyer = nullptr; + if (device->GetKeyer(&deckLinkKeyer)) { + if (keyerMode) { + deckLinkKeyer->Enable(keyerMode == 1); + deckLinkKeyer->SetLevel(255); + } else { + deckLinkKeyer->Disable(); + } + } + + auto decklinkOutput = dynamic_cast(decklink); + if (decklinkOutput == nullptr) + return false; + + int rowBytes = decklinkOutput->GetWidth() * 2; + if (decklinkOutput->keyerMode != 0) { + rowBytes = decklinkOutput->GetWidth() * 4; + } + + BMDPixelFormat pixelFormat = bmdFormat8BitYUV; + if (keyerMode != 0) { + pixelFormat = bmdFormat8BitBGRA; + } + + HRESULT result; + result = output->CreateVideoFrame(decklinkOutput->GetWidth(), + decklinkOutput->GetHeight(), + rowBytes, + pixelFormat, + bmdFrameFlagDefault, + &decklinkOutputFrame); + if (result != S_OK) { + blog(LOG_ERROR ,"failed to make frame 0x%X", result); + return false; + } + + return true; +} + +bool DeckLinkDeviceInstance::StopOutput() +{ + if (mode == nullptr || output == nullptr) + return false; + + LOG(LOG_INFO, "Stopping output of '%s'...", + GetDevice()->GetDisplayName().c_str()); + + output->DisableVideoOutput(); + output->DisableAudioOutput(); + + if (decklinkOutputFrame != nullptr) { + decklinkOutputFrame->Release(); + decklinkOutputFrame = nullptr; + } + + return true; +} + +void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame) +{ + auto decklinkOutput = dynamic_cast(decklink); + if (decklinkOutput == nullptr) + return; + + uint8_t *destData; + decklinkOutputFrame->GetBytes((void**)&destData); + + uint8_t *outData = frame->data[0]; + + int rowBytes = decklinkOutput->GetWidth() * 2; + if (device->GetKeyerMode()) { + rowBytes = decklinkOutput->GetWidth() * 4; + } + + std::copy(outData, outData + (decklinkOutput->GetHeight() * + rowBytes), destData); + + output->DisplayVideoFrameSync(decklinkOutputFrame); +} + +void DeckLinkDeviceInstance::WriteAudio(audio_data *frames) +{ + uint32_t sampleFramesWritten; + output->WriteAudioSamplesSync(frames->data[0], + frames->frames, + &sampleFramesWritten); +} + #define TIME_BASE 1000000000 HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived( diff --git a/plugins/decklink/decklink-device-instance.hpp b/plugins/decklink/decklink-device-instance.hpp index ffbed07..bee60e2 100644 --- a/plugins/decklink/decklink-device-instance.hpp +++ b/plugins/decklink/decklink-device-instance.hpp @@ -1,28 +1,39 @@ #pragma once +#define LOG(level, message, ...) blog(level, "%s: " message, "decklink", ##__VA_ARGS__) + +#include #include "decklink-device.hpp" +#include "../../libobs/media-io/video-scaler.h" class AudioRepacker; +class DecklinkBase; class DeckLinkDeviceInstance : public IDeckLinkInputCallback { protected: - struct obs_source_frame currentFrame; + struct obs_source_frame2 currentFrame; struct obs_source_audio currentPacket; - DeckLink *decklink = nullptr; + DecklinkBase *decklink = nullptr; DeckLinkDevice *device = nullptr; DeckLinkDeviceMode *mode = nullptr; + BMDVideoConnection videoConnection; + BMDAudioConnection audioConnection; BMDDisplayMode displayMode = bmdModeNTSC; BMDPixelFormat pixelFormat = bmdFormat8BitYUV; video_colorspace colorSpace = VIDEO_CS_DEFAULT; video_colorspace activeColorSpace = VIDEO_CS_DEFAULT; video_range_type colorRange = VIDEO_RANGE_DEFAULT; ComPtr input; + ComPtr output; volatile long refCount = 1; int64_t audioOffset = 0; uint64_t nextAudioTS = 0; uint64_t lastVideoTS = 0; AudioRepacker *audioRepacker = nullptr; speaker_layout channelFormat = SPEAKERS_STEREO; + bool swap; + + IDeckLinkMutableVideoFrame *decklinkOutputFrame = nullptr; void FinalizeStream(); void SetupVideoFormat(DeckLinkDeviceMode *mode_); @@ -33,7 +44,7 @@ protected: const uint64_t timestamp); public: - DeckLinkDeviceInstance(DeckLink *decklink, DeckLinkDevice *device); + DeckLinkDeviceInstance(DecklinkBase *decklink, DeckLinkDevice *device); virtual ~DeckLinkDeviceInstance(); inline DeckLinkDevice *GetDevice() const {return device;} @@ -46,12 +57,20 @@ public: inline video_colorspace GetActiveColorSpace() const {return colorSpace;} inline video_range_type GetActiveColorRange() const {return colorRange;} inline speaker_layout GetActiveChannelFormat() const {return channelFormat;} + inline bool GetActiveSwapState() const {return swap;} + inline BMDVideoConnection GetVideoConnection() const {return videoConnection;} + inline BMDAudioConnection GetAudioConnection() const {return audioConnection;} inline DeckLinkDeviceMode *GetMode() const {return mode;} - bool StartCapture(DeckLinkDeviceMode *mode); + bool StartCapture(DeckLinkDeviceMode *mode, + BMDVideoConnection bmdVideoConnection, + BMDAudioConnection bmdAudioConnection); bool StopCapture(void); + bool StartOutput(DeckLinkDeviceMode *mode_); + bool StopOutput(void); + HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioPacket); @@ -63,4 +82,7 @@ public: ULONG STDMETHODCALLTYPE AddRef(void); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv); ULONG STDMETHODCALLTYPE Release(void); + + void DisplayVideoFrame(video_data *frame); + void WriteAudio(audio_data *frames); }; diff --git a/plugins/decklink/decklink-device-mode.cpp b/plugins/decklink/decklink-device-mode.cpp index 48d6eac..67a052b 100644 --- a/plugins/decklink/decklink-device-mode.cpp +++ b/plugins/decklink/decklink-device-mode.cpp @@ -32,6 +32,22 @@ BMDDisplayMode DeckLinkDeviceMode::GetDisplayMode(void) const return bmdModeUnknown; } +int DeckLinkDeviceMode::GetWidth() +{ + if (mode != nullptr) + return mode->GetWidth(); + + return 0; +} + +int DeckLinkDeviceMode::GetHeight() +{ + if (mode != nullptr) + return mode->GetHeight(); + + return 0; +} + BMDDisplayModeFlags DeckLinkDeviceMode::GetDisplayModeFlags(void) const { if (mode != nullptr) diff --git a/plugins/decklink/decklink-device-mode.hpp b/plugins/decklink/decklink-device-mode.hpp index 8ff642d..ae00153 100644 --- a/plugins/decklink/decklink-device-mode.hpp +++ b/plugins/decklink/decklink-device-mode.hpp @@ -23,4 +23,7 @@ public: const std::string& GetName(void) const; void SetMode(IDeckLinkDisplayMode *mode); + + int GetWidth(); + int GetHeight(); }; diff --git a/plugins/decklink/decklink-device.cpp b/plugins/decklink/decklink-device.cpp index d173053..c2166cd 100644 --- a/plugins/decklink/decklink-device.cpp +++ b/plugins/decklink/decklink-device.cpp @@ -1,4 +1,4 @@ -#include +#include #include "decklink-device.hpp" @@ -10,7 +10,10 @@ DeckLinkDevice::DeckLinkDevice(IDeckLink *device_) : device(device_) DeckLinkDevice::~DeckLinkDevice(void) { - for (DeckLinkDeviceMode *mode : modes) + for (DeckLinkDeviceMode *mode : inputModes) + delete mode; + + for (DeckLinkDeviceMode *mode : outputModes) delete mode; } @@ -37,37 +40,85 @@ bool DeckLinkDevice::Init() decklink_bool_t detectable = false; if (attributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &detectable) == S_OK && !!detectable) { - DeckLinkDeviceMode *mode = - new DeckLinkDeviceMode("Auto", MODE_ID_AUTO); - modes.push_back(mode); - modeIdMap[MODE_ID_AUTO] = mode; + DeckLinkDeviceMode *mode = new DeckLinkDeviceMode( + "Auto", + MODE_ID_AUTO); + inputModes.push_back(mode); + inputModeIdMap[MODE_ID_AUTO] = mode; } } + // Find input modes ComPtr input; - if (device->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK) - return false; + if (device->QueryInterface(IID_IDeckLinkInput, (void **) &input) == S_OK) { + IDeckLinkDisplayModeIterator *modeIterator; + if (input->GetDisplayModeIterator(&modeIterator) == S_OK) { + IDeckLinkDisplayMode *displayMode; + long long modeId = 1; - IDeckLinkDisplayModeIterator *modeIterator; - if (input->GetDisplayModeIterator(&modeIterator) == S_OK) { - IDeckLinkDisplayMode *displayMode; - long long modeId = 1; + while (modeIterator->Next(&displayMode) == S_OK) { + if (displayMode == nullptr) + continue; - while (modeIterator->Next(&displayMode) == S_OK) { - if (displayMode == nullptr) - continue; + DeckLinkDeviceMode *mode = + new DeckLinkDeviceMode(displayMode, modeId); + inputModes.push_back(mode); + inputModeIdMap[modeId] = mode; + displayMode->Release(); + ++modeId; + } - DeckLinkDeviceMode *mode = - new DeckLinkDeviceMode(displayMode, modeId); - modes.push_back(mode); - modeIdMap[modeId] = mode; - displayMode->Release(); - ++modeId; + modeIterator->Release(); } - - modeIterator->Release(); } + // Get supported video connections + attributes->GetInt(BMDDeckLinkVideoInputConnections, + &supportedVideoInputConnections); + attributes->GetInt(BMDDeckLinkVideoOutputConnections, + &supportedVideoOutputConnections); + + // Get supported audio connections + attributes->GetInt(BMDDeckLinkAudioInputConnections, + &supportedAudioInputConnections); + attributes->GetInt(BMDDeckLinkAudioOutputConnections, + &supportedAudioOutputConnections); + + // find output modes + ComPtr output; + if (device->QueryInterface(IID_IDeckLinkOutput, (void **) &output) == S_OK) { + + IDeckLinkDisplayModeIterator *modeIterator; + if (output->GetDisplayModeIterator(&modeIterator) == S_OK) { + IDeckLinkDisplayMode *displayMode; + long long modeId = 1; + + while (modeIterator->Next(&displayMode) == S_OK) { + if (displayMode == nullptr) + continue; + + DeckLinkDeviceMode *mode = + new DeckLinkDeviceMode(displayMode, modeId); + outputModes.push_back(mode); + outputModeIdMap[modeId] = mode; + displayMode->Release(); + ++modeId; + } + + modeIterator->Release(); + } + } + + // get keyer support + attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, + &supportsExternalKeyer); + attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, + &supportsInternalKeyer); + + // Sub Device Counts + attributes->GetInt(BMDDeckLinkSubDeviceIndex, &subDeviceIndex); + attributes->GetInt(BMDDeckLinkNumberOfSubDevices, &numSubDevices); + decklink_string_t decklinkModelName; decklink_string_t decklinkDisplayName; @@ -115,9 +166,43 @@ bool DeckLinkDevice::GetInput(IDeckLinkInput **input) return true; } -DeckLinkDeviceMode *DeckLinkDevice::FindMode(long long id) +bool DeckLinkDevice::GetOutput(IDeckLinkOutput **output) { - return modeIdMap[id]; + if (device->QueryInterface(IID_IDeckLinkOutput, (void**)output) != S_OK) + return false; + + return true; +} + +bool DeckLinkDevice::GetKeyer(IDeckLinkKeyer **deckLinkKeyer) +{ + if (device->QueryInterface(IID_IDeckLinkKeyer, (void**)deckLinkKeyer) != S_OK) + { + fprintf(stderr, "Could not obtain the IDeckLinkKeyer interface\n"); + return false; + } + + return true; +} + +void DeckLinkDevice::SetKeyerMode(int newKeyerMode) +{ + keyerMode = newKeyerMode; +} + +int DeckLinkDevice::GetKeyerMode(void) +{ + return keyerMode; +} + +DeckLinkDeviceMode *DeckLinkDevice::FindInputMode(long long id) +{ + return inputModeIdMap[id]; +} + +DeckLinkDeviceMode *DeckLinkDevice::FindOutputMode(long long id) +{ + return outputModeIdMap[id]; } const std::string& DeckLinkDevice::GetDisplayName(void) @@ -130,9 +215,45 @@ const std::string& DeckLinkDevice::GetHash(void) const return hash; } -const std::vector& DeckLinkDevice::GetModes(void) const +const std::vector& DeckLinkDevice::GetInputModes(void) const { - return modes; + return inputModes; +} + +const std::vector& DeckLinkDevice::GetOutputModes(void) const +{ + return outputModes; +} + +int64_t DeckLinkDevice::GetVideoInputConnections() +{ + return supportedVideoInputConnections; +} + +int64_t DeckLinkDevice::GetAudioInputConnections() +{ + return supportedAudioInputConnections; +} + + +bool DeckLinkDevice::GetSupportsExternalKeyer(void) const +{ + return supportsExternalKeyer; +} + +bool DeckLinkDevice::GetSupportsInternalKeyer(void) const +{ + return supportsInternalKeyer; +} + +int64_t DeckLinkDevice::GetSubDeviceCount() +{ + return numSubDevices; +} + +int64_t DeckLinkDevice::GetSubDeviceIndex() +{ + return subDeviceIndex; } const std::string& DeckLinkDevice::GetName(void) const diff --git a/plugins/decklink/decklink-device.hpp b/plugins/decklink/decklink-device.hpp index 9825ad8..a882a6c 100644 --- a/plugins/decklink/decklink-device.hpp +++ b/plugins/decklink/decklink-device.hpp @@ -1,20 +1,33 @@ -#pragma once +#pragma once -#include "decklink.hpp" #include "decklink-device-mode.hpp" #include #include #include +#include + + class DeckLinkDevice { ComPtr device; - std::map modeIdMap; - std::vector modes; + std::map inputModeIdMap; + std::vector inputModes; + std::map outputModeIdMap; + std::vector outputModes; std::string name; std::string displayName; std::string hash; - int32_t maxChannel; + int32_t maxChannel = 0; + decklink_bool_t supportsExternalKeyer = false; + decklink_bool_t supportsInternalKeyer = false; + int64_t subDeviceIndex = 0; + int64_t numSubDevices = 0; + int64_t supportedVideoInputConnections = -1; + int64_t supportedVideoOutputConnections = -1; + int64_t supportedAudioInputConnections = -1; + int64_t supportedAudioOutputConnections = -1; + int keyerMode = 0; volatile long refCount = 1; public: @@ -26,14 +39,26 @@ public: bool Init(); - DeckLinkDeviceMode *FindMode(long long id); + DeckLinkDeviceMode *FindInputMode(long long id); + DeckLinkDeviceMode *FindOutputMode(long long id); const std::string& GetDisplayName(void); const std::string& GetHash(void) const; - const std::vector& GetModes(void) const; + const std::vector& GetInputModes(void) const; + const std::vector& GetOutputModes(void) const; + int64_t GetVideoInputConnections(); + int64_t GetAudioInputConnections(); + bool GetSupportsExternalKeyer(void) const; + bool GetSupportsInternalKeyer(void) const; + int64_t GetSubDeviceCount(); + int64_t GetSubDeviceIndex(); + int GetKeyerMode(void); + void SetKeyerMode(int newKeyerMode); const std::string& GetName(void) const; int32_t GetMaxChannel(void) const; bool GetInput(IDeckLinkInput **input); + bool GetOutput(IDeckLinkOutput **output); + bool GetKeyer(IDeckLinkKeyer **keyer); inline bool IsDevice(IDeckLink *device_) { diff --git a/plugins/decklink/decklink-devices.cpp b/plugins/decklink/decklink-devices.cpp new file mode 100644 index 0000000..28607ef --- /dev/null +++ b/plugins/decklink/decklink-devices.cpp @@ -0,0 +1,17 @@ +#include "decklink-devices.hpp" + +DeckLinkDeviceDiscovery *deviceEnum = nullptr; + +void fill_out_devices(obs_property_t *list) +{ + deviceEnum->Lock(); + + const std::vector &devices = deviceEnum->GetDevices(); + for (DeckLinkDevice *device : devices) { + obs_property_list_add_string(list, + device->GetDisplayName().c_str(), + device->GetHash().c_str()); + } + + deviceEnum->Unlock(); +} diff --git a/plugins/decklink/decklink-devices.hpp b/plugins/decklink/decklink-devices.hpp new file mode 100644 index 0000000..fa1d24c --- /dev/null +++ b/plugins/decklink/decklink-devices.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" + +extern DeckLinkDeviceDiscovery *deviceEnum; + +void fill_out_devices(obs_property_t *list); diff --git a/plugins/decklink/decklink-output.cpp b/plugins/decklink/decklink-output.cpp new file mode 100644 index 0000000..a5cf2f4 --- /dev/null +++ b/plugins/decklink/decklink-output.cpp @@ -0,0 +1,262 @@ +#include +#include + +#include "const.h" + +#include "DecklinkOutput.hpp" +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" +#include "decklink-devices.hpp" + +#include "../../libobs/media-io/video-scaler.h" + +static void decklink_output_destroy(void *data) +{ + auto *decklink = (DeckLinkOutput *)data; + delete decklink; +} + +static void *decklink_output_create(obs_data_t *settings, obs_output_t *output) +{ + auto *decklinkOutput = new DeckLinkOutput(output, deviceEnum); + + decklinkOutput->deviceHash = obs_data_get_string(settings, DEVICE_HASH); + decklinkOutput->modeID = obs_data_get_int(settings, MODE_ID); + decklinkOutput->keyerMode = obs_data_get_int(settings, KEYER); + + return decklinkOutput; +} + +static void decklink_output_update(void *data, obs_data_t *settings) +{ + auto *decklink = (DeckLinkOutput *)data; + + decklink->deviceHash = obs_data_get_string(settings, DEVICE_HASH); + decklink->modeID = obs_data_get_int(settings, MODE_ID); + decklink->keyerMode = obs_data_get_int(settings, KEYER); +} + +static bool decklink_output_start(void *data) +{ + auto *decklink = (DeckLinkOutput *)data; + struct obs_audio_info aoi; + + if (!obs_get_audio_info(&aoi)) { + blog(LOG_WARNING, "No active audio"); + return false; + } + + decklink->audio_samplerate = aoi.samples_per_sec; + decklink->audio_planes = 2; + decklink->audio_size = get_audio_size(AUDIO_FORMAT_16BIT, aoi.speakers, 1); + + decklink->start_timestamp = 0; + + ComPtr device; + + device.Set(deviceEnum->FindByHash(decklink->deviceHash)); + + DeckLinkDeviceMode *mode = device->FindOutputMode(decklink->modeID); + + decklink->SetSize(mode->GetWidth(), mode->GetHeight()); + + struct video_scale_info to = {}; + + if (decklink->keyerMode != 0) { + to.format = VIDEO_FORMAT_BGRA; + } else { + to.format = VIDEO_FORMAT_UYVY; + } + to.width = mode->GetWidth(); + to.height = mode->GetHeight(); + + obs_output_set_video_conversion(decklink->GetOutput(), &to); + + device->SetKeyerMode(decklink->keyerMode); + decklink->Activate(device, decklink->modeID); + + struct audio_convert_info conversion = {}; + conversion.format = AUDIO_FORMAT_16BIT; + conversion.speakers = SPEAKERS_STEREO; + conversion.samples_per_sec = 48000; // Only format the decklink supports + + obs_output_set_audio_conversion(decklink->GetOutput(), &conversion); + + if (!obs_output_begin_data_capture(decklink->GetOutput(), 0)) + return false; + + return true; +} + +static void decklink_output_stop(void *data, uint64_t) +{ + auto *decklink = (DeckLinkOutput *)data; + + obs_output_end_data_capture(decklink->GetOutput()); + + ComPtr device; + + device.Set(deviceEnum->FindByHash(decklink->deviceHash)); + + decklink->Deactivate(); +} + +static void decklink_output_raw_video(void *data, struct video_data *frame) +{ + auto *decklink = (DeckLinkOutput *)data; + + if (!decklink->start_timestamp) + decklink->start_timestamp = frame->timestamp; + + decklink->DisplayVideoFrame(frame); +} + +static bool prepare_audio(DeckLinkOutput *decklink, + const struct audio_data *frame, + struct audio_data *output) +{ + *output = *frame; + + if (frame->timestamp < decklink->start_timestamp) { + uint64_t duration = (uint64_t)frame->frames * 1000000000 / + (uint64_t)decklink->audio_samplerate; + uint64_t end_ts = frame->timestamp + duration; + uint64_t cutoff; + + if (end_ts <= decklink->start_timestamp) + return false; + + cutoff = decklink->start_timestamp - frame->timestamp; + output->timestamp += cutoff; + + cutoff *= (uint64_t)decklink->audio_samplerate / 1000000000; + + for (size_t i = 0; i < decklink->audio_planes; i++) + output->data[i] += decklink->audio_size * + (uint32_t)cutoff; + + output->frames -= (uint32_t)cutoff; + } + + return true; +} + +static void decklink_output_raw_audio(void *data, struct audio_data *frames) +{ + auto *decklink = (DeckLinkOutput *)data; + struct audio_data in; + + if (!decklink->start_timestamp) + return; + + if (!prepare_audio(decklink, frames, &in)) + return; + + decklink->WriteAudio(&in); +} + +static bool decklink_output_device_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + const char *name = obs_data_get_string(settings, DEVICE_NAME); + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + const char *mode = obs_data_get_string(settings, MODE_NAME); + long long modeId = obs_data_get_int(settings, MODE_ID); + + size_t itemCount = obs_property_list_item_count(list); + bool itemFound = false; + + for (size_t i = 0; i < itemCount; i++) { + const char *curHash = obs_property_list_item_string(list, i); + if (strcmp(hash, curHash) == 0) { + itemFound = true; + break; + } + } + + if (!itemFound) { + obs_property_list_insert_string(list, 0, name, hash); + obs_property_list_item_disable(list, 0, true); + } + + obs_property_t *modeList = obs_properties_get(props, MODE_ID); + obs_property_t *keyerList = obs_properties_get(props, KEYER); + + obs_property_list_clear(modeList); + obs_property_list_clear(keyerList); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + if (!device) { + obs_property_list_add_int(modeList, mode, modeId); + obs_property_list_item_disable(modeList, 0, true); + obs_property_list_item_disable(keyerList, 0, true); + } else { + const std::vector &modes = + device->GetOutputModes(); + + for (DeckLinkDeviceMode *mode : modes) { + obs_property_list_add_int(modeList, + mode->GetName().c_str(), + mode->GetId()); + } + + obs_property_list_add_int(keyerList, "Disabled", 0); + + if (device->GetSupportsExternalKeyer()) { + obs_property_list_add_int(keyerList, "External", 1); + } + + if (device->GetSupportsInternalKeyer()) { + obs_property_list_add_int(keyerList, "Internal", 2); + } + } + + return true; +} + +static obs_properties_t *decklink_output_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + obs_properties_t *props = obs_properties_create(); + + obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, + TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_set_modified_callback(list, decklink_output_device_changed); + + fill_out_devices(list); + + obs_properties_add_list(props, + MODE_ID, TEXT_MODE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_properties_add_bool(props, AUTO_START, TEXT_AUTO_START); + + obs_properties_add_list(props, KEYER, TEXT_ENABLE_KEYER, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + return props; +} + +static const char *decklink_output_get_name(void*) +{ + return obs_module_text("BlackmagicDevice"); +} + +struct obs_output_info create_decklink_output_info() +{ + struct obs_output_info decklink_output_info = {}; + + decklink_output_info.id = "decklink_output"; + decklink_output_info.flags = OBS_OUTPUT_AV; + decklink_output_info.get_name = decklink_output_get_name; + decklink_output_info.create = decklink_output_create; + decklink_output_info.destroy = decklink_output_destroy; + decklink_output_info.start = decklink_output_start; + decklink_output_info.stop = decklink_output_stop; + decklink_output_info.get_properties = decklink_output_properties; + decklink_output_info.raw_video = decklink_output_raw_video; + decklink_output_info.raw_audio = decklink_output_raw_audio; + decklink_output_info.update = decklink_output_update; + + return decklink_output_info; +} diff --git a/plugins/decklink/decklink-source.cpp b/plugins/decklink/decklink-source.cpp new file mode 100644 index 0000000..f781898 --- /dev/null +++ b/plugins/decklink/decklink-source.cpp @@ -0,0 +1,318 @@ +#include + +#include "const.h" +#include "util.hpp" + +#include "DecklinkInput.hpp" +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" +#include "decklink-devices.hpp" + +static void decklink_enable_buffering(DeckLinkInput *decklink, bool enabled) +{ + obs_source_t *source = decklink->GetSource(); + obs_source_set_async_unbuffered(source, !enabled); + decklink->buffering = enabled; +} + +static void decklink_deactivate_when_not_showing(DeckLinkInput *decklink, bool dwns) +{ + decklink->dwns = dwns; +} + +static void *decklink_create(obs_data_t *settings, obs_source_t *source) +{ + DeckLinkInput *decklink = new DeckLinkInput(source, deviceEnum); + + obs_source_set_async_decoupled(source, true); + decklink_enable_buffering(decklink, + obs_data_get_bool(settings, BUFFERING)); + + obs_source_update(source, settings); + return decklink; +} + +static void decklink_destroy(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + delete decklink; +} + +static void decklink_update(void *data, obs_data_t *settings) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + long long id = obs_data_get_int(settings, MODE_ID); + BMDVideoConnection videoConnection = (BMDVideoConnection) obs_data_get_int(settings, + VIDEO_CONNECTION); + BMDAudioConnection audioConnection = (BMDAudioConnection) obs_data_get_int(settings, + AUDIO_CONNECTION); + BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, + PIXEL_FORMAT); + video_colorspace colorSpace = (video_colorspace)obs_data_get_int(settings, + COLOR_SPACE); + video_range_type colorRange = (video_range_type)obs_data_get_int(settings, + COLOR_RANGE); + int chFmtInt = (int)obs_data_get_int(settings, CHANNEL_FORMAT); + + if (chFmtInt == 7) + chFmtInt = SPEAKERS_5POINT1; + else if (chFmtInt < SPEAKERS_UNKNOWN || chFmtInt > SPEAKERS_7POINT1) + chFmtInt = 2; + + speaker_layout channelFormat = (speaker_layout)chFmtInt; + + decklink_enable_buffering(decklink, + obs_data_get_bool(settings, BUFFERING)); + + decklink_deactivate_when_not_showing(decklink, + obs_data_get_bool(settings, DEACTIVATE_WNS)); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + decklink->SetPixelFormat(pixelFormat); + decklink->SetColorSpace(colorSpace); + decklink->SetColorRange(colorRange); + decklink->SetChannelFormat(channelFormat); + decklink->hash = std::string(hash); + decklink->swap = obs_data_get_bool(settings, SWAP); + decklink->Activate(device, id, videoConnection, audioConnection); +} + +static void decklink_show(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + obs_source_t *source = decklink->GetSource(); + bool showing = obs_source_showing(source); + if (decklink->dwns && showing && !decklink->Capturing()) { + ComPtr device; + device.Set(deviceEnum->FindByHash(decklink->hash.c_str())); + decklink->Activate(device, decklink->id, decklink->videoConnection, + decklink->audioConnection); + } +} +static void decklink_hide(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + obs_source_t *source = decklink->GetSource(); + bool showing = obs_source_showing(source); + if (decklink->dwns && showing) + decklink->Deactivate(); +} + +static void decklink_get_defaults(obs_data_t *settings) +{ + obs_data_set_default_bool(settings, BUFFERING, false); + obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV); + obs_data_set_default_int(settings, COLOR_SPACE, VIDEO_CS_DEFAULT); + obs_data_set_default_int(settings, COLOR_RANGE, VIDEO_RANGE_DEFAULT); + obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO); + obs_data_set_default_bool(settings, SWAP, false); +} + +static const char *decklink_get_name(void*) +{ + return obs_module_text("BlackmagicDevice"); +} + +static bool decklink_device_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + const char *name = obs_data_get_string(settings, DEVICE_NAME); + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + const char *mode = obs_data_get_string(settings, MODE_NAME); + long long modeId = obs_data_get_int(settings, MODE_ID); + + size_t itemCount = obs_property_list_item_count(list); + bool itemFound = false; + + for (size_t i = 0; i < itemCount; i++) { + const char *curHash = obs_property_list_item_string(list, i); + if (strcmp(hash, curHash) == 0) { + itemFound = true; + break; + } + } + + if (!itemFound) { + obs_property_list_insert_string(list, 0, name, hash); + obs_property_list_item_disable(list, 0, true); + } + + obs_property_t *videoConnectionList = obs_properties_get(props, + VIDEO_CONNECTION); + obs_property_t *audioConnectionList = obs_properties_get(props, + AUDIO_CONNECTION); + obs_property_t *modeList = obs_properties_get(props, MODE_ID); + obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); + + obs_property_list_clear(videoConnectionList); + obs_property_list_clear(audioConnectionList); + + 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_item_disable(videoConnectionList, 0, true); + obs_property_list_item_disable(audioConnectionList, 0, true); + obs_property_list_add_int(modeList, mode, modeId); + obs_property_list_item_disable(modeList, 0, true); + } else { + const BMDVideoConnection BMDVideoConnections[] = { + bmdVideoConnectionSDI, bmdVideoConnectionHDMI, + bmdVideoConnectionOpticalSDI, bmdVideoConnectionComponent, + bmdVideoConnectionComposite, bmdVideoConnectionSVideo + }; + + for (BMDVideoConnection conn : BMDVideoConnections) { + if ((device->GetVideoInputConnections() & conn) == conn) { + obs_property_list_add_int(videoConnectionList, + bmd_video_connection_to_name(conn), conn); + } + } + + const BMDAudioConnection BMDAudioConnections[] = { + bmdAudioConnectionEmbedded, bmdAudioConnectionAESEBU, + bmdAudioConnectionAnalog, bmdAudioConnectionAnalogXLR, + bmdAudioConnectionAnalogRCA, bmdAudioConnectionMicrophone, + bmdAudioConnectionHeadphones + }; + + for (BMDAudioConnection conn : BMDAudioConnections) { + if ((device->GetAudioInputConnections() & conn) == conn) { + obs_property_list_add_int(audioConnectionList, + bmd_audio_connection_to_name(conn), conn); + } + } + + const std::vector &modes = + device->GetInputModes(); + + for (DeckLinkDeviceMode *mode : modes) { + obs_property_list_add_int(modeList, + mode->GetName().c_str(), + mode->GetId()); + } + + if (device->GetMaxChannel() >= 8) { + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_2_1CH, SPEAKERS_2POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_4_0CH, SPEAKERS_4POINT0); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_4_1CH, SPEAKERS_4POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_5_1CH, SPEAKERS_5POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_7_1CH, SPEAKERS_7POINT1); + } + } + + return true; +} + +static bool mode_id_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + long long id = obs_data_get_int(settings, MODE_ID); + + list = obs_properties_get(props, PIXEL_FORMAT); + obs_property_set_visible(list, id != MODE_ID_AUTO); + + return true; +} + +static obs_properties_t *decklink_get_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, + TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_set_modified_callback(list, decklink_device_changed); + + fill_out_devices(list); + + obs_properties_add_list(props, VIDEO_CONNECTION, TEXT_VIDEO_CONNECTION, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_properties_add_list(props, AUDIO_CONNECTION, TEXT_AUDIO_CONNECTION, + 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); + obs_property_set_modified_callback(list, mode_id_changed); + + list = obs_properties_add_list(props, PIXEL_FORMAT, + TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); + obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); + + list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT, VIDEO_CS_DEFAULT); + obs_property_list_add_int(list, "BT.601", VIDEO_CS_601); + obs_property_list_add_int(list, "BT.709", VIDEO_CS_709); + + list = obs_properties_add_list(props, COLOR_RANGE, TEXT_COLOR_RANGE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT, VIDEO_RANGE_DEFAULT); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL, VIDEO_RANGE_PARTIAL); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL, VIDEO_RANGE_FULL); + + list = obs_properties_add_list(props, CHANNEL_FORMAT, + TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE, + SPEAKERS_UNKNOWN); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH, + SPEAKERS_STEREO); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_1CH, + SPEAKERS_2POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_0CH, + SPEAKERS_4POINT0); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_1CH, + SPEAKERS_4POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_5_1CH, + SPEAKERS_5POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH, + SPEAKERS_7POINT1); + + obs_property_t *swap = obs_properties_add_bool(props, SWAP, TEXT_SWAP); + obs_property_set_long_description(swap, TEXT_SWAP_TOOLTIP); + + obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); + + obs_properties_add_bool(props, DEACTIVATE_WNS, TEXT_DWNS); + + UNUSED_PARAMETER(data); + return props; +} + + +struct obs_source_info create_decklink_source_info() +{ + struct obs_source_info decklink_source_info = {}; + decklink_source_info.id = "decklink-input"; + decklink_source_info.type = OBS_SOURCE_TYPE_INPUT; + decklink_source_info.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE; + decklink_source_info.create = decklink_create; + decklink_source_info.destroy = decklink_destroy; + decklink_source_info.get_defaults = decklink_get_defaults; + decklink_source_info.get_name = decklink_get_name; + decklink_source_info.get_properties = decklink_get_properties; + decklink_source_info.update = decklink_update; + decklink_source_info.show = decklink_show; + decklink_source_info.hide = decklink_hide; + + return decklink_source_info; +} diff --git a/plugins/decklink/linux/CMakeLists.txt b/plugins/decklink/linux/CMakeLists.txt index 1ed5ae4..4f006ec 100644 --- a/plugins/decklink/linux/CMakeLists.txt +++ b/plugins/decklink/linux/CMakeLists.txt @@ -21,33 +21,47 @@ set(linux-decklink-sdk_SOURCES ) set(linux-decklink_HEADERS + ../decklink-devices.hpp + ../const.h + ../DecklinkOutput.hpp ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp ../decklink-device-mode.hpp ../audio-repack.h ../audio-repack.hpp + ../util.hpp ) set(linux-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../decklink-source.cpp + ../decklink-output.cpp + ../DecklinkOutput.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ../util.cpp + ) add_library(linux-decklink MODULE ${linux-decklink_SOURCES} ${linux-decklink_HEADERS} ${linux-decklink-sdk_HEADERS} - ${linux-decklink-sdk_SOURCES}) + ${linux-decklink-sdk_SOURCES} + ) target_link_libraries(linux-decklink - libobs) + libobs + ) install_obs_plugin_with_data(linux-decklink ../data) diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h index 2eed7b7..e113fc3 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + /* DeckLink API */ #include @@ -64,12 +68,16 @@ BMD_CONST REFIID IID_IDeckLinkIterator = /* 50FB36CD- BMD_CONST REFIID IID_IDeckLinkAPIInformation = /* 7BEA3C68-730D-4322-AF34-8A7152B532A4 */ {0x7B,0xEA,0x3C,0x68,0x73,0x0D,0x43,0x22,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4}; BMD_CONST REFIID IID_IDeckLinkOutput = /* CC5C8A6E-3F2F-4B3A-87EA-FD78AF300564 */ {0xCC,0x5C,0x8A,0x6E,0x3F,0x2F,0x4B,0x3A,0x87,0xEA,0xFD,0x78,0xAF,0x30,0x05,0x64}; BMD_CONST REFIID IID_IDeckLinkInput = /* AF22762B-DFAC-4846-AA79-FA8883560995 */ {0xAF,0x22,0x76,0x2B,0xDF,0xAC,0x48,0x46,0xAA,0x79,0xFA,0x88,0x83,0x56,0x09,0x95}; +BMD_CONST REFIID IID_IDeckLinkHDMIInputEDID = /* ABBBACBC-45BC-4665-9D92-ACE6E5A97902 */ {0xAB,0xBB,0xAC,0xBC,0x45,0xBC,0x46,0x65,0x9D,0x92,0xAC,0xE6,0xE5,0xA9,0x79,0x02}; BMD_CONST REFIID IID_IDeckLinkEncoderInput = /* 270587DA-6B7D-42E7-A1F0-6D853F581185 */ {0x27,0x05,0x87,0xDA,0x6B,0x7D,0x42,0xE7,0xA1,0xF0,0x6D,0x85,0x3F,0x58,0x11,0x85}; BMD_CONST REFIID IID_IDeckLinkVideoFrame = /* 3F716FE0-F023-4111-BE5D-EF4414C05B17 */ {0x3F,0x71,0x6F,0xE0,0xF0,0x23,0x41,0x11,0xBE,0x5D,0xEF,0x44,0x14,0xC0,0x5B,0x17}; BMD_CONST REFIID IID_IDeckLinkMutableVideoFrame = /* 69E2639F-40DA-4E19-B6F2-20ACE815C390 */ {0x69,0xE2,0x63,0x9F,0x40,0xDA,0x4E,0x19,0xB6,0xF2,0x20,0xAC,0xE8,0x15,0xC3,0x90}; BMD_CONST REFIID IID_IDeckLinkVideoFrame3DExtensions = /* DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7 */ {0xDA,0x0F,0x7E,0x4A,0xED,0xC7,0x48,0xA8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7}; BMD_CONST REFIID IID_IDeckLinkVideoFrameMetadataExtensions = /* D5973DC9-6432-46D0-8F0B-2496F8A1238F */ {0xD5,0x97,0x3D,0xC9,0x64,0x32,0x46,0xD0,0x8F,0x0B,0x24,0x96,0xF8,0xA1,0x23,0x8F}; BMD_CONST REFIID IID_IDeckLinkVideoInputFrame = /* 05CFE374-537C-4094-9A57-680525118F44 */ {0x05,0xCF,0xE3,0x74,0x53,0x7C,0x40,0x94,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacket = /* CC5BBF7E-029C-4D3B-9158-6000EF5E3670 */ {0xCC,0x5B,0xBF,0x7E,0x02,0x9C,0x4D,0x3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacketIterator = /* 3FC8994B-88FB-4C17-968F-9AAB69D964A7 */ {0x3F,0xC8,0x99,0x4B,0x88,0xFB,0x4C,0x17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7}; +BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillaryPackets = /* 6C186C0F-459E-41D8-AEE2-4812D81AEE68 */ {0x6C,0x18,0x6C,0x0F,0x45,0x9E,0x41,0xD8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68}; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillary = /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ {0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04}; BMD_CONST REFIID IID_IDeckLinkEncoderPacket = /* B693F36C-316E-4AF1-B6C2-F389A4BCA620 */ {0xB6,0x93,0xF3,0x6C,0x31,0x6E,0x4A,0xF1,0xB6,0xC2,0xF3,0x89,0xA4,0xBC,0xA6,0x20}; BMD_CONST REFIID IID_IDeckLinkEncoderVideoPacket = /* 4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0 */ {0x4E,0x7F,0xD9,0x44,0xE8,0xC7,0x4E,0xAC,0xB8,0xC0,0x7B,0x77,0xF8,0x0F,0x5A,0xE0}; @@ -113,9 +121,11 @@ enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameContainsHDRMetadata = 1 << 1, + bmdFrameContainsCintelMetadata = 1 << 2, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ + bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; @@ -159,10 +169,10 @@ enum _BMDDeckLinkCapturePassthroughMode { typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { - bmdOutputFrameCompleted, - bmdOutputFrameDisplayedLate, - bmdOutputFrameDropped, - bmdOutputFrameFlushed + bmdOutputFrameCompleted, + bmdOutputFrameDisplayedLate, + bmdOutputFrameDropped, + bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ @@ -199,9 +209,9 @@ enum _BMDAudioSampleType { typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { - bmdAudioOutputStreamContinuous, - bmdAudioOutputStreamContinuousDontResample, - bmdAudioOutputStreamTimestamped + bmdAudioOutputStreamContinuous, + bmdAudioOutputStreamContinuousDontResample, + bmdAudioOutputStreamTimestamped }; /* Enum BMDDisplayModeSupport - Output mode supported flags */ @@ -209,8 +219,17 @@ enum _BMDAudioOutputStreamType { typedef uint32_t BMDDisplayModeSupport; enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, - bmdDisplayModeSupported, - bmdDisplayModeSupportedWithConversion + bmdDisplayModeSupported, + bmdDisplayModeSupportedWithConversion +}; + +/* Enum BMDAncillaryPacketFormat - Ancillary packet format */ + +typedef uint32_t BMDAncillaryPacketFormat; +enum _BMDAncillaryPacketFormat { + bmdAncillaryPacketFormatUInt8 = /* 'ui08' */ 0x75693038, + bmdAncillaryPacketFormatUInt16 = /* 'ui16' */ 0x75693136, + bmdAncillaryPacketFormatYCbCr10 = /* 'v210' */ 0x76323130 }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ @@ -332,11 +351,68 @@ enum _BMDDeviceInterface { bmdDeviceInterfaceThunderbolt = /* 'thun' */ 0x7468756E }; +/* Enum BMDColorspace - Colorspace */ + +typedef uint32_t BMDColorspace; +enum _BMDColorspace { + bmdColorspaceRec601 = /* 'r601' */ 0x72363031, + bmdColorspaceRec709 = /* 'r709' */ 0x72373039, + bmdColorspaceRec2020 = /* '2020' */ 0x32303230 +}; + +/* Enum BMDDynamicRange - SDR or HDR */ + +typedef uint32_t BMDDynamicRange; +enum _BMDDynamicRange { + bmdDynamicRangeSDR = 0, + bmdDynamicRangeHDRStaticPQ = 1 << 29, // SMPTE ST 2084 + bmdDynamicRangeHDRStaticHLG = 1 << 30 // ITU-R BT.2100-0 +}; + +/* Enum BMDDeckLinkHDMIInputEDIDID - DeckLink HDMI Input EDID ID */ + +typedef uint32_t BMDDeckLinkHDMIInputEDIDID; +enum _BMDDeckLinkHDMIInputEDIDID { + bmdDeckLinkHDMIInputEDIDDynamicRange = /* 'HIDy' */ 0x48494479 // Parameter is of type BMDDynamicRange. Default is (bmdDynamicRangeSDR|bmdDynamicRangeHDRStaticPQ) +}; + /* Enum BMDDeckLinkFrameMetadataID - DeckLink Frame Metadata ID */ typedef uint32_t BMDDeckLinkFrameMetadataID; enum _BMDDeckLinkFrameMetadataID { + bmdDeckLinkFrameMetadataColorspace = /* 'cspc' */ 0x63737063, // Colorspace of video frame (see BMDColorspace) bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = /* 'eotf' */ 0x656F7466, // EOTF in range 0-7 as per CEA 861.3 + bmdDeckLinkFrameMetadataCintelFilmType = /* 'cfty' */ 0x63667479, // Current film type + bmdDeckLinkFrameMetadataCintelFilmGauge = /* 'cfga' */ 0x63666761, // Current film gauge + bmdDeckLinkFrameMetadataCintelOffsetDetectedHorizontal = /* 'odfh' */ 0x6F646668, // Horizontal offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelOffsetDetectedVertical = /* 'odfv' */ 0x6F646676, // Vertical offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelKeykodeLow = /* 'ckkl' */ 0x636B6B6C, // Raw keykode value - low 64 bits + bmdDeckLinkFrameMetadataCintelKeykodeHigh = /* 'ckkh' */ 0x636B6B68, // Raw keykode value - high 64 bits + bmdDeckLinkFrameMetadataCintelTile1Size = /* 'ct1s' */ 0x63743173, // Size in bytes of compressed raw tile 1 + bmdDeckLinkFrameMetadataCintelTile2Size = /* 'ct2s' */ 0x63743273, // Size in bytes of compressed raw tile 2 + bmdDeckLinkFrameMetadataCintelTile3Size = /* 'ct3s' */ 0x63743373, // Size in bytes of compressed raw tile 3 + bmdDeckLinkFrameMetadataCintelTile4Size = /* 'ct4s' */ 0x63743473, // Size in bytes of compressed raw tile 4 + bmdDeckLinkFrameMetadataCintelImageWidth = /* 'IWPx' */ 0x49575078, // Width in pixels of image + bmdDeckLinkFrameMetadataCintelImageHeight = /* 'IHPx' */ 0x49485078, // Height in pixels of image + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed = /* 'mrir' */ 0x6D726972, // Red in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed = /* 'mgir' */ 0x6D676972, // Green in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed = /* 'mbir' */ 0x6D626972, // Blue in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen = /* 'mrig' */ 0x6D726967, // Red in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen = /* 'mgig' */ 0x6D676967, // Green in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen = /* 'mbig' */ 0x6D626967, // Blue in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue = /* 'mrib' */ 0x6D726962, // Red in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue = /* 'mgib' */ 0x6D676962, // Green in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue = /* 'mbib' */ 0x6D626962, // Blue in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed = /* 'mlrr' */ 0x6D6C7272, // Red in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed = /* 'mlgr' */ 0x6D6C6772, // Green in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed = /* 'mlbr' */ 0x6D6C6272, // Blue in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen = /* 'mlrg' */ 0x6D6C7267, // Red in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen = /* 'mlgg' */ 0x6D6C6767, // Green in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen = /* 'mlbg' */ 0x6D6C6267, // Blue in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue = /* 'mlrb' */ 0x6D6C7262, // Red in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue = /* 'mlgb' */ 0x6D6C6762, // Green in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue = /* 'mlbb' */ 0x6D6C6262, // Blue in blue log masking parameter + bmdDeckLinkFrameMetadataCintelFilmFrameRate = /* 'cffr' */ 0x63666672, // Film frame rate bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = /* 'hdrx' */ 0x68647278, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = /* 'hdry' */ 0x68647279, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = /* 'hdgx' */ 0x68646778, // Green display primaries in range 0.0 - 1.0 @@ -348,7 +424,15 @@ enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = /* 'hdml' */ 0x68646D6C, // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = /* 'hmil' */ 0x686D696C, // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = /* 'mcll' */ 0x6D636C6C, // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 - bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C, // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal = /* 'otah' */ 0x6F746168, // Horizontal offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical = /* 'otav' */ 0x6F746176, // Vertical offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelGainRed = /* 'LfRd' */ 0x4C665264, // Red gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainGreen = /* 'LfGr' */ 0x4C664772, // Green gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainBlue = /* 'LfBl' */ 0x4C66426C, // Blue gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelLiftRed = /* 'GnRd' */ 0x476E5264, // Red lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftGreen = /* 'GnGr' */ 0x476E4772, // Green lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftBlue = /* 'GnBl' */ 0x476E426C // Blue lift parameter to apply after log and gain }; /* Enum BMDDuplexMode - Duplex for configurable ports */ @@ -386,22 +470,24 @@ enum _BMDDeckLinkAttributeID { BMDDeckLinkHasLTCTimecodeInput = /* 'hltc' */ 0x686C7463, BMDDeckLinkSupportsDuplexModeConfiguration = /* 'dupx' */ 0x64757078, BMDDeckLinkSupportsHDRMetadata = /* 'hdrm' */ 0x6864726D, + BMDDeckLinkSupportsColorspaceMetadata = /* 'cmet' */ 0x636D6574, /* Integers */ BMDDeckLinkMaximumAudioChannels = /* 'mach' */ 0x6D616368, - BMDDeckLinkMaximumAnalogAudioChannels = /* 'aach' */ 0x61616368, + BMDDeckLinkMaximumAnalogAudioInputChannels = /* 'iach' */ 0x69616368, + BMDDeckLinkMaximumAnalogAudioOutputChannels = /* 'aach' */ 0x61616368, BMDDeckLinkNumberOfSubDevices = /* 'nsbd' */ 0x6E736264, BMDDeckLinkSubDeviceIndex = /* 'subi' */ 0x73756269, BMDDeckLinkPersistentID = /* 'peid' */ 0x70656964, BMDDeckLinkDeviceGroupID = /* 'dgid' */ 0x64676964, BMDDeckLinkTopologicalID = /* 'toid' */ 0x746F6964, - BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, - BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, - BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, - BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, + BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, // Returns a BMDAudioConnection bit field + BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = /* 'vios' */ 0x76696F73, // Returns a BMDVideoIOSupport bit field - BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, + BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = /* 'dbus' */ 0x64627573, // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = /* 'airc' */ 0x61697263, BMDDeckLinkAudioInputXLRChannelCount = /* 'aixc' */ 0x61697863, @@ -455,11 +541,14 @@ enum _BMDDeckLinkStatusID { bmdDeckLinkStatusReferenceSignalFlags = /* 'reff' */ 0x72656666, bmdDeckLinkStatusDuplexMode = /* 'dupx' */ 0x64757078, bmdDeckLinkStatusBusy = /* 'busy' */ 0x62757379, + bmdDeckLinkStatusInterchangeablePanelType = /* 'icpt' */ 0x69637074, + bmdDeckLinkStatusDeviceTemperature = /* 'dtmp' */ 0x64746D70, /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = /* 'visl' */ 0x7669736C, - bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C + bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C, + bmdDeckLinkStatusReceivedEDID = /* 'edid' */ 0x65646964 }; /* Enum BMDDeckLinkVideoStatusFlags - */ @@ -480,6 +569,14 @@ enum _BMDDuplexStatus { bmdDuplexStatusInactive = /* 'inac' */ 0x696E6163 }; +/* Enum BMDPanelType - The type of interchangeable panel */ + +typedef uint32_t BMDPanelType; +enum _BMDPanelType { + bmdPanelNotDetected = /* 'npnl' */ 0x6E706E6C, + bmdPanelTeranexMiniSmartPanel = /* 'tmsm' */ 0x746D736D +}; + /* Enum BMDDeviceBusyState - Current device busy state */ typedef uint32_t BMDDeviceBusyState; @@ -529,12 +626,16 @@ class IDeckLinkIterator; class IDeckLinkAPIInformation; class IDeckLinkOutput; class IDeckLinkInput; +class IDeckLinkHDMIInputEDID; class IDeckLinkEncoderInput; class IDeckLinkVideoFrame; class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoFrameMetadataExtensions; class IDeckLinkVideoInputFrame; +class IDeckLinkAncillaryPacket; +class IDeckLinkAncillaryPacketIterator; +class IDeckLinkVideoFrameAncillaryPackets; class IDeckLinkVideoFrameAncillary; class IDeckLinkEncoderPacket; class IDeckLinkEncoderVideoPacket; @@ -554,7 +655,7 @@ class IDeckLinkDiscovery; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ -class IDeckLinkVideoOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; @@ -566,7 +667,7 @@ protected: /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -578,7 +679,7 @@ protected: /* Interface IDeckLinkEncoderInputCallback - Frame arrival callback. */ -class IDeckLinkEncoderInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInputCallback : public IUnknown { public: virtual HRESULT VideoInputSignalChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -591,7 +692,7 @@ protected: /* Interface IDeckLinkMemoryAllocator - Memory allocator for video frames. */ -class IDeckLinkMemoryAllocator : public IUnknown +class BMD_PUBLIC IDeckLinkMemoryAllocator : public IUnknown { public: virtual HRESULT AllocateBuffer (/* in */ uint32_t bufferSize, /* out */ void **allocatedBuffer) = 0; @@ -603,7 +704,7 @@ public: /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ -class IDeckLinkAudioOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; @@ -611,7 +712,7 @@ public: /* Interface IDeckLinkIterator - enumerates installed DeckLink hardware */ -class IDeckLinkIterator : public IUnknown +class BMD_PUBLIC IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink **deckLinkInstance) = 0; @@ -619,7 +720,7 @@ public: /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ -class IDeckLinkAPIInformation : public IUnknown +class BMD_PUBLIC IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool *value) = 0; @@ -633,7 +734,7 @@ protected: /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput : public IUnknown +class BMD_PUBLIC IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -648,7 +749,7 @@ public: virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; - virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; + virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -690,7 +791,7 @@ protected: /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput : public IUnknown +class BMD_PUBLIC IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -727,9 +828,22 @@ protected: virtual ~IDeckLinkInput () {} // call Release method to drop reference count }; +/* Interface IDeckLinkHDMIInputEDID - Created by QueryInterface from IDeckLink. Releasing all references will restore EDID to default */ + +class BMD_PUBLIC IDeckLinkHDMIInputEDID : public IUnknown +{ +public: + virtual HRESULT SetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* in */ int64_t value) = 0; + virtual HRESULT GetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* out */ int64_t *value) = 0; + virtual HRESULT WriteToEDID (void) = 0; + +protected: + virtual ~IDeckLinkHDMIInputEDID () {} // call Release method to drop reference count +}; + /* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkEncoderInput : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -766,7 +880,7 @@ protected: /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ -class IDeckLinkVideoFrame : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; @@ -777,7 +891,7 @@ public: virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) = 0; - virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; + virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred protected: virtual ~IDeckLinkVideoFrame () {} // call Release method to drop reference count @@ -785,7 +899,7 @@ protected: /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ -class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; @@ -801,7 +915,7 @@ protected: /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface implemented on IDeckLinkVideoFrame to support 3D frames */ -class IDeckLinkVideoFrame3DExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; @@ -813,7 +927,7 @@ protected: /* Interface IDeckLinkVideoFrameMetadataExtensions - Optional interface implemented on IDeckLinkVideoFrame to support frame metadata such as HDMI HDR information */ -class IDeckLinkVideoFrameMetadataExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrameMetadataExtensions : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ int64_t *value) = 0; @@ -827,7 +941,7 @@ protected: /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -837,13 +951,56 @@ protected: virtual ~IDeckLinkVideoInputFrame () {} // call Release method to drop reference count }; -/* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ +/* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ -class IDeckLinkVideoFrameAncillary : public IUnknown +class BMD_PUBLIC IDeckLinkAncillaryPacket : public IUnknown { public: - virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; + virtual HRESULT GetBytes (/* in */ BMDAncillaryPacketFormat format /* For output, only one format need be offered */, /* out */ const void **data /* Optional */, /* out */ uint32_t *size /* Optional */) = 0; + virtual uint8_t GetDID (void) = 0; + virtual uint8_t GetSDID (void) = 0; + virtual uint32_t GetLineNumber (void) = 0; // On output, zero is auto + virtual uint8_t GetDataStreamIndex (void) = 0; // Usually zero. Can only be 1 if non-SD and the first data stream is completely full + +protected: + virtual ~IDeckLinkAncillaryPacket () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ + +class BMD_PUBLIC IDeckLinkAncillaryPacketIterator : public IUnknown +{ +public: + virtual HRESULT Next (/* out */ IDeckLinkAncillaryPacket **packet) = 0; + +protected: + virtual ~IDeckLinkAncillaryPacketIterator () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +class BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets : public IUnknown +{ +public: + + virtual HRESULT GetPacketIterator (/* out */ IDeckLinkAncillaryPacketIterator **iterator) = 0; + virtual HRESULT GetFirstPacketByID (/* in */ uint8_t DID, /* in */ uint8_t SDID, /* out */ IDeckLinkAncillaryPacket **packet) = 0; + virtual HRESULT AttachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; // Implement IDeckLinkAncillaryPacket to output your own + virtual HRESULT DetachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; + virtual HRESULT DetachAllPackets (void) = 0; + +protected: + virtual ~IDeckLinkVideoFrameAncillaryPackets () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +class BMD_PUBLIC IDeckLinkVideoFrameAncillary : public IUnknown +{ +public: + + virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; @@ -853,7 +1010,7 @@ protected: /* Interface IDeckLinkEncoderPacket - Interface to encapsulate an encoded packet. */ -class IDeckLinkEncoderPacket : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderPacket : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void **buffer) = 0; @@ -867,7 +1024,7 @@ protected: /* Interface IDeckLinkEncoderVideoPacket - Provided by the IDeckLinkEncoderInput video packet arrival callback. */ -class IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket { public: virtual BMDPixelFormat GetPixelFormat (void) = 0; @@ -881,7 +1038,7 @@ protected: /* Interface IDeckLinkEncoderAudioPacket - Provided by the IDeckLinkEncoderInput audio packet arrival callback. */ -class IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket { public: virtual BMDAudioFormat GetAudioFormat (void) = 0; @@ -892,7 +1049,7 @@ protected: /* Interface IDeckLinkH265NALPacket - Obtained through QueryInterface() on an IDeckLinkEncoderVideoPacket object */ -class IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket +class BMD_PUBLIC IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket { public: virtual HRESULT GetUnitType (/* out */ uint8_t *unitType) = 0; @@ -905,7 +1062,7 @@ protected: /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ -class IDeckLinkAudioInputPacket : public IUnknown +class BMD_PUBLIC IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; @@ -918,7 +1075,7 @@ protected: /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ -class IDeckLinkScreenPreviewCallback : public IUnknown +class BMD_PUBLIC IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; @@ -929,7 +1086,7 @@ protected: /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ -class IDeckLinkGLScreenPreviewHelper : public IUnknown +class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper : public IUnknown { public: @@ -946,7 +1103,7 @@ protected: /* Interface IDeckLinkNotificationCallback - DeckLink Notification Callback Interface */ -class IDeckLinkNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkNotificationCallback : public IUnknown { public: virtual HRESULT Notify (/* in */ BMDNotifications topic, /* in */ uint64_t param1, /* in */ uint64_t param2) = 0; @@ -954,7 +1111,7 @@ public: /* Interface IDeckLinkNotification - DeckLink Notification interface */ -class IDeckLinkNotification : public IUnknown +class BMD_PUBLIC IDeckLinkNotification : public IUnknown { public: virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0; @@ -963,7 +1120,7 @@ public: /* Interface IDeckLinkAttributes - DeckLink Attribute interface */ -class IDeckLinkAttributes : public IUnknown +class BMD_PUBLIC IDeckLinkAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool *value) = 0; @@ -977,7 +1134,7 @@ protected: /* Interface IDeckLinkStatus - DeckLink Status interface */ -class IDeckLinkStatus : public IUnknown +class BMD_PUBLIC IDeckLinkStatus : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkStatusID statusID, /* out */ bool *value) = 0; @@ -992,7 +1149,7 @@ protected: /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ -class IDeckLinkKeyer : public IUnknown +class BMD_PUBLIC IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; @@ -1007,7 +1164,7 @@ protected: /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ -class IDeckLinkVideoConversion : public IUnknown +class BMD_PUBLIC IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; @@ -1018,7 +1175,7 @@ protected: /* Interface IDeckLinkDeviceNotificationCallback - DeckLink device arrival/removal notification callbacks */ -class IDeckLinkDeviceNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeviceNotificationCallback : public IUnknown { public: virtual HRESULT DeckLinkDeviceArrived (/* in */ IDeckLink* deckLinkDevice) = 0; @@ -1030,7 +1187,7 @@ protected: /* Interface IDeckLinkDiscovery - DeckLink device discovery */ -class IDeckLinkDiscovery : public IUnknown +class BMD_PUBLIC IDeckLinkDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IDeckLinkDeviceNotificationCallback* deviceNotificationCallback) = 0; @@ -1044,11 +1201,12 @@ protected: extern "C" { - IDeckLinkIterator* CreateDeckLinkIteratorInstance (void); - IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void); - IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void); - IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void); - IDeckLinkVideoConversion* CreateVideoConversionInstance (void); + IDeckLinkIterator* BMD_PUBLIC CreateDeckLinkIteratorInstance (void); + IDeckLinkDiscovery* BMD_PUBLIC CreateDeckLinkDiscoveryInstance (void); + IDeckLinkAPIInformation* BMD_PUBLIC CreateDeckLinkAPIInformationInstance (void); + IDeckLinkGLScreenPreviewHelper* BMD_PUBLIC CreateOpenGLScreenPreviewHelper (void); + IDeckLinkVideoConversion* BMD_PUBLIC CreateVideoConversionInstance (void); + IDeckLinkVideoFrameAncillaryPackets* BMD_PUBLIC CreateVideoFrameAncillaryPacketsInstance (void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame } diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h index dedf15e..430f871 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,12 +37,16 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations // Interface ID Declarations -BMD_CONST REFIID IID_IDeckLinkConfiguration = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; +BMD_CONST REFIID IID_IDeckLinkConfiguration = /* EF90380B-4AE5-4346-9077-E288E149F129 */ {0xEF,0x90,0x38,0x0B,0x4A,0xE5,0x43,0x46,0x90,0x77,0xE2,0x88,0xE1,0x49,0xF1,0x29}; BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration = /* 138050E5-C60A-4552-BF3F-0F358049327E */ {0x13,0x80,0x50,0xE5,0xC6,0x0A,0x45,0x52,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E}; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ @@ -54,10 +58,6 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigSwapSerialRxTx = /* 'ssrt' */ 0x73737274, - /* Video Input/Output Flags */ - - bmdDeckLinkConfigUse1080pNotPsF = /* 'fpro' */ 0x6670726F, - /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066, @@ -78,6 +78,12 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F, bmdDeckLinkConfigDownConversionOnAllAnalogOutput = /* 'caao' */ 0x6361616F, bmdDeckLinkConfigSMPTELevelAOutput = /* 'smta' */ 0x736D7461, + bmdDeckLinkConfigRec2020Output = /* 'rec2' */ 0x72656332, // Ensure output is Rec.2020 colorspace + bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = /* 'SDQS' */ 0x53445153, + + /* Video Output Flags */ + + bmdDeckLinkConfigOutput1080pAsPsF = /* 'pfpr' */ 0x70667072, /* Video Output Integers */ @@ -106,6 +112,10 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigUseDedicatedLTCInput = /* 'dltc' */ 0x646C7463, // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = /* '3dds' */ 0x33646473, + /* Video Input Flags */ + + bmdDeckLinkConfigCapture1080pAsPsF = /* 'cfpr' */ 0x63667072, + /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E, @@ -206,7 +216,7 @@ class IDeckLinkEncoderConfiguration; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ -class IDeckLinkConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; @@ -225,7 +235,7 @@ protected: /* Interface IDeckLinkEncoderConfiguration - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ -class IDeckLinkEncoderConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h index 30bd5ae..457f041 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ class IDeckLinkConfiguration_v10_2; /* Interface IDeckLinkConfiguration_v10_2 - DeckLink Configuration interface */ -class IDeckLinkConfiguration_v10_2 : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration_v10_2 : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h index 750387f..c4d340f 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_4.h @@ -42,7 +42,7 @@ class IDeckLinkConfiguration_v10_4; /* Interface IDeckLinkConfiguration_v10_4 - DeckLink Configuration interface */ -class IDeckLinkConfiguration_v10_4 : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration_v10_4 : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h index da36e50..684b83b 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ class IDeckLinkEncoderConfiguration_v10_5; /* Interface IDeckLinkEncoderConfiguration_v10_5 - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ -class IDeckLinkEncoderConfiguration_v10_5 : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderConfiguration_v10_5 : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h new file mode 100644 index 0000000..f507637 --- /dev/null +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h @@ -0,0 +1,62 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPICONFIGURATION_v10_9_H +#define BMD_DECKLINKAPICONFIGURATION_v10_9_H + +#include "DeckLinkAPIConfiguration.h" + +// Interface ID Declarations + +BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_9 = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; + +// +// Forward Declarations + +class IDeckLinkConfiguration_v10_9; + +/* Interface IDeckLinkConfiguration_v10_9 - DeckLink Configuration interface */ + +class BMD_PUBLIC IDeckLinkConfiguration_v10_9 : public IUnknown +{ +public: + virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; + virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0; + virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; + virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0; + virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; + virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0; + virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char *value) = 0; + virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char **value) = 0; + virtual HRESULT WriteConfigurationToPreferences (void) = 0; + +protected: + virtual ~IDeckLinkConfiguration_v10_9 () {} // call Release method to drop reference count +}; + + +#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_9_H) */ diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h index 8dc069c..e0522a2 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDeckControl.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -149,7 +153,7 @@ class IDeckLinkDeckControl; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ -class IDeckLinkDeckControlStatusCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; @@ -163,7 +167,7 @@ protected: /* Interface IDeckLinkDeckControl - Deck Control main interface */ -class IDeckLinkDeckControl : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h index d9253ff..d6619ca 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDiscovery.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -50,7 +54,7 @@ class IDeckLink; /* Interface IDeckLink - represents a DeckLink device */ -class IDeckLink : public IUnknown +class BMD_PUBLIC IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ const char **modelName) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp index f639286..880cf34 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -39,6 +39,7 @@ typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); +typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT; @@ -50,21 +51,22 @@ static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL; +static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL; -void InitDeckLinkAPI (void) +static void InitDeckLinkAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } - + gLoadedDeckLinkAPI = true; - - gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0002"); + + gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0003"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001"); @@ -73,15 +75,18 @@ void InitDeckLinkAPI (void) gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001"); if (!gCreateVideoConversionFunc) fprintf(stderr, "%s\n", dlerror()); - gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0001"); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0002"); if (!gCreateDeckLinkDiscoveryFunc) fprintf(stderr, "%s\n", dlerror()); + gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0001"); + if (!gCreateVideoFrameAncillaryPacketsFunc) + fprintf(stderr, "%s\n", dlerror()); } -void InitDeckLinkPreviewAPI (void) +static void InitDeckLinkPreviewAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { @@ -102,7 +107,7 @@ bool IsDeckLinkAPIPresent (void) IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); @@ -111,7 +116,7 @@ IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; return gCreateAPIInformationFunc(); @@ -121,7 +126,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); @@ -130,7 +135,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); @@ -139,8 +144,17 @@ IDeckLinkVideoConversion* CreateVideoConversionInstance (void) IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateDeckLinkDiscoveryFunc == NULL) return NULL; return gCreateDeckLinkDiscoveryFunc(); } + +IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance (void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoFrameAncillaryPacketsFunc == NULL) + return NULL; + return gCreateVideoFrameAncillaryPacketsFunc(); +} diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp new file mode 100644 index 0000000..3172ba5 --- /dev/null +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp @@ -0,0 +1,146 @@ +/* -LICENSE-START- +** Copyright (c) 2009 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +**/ + +#include +#include +#include + +#include "DeckLinkAPI.h" + +#define kDeckLinkAPI_Name "libDeckLinkAPI.so" +#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so" + +typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); +typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); +typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); +typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); +typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); + +static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; +static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT; + +static bool gLoadedDeckLinkAPI = false; + +static CreateIteratorFunc gCreateIteratorFunc = NULL; +static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; +static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; +static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; +static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL; + +static void InitDeckLinkAPI(void) +{ + void *libraryHandle; + + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW | RTLD_GLOBAL); + if (!libraryHandle) + { + fprintf(stderr, "%s\n", dlerror()); + return; + } + + gLoadedDeckLinkAPI = true; + + gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0002"); + if (!gCreateIteratorFunc) + fprintf(stderr, "%s\n", dlerror()); + gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001"); + if (!gCreateAPIInformationFunc) + fprintf(stderr, "%s\n", dlerror()); + gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001"); + if (!gCreateVideoConversionFunc) + fprintf(stderr, "%s\n", dlerror()); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0001"); + if (!gCreateDeckLinkDiscoveryFunc) + fprintf(stderr, "%s\n", dlerror()); +} + +static void InitDeckLinkPreviewAPI(void) +{ + void *libraryHandle; + + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW | RTLD_GLOBAL); + if (!libraryHandle) + { + fprintf(stderr, "%s\n", dlerror()); + return; + } + gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0001"); + if (!gCreateOpenGLPreviewFunc) + fprintf(stderr, "%s\n", dlerror()); +} + +bool IsDeckLinkAPIPresent(void) +{ + // If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller + return gLoadedDeckLinkAPI; +} + +IDeckLinkIterator* CreateDeckLinkIteratorInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateIteratorFunc == NULL) + return NULL; + return gCreateIteratorFunc(); +} + +IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateAPIInformationFunc == NULL) + return NULL; + return gCreateAPIInformationFunc(); +} + +IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); + + if (gCreateOpenGLPreviewFunc == NULL) + return NULL; + return gCreateOpenGLPreviewFunc(); +} + +IDeckLinkVideoConversion* CreateVideoConversionInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoConversionFunc == NULL) + return NULL; + return gCreateVideoConversionFunc(); +} + +IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateDeckLinkDiscoveryFunc == NULL) + return NULL; + return gCreateDeckLinkDiscoveryFunc(); +} diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp index 4963060..3726624 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -45,17 +45,17 @@ static CreateIteratorFunc_v7_6 gCreateIteratorFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc_v7_6 gCreateOpenGLPreviewFunc = NULL; static CreateVideoConversionInstanceFunc_v7_6 gCreateVideoConversionFunc = NULL; -void InitDeckLinkAPI_v7_6 (void) +static void InitDeckLinkAPI_v7_6 (void) { void *libraryHandle; - + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } - + gCreateIteratorFunc = (CreateIteratorFunc_v7_6)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); @@ -64,10 +64,10 @@ void InitDeckLinkAPI_v7_6 (void) fprintf(stderr, "%s\n", dlerror()); } -void InitDeckLinkPreviewAPI_v7_6 (void) +static void InitDeckLinkPreviewAPI_v7_6 (void) { void *libraryHandle; - + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { @@ -82,7 +82,7 @@ void InitDeckLinkPreviewAPI_v7_6 (void) IDeckLinkIterator* CreateDeckLinkIteratorInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); @@ -92,7 +92,7 @@ IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI_v7_6); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); @@ -101,7 +101,7 @@ IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void IDeckLinkVideoConversion_v7_6* CreateVideoConversionInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp index 770f143..1a924f7 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -49,19 +49,19 @@ static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; -void InitDeckLinkAPI (void) +static void InitDeckLinkAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } - + gLoadedDeckLinkAPI = true; - + gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0001"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); @@ -73,10 +73,10 @@ void InitDeckLinkAPI (void) fprintf(stderr, "%s\n", dlerror()); } -void InitDeckLinkPreviewAPI (void) +static void InitDeckLinkPreviewAPI (void) { void *libraryHandle; - + libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { @@ -97,7 +97,7 @@ bool IsDeckLinkAPIPresent (void) IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); @@ -106,7 +106,7 @@ IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance (void) IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; return gCreateAPIInformationFunc(); @@ -116,7 +116,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); @@ -125,7 +125,7 @@ IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h index 6c52051..543aaa1 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIModes.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -65,12 +69,12 @@ enum _BMDDisplayMode { bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235, bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239, bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330, - bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, - bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, - bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530, bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539, bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz. + bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, + bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, + bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ @@ -78,19 +82,24 @@ enum _BMDDisplayMode { bmdModeHD720p5994 = /* 'hp59' */ 0x68703539, bmdModeHD720p60 = /* 'hp60' */ 0x68703630, - /* 2k Modes */ + /* 2K Modes */ bmdMode2k2398 = /* '2k23' */ 0x326B3233, bmdMode2k24 = /* '2k24' */ 0x326B3234, bmdMode2k25 = /* '2k25' */ 0x326B3235, - /* DCI Modes (output only) */ + /* 2K DCI Modes */ bmdMode2kDCI2398 = /* '2d23' */ 0x32643233, bmdMode2kDCI24 = /* '2d24' */ 0x32643234, bmdMode2kDCI25 = /* '2d25' */ 0x32643235, + bmdMode2kDCI2997 = /* '2d29' */ 0x32643239, + bmdMode2kDCI30 = /* '2d30' */ 0x32643330, + bmdMode2kDCI50 = /* '2d50' */ 0x32643530, + bmdMode2kDCI5994 = /* '2d59' */ 0x32643539, + bmdMode2kDCI60 = /* '2d60' */ 0x32643630, - /* 4k Modes */ + /* 4K UHD Modes */ bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233, bmdMode4K2160p24 = /* '4k24' */ 0x346B3234, @@ -101,11 +110,43 @@ enum _BMDDisplayMode { bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539, bmdMode4K2160p60 = /* '4k60' */ 0x346B3630, - /* DCI Modes (output only) */ + /* 4K DCI Modes */ bmdMode4kDCI2398 = /* '4d23' */ 0x34643233, bmdMode4kDCI24 = /* '4d24' */ 0x34643234, bmdMode4kDCI25 = /* '4d25' */ 0x34643235, + bmdMode4kDCI2997 = /* '4d29' */ 0x34643239, + bmdMode4kDCI30 = /* '4d30' */ 0x34643330, + bmdMode4kDCI50 = /* '4d50' */ 0x34643530, + bmdMode4kDCI5994 = /* '4d59' */ 0x34643539, + bmdMode4kDCI60 = /* '4d60' */ 0x34643630, + + /* 8K UHD Modes */ + + bmdMode8K4320p2398 = /* '8k23' */ 0x386B3233, + bmdMode8K4320p24 = /* '8k24' */ 0x386B3234, + bmdMode8K4320p25 = /* '8k25' */ 0x386B3235, + bmdMode8K4320p2997 = /* '8k29' */ 0x386B3239, + bmdMode8K4320p30 = /* '8k30' */ 0x386B3330, + bmdMode8K4320p50 = /* '8k50' */ 0x386B3530, + bmdMode8K4320p5994 = /* '8k59' */ 0x386B3539, + bmdMode8K4320p60 = /* '8k60' */ 0x386B3630, + + /* 8K DCI Modes */ + + bmdMode8kDCI2398 = /* '8d23' */ 0x38643233, + bmdMode8kDCI24 = /* '8d24' */ 0x38643234, + bmdMode8kDCI25 = /* '8d25' */ 0x38643235, + bmdMode8kDCI2997 = /* '8d29' */ 0x38643239, + bmdMode8kDCI30 = /* '8d30' */ 0x38643330, + bmdMode8kDCI50 = /* '8d50' */ 0x38643530, + bmdMode8kDCI5994 = /* '8d59' */ 0x38643539, + bmdMode8kDCI60 = /* '8d60' */ 0x38643630, + + /* RAW Modes for Cintel (input only) */ + + bmdModeCintelRAW = /* 'rwci' */ 0x72776369, // Frame size up to 4096x3072, variable frame rate + bmdModeCintelCompressedRAW = /* 'rwcc' */ 0x72776363, // Frame size up to 4096x3072, variable frame rate /* Special Modes */ @@ -140,7 +181,12 @@ enum _BMDPixelFormat { /* AVID DNxHR */ - bmdFormatDNxHR = /* 'AVdh' */ 0x41566468 + bmdFormatDNxHR = /* 'AVdh' */ 0x41566468, + + /* Cintel formats */ + + bmdFormat12BitRAWGRBG = /* 'r12p' */ 0x72313270, // 12-bit RAW data for bayer pattern GRBG + bmdFormat12BitRAWJPEG = /* 'r16p' */ 0x72313670 // 12-bit RAW data arranged in tiles and JPEG compressed }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ @@ -149,7 +195,8 @@ typedef uint32_t BMDDisplayModeFlags; enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = 1 << 0, bmdDisplayModeColorspaceRec601 = 1 << 1, - bmdDisplayModeColorspaceRec709 = 1 << 2 + bmdDisplayModeColorspaceRec709 = 1 << 2, + bmdDisplayModeColorspaceRec2020 = 1 << 3 }; // Forward Declarations @@ -159,7 +206,7 @@ class IDeckLinkDisplayMode; /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ -class IDeckLinkDisplayModeIterator : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; @@ -170,7 +217,7 @@ protected: /* Interface IDeckLinkDisplayMode - represents a display mode */ -class IDeckLinkDisplayMode : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ const char **name) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h index 979a9aa..5591262 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPITypes.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations typedef int64_t BMDTimeValue; @@ -54,7 +58,8 @@ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, - bmdTimecodeFieldMark = 1 << 1 + bmdTimecodeFieldMark = 1 << 1, + bmdTimecodeColorFrame = 1 << 2 }; /* Enum BMDVideoConnection - Video connection types */ @@ -96,7 +101,7 @@ class IDeckLinkTimecode; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ -class IDeckLinkTimecode : public IUnknown +class BMD_PUBLIC IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h index dfd1799..f9db7ff 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPIVersion.h @@ -7,14 +7,14 @@ * ** execute, and transmit the Software, and to prepare derivative works of the * ** Software, and to permit third-parties to whom the Software is furnished to * ** do so, all subject to the following: - * ** + * ** * ** The copyright notices in the Software and this entire statement, including * ** the above license grant, this restriction and the following disclaimer, * ** must be included in all copies of the Software, in whole or in part, and * ** all derivative works of the Software, unless such copies or derivative * ** works are solely in the form of machine-executable object code generated by * ** a source language processor. - * ** + * ** * ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -30,8 +30,8 @@ #ifndef __DeckLink_API_Version_h__ #define __DeckLink_API_Version_h__ -#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a080000 -#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.8" +#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a0b0400 +#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.11.4" #endif // __DeckLink_API_Version_h__ diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h index a465133..ad2ff32 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,7 +37,7 @@ typedef uint32_t BMDDeckLinkConfigurationID_v10_2; enum _BMDDeckLinkConfigurationID_v10_2 { /* Video output flags */ - + bmdDeckLinkConfig3GBpsVideoOutput_v10_2 = '3gbs', }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_9.h new file mode 100644 index 0000000..aec8c7c --- /dev/null +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v10_9.h @@ -0,0 +1,45 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPI_v10_9_H +#define BMD_DECKLINKAPI_v10_9_H + +#include "DeckLinkAPI.h" + +// Type Declarations + +/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ + +typedef uint32_t BMDDeckLinkConfigurationID_v10_9; +enum _BMDDeckLinkConfigurationID_v10_9 { + + /* Flags */ + + bmdDeckLinkConfig1080pNotPsF_v10_9 = 'fpro', +}; + +#endif /* defined(BMD_DECKLINKAPI_v10_9_H) */ diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h index 1602d6a..b0c637c 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -66,14 +66,14 @@ class IDeckLinkVideoFrame_v7_1; class IDeckLinkVideoInputFrame_v7_1; class IDeckLinkAudioInputPacket_v7_1; -class IDeckLinkDisplayModeIterator_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next (IDeckLinkDisplayMode_v7_1* *deckLinkDisplayMode) = 0; }; -class IDeckLinkDisplayMode_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetName (const char **name) = 0; @@ -83,56 +83,56 @@ public: virtual HRESULT STDMETHODCALLTYPE GetFrameRate (BMDTimeValue *frameDuration, BMDTimeScale *timeScale) = 0; }; -class IDeckLinkVideoOutputCallback_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted (IDeckLinkVideoFrame_v7_1* completedFrame, BMDOutputFrameCompletionResult result) = 0; }; -class IDeckLinkInputCallback_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived (IDeckLinkVideoInputFrame_v7_1* videoFrame, IDeckLinkAudioInputPacket_v7_1* audioPacket) = 0; }; // IDeckLinkOutput_v7_1. Created by QueryInterface from IDeckLink. -class IDeckLinkOutput_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v7_1 : public IUnknown { public: // Display mode predicates virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1* *iterator) = 0; - - + + // Video output virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput (BMDDisplayMode displayMode) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator (IDeckLinkMemoryAllocator* theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame (int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrameFromBuffer (void* buffer, int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; - + virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync (IDeckLinkVideoFrame_v7_1* theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame (IDeckLinkVideoFrame_v7_1* theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback (IDeckLinkVideoOutputCallback_v7_1* theCallback) = 0; - - + + // Audio output virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples (void* buffer, uint32_t sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetAudioCallback (IDeckLinkAudioOutputCallback* theCallback) = 0; - - + + // Output control virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback (BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback (BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale) = 0; @@ -140,23 +140,23 @@ public: }; // IDeckLinkInput_v7_1. Created by QueryInterface from IDeckLink. -class IDeckLinkInput_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1 **iterator) = 0; - + // Video input virtual HRESULT STDMETHODCALLTYPE EnableVideoInput (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput () = 0; - + // Audio input virtual HRESULT STDMETHODCALLTYPE EnableAudioInput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput () = 0; virtual HRESULT STDMETHODCALLTYPE ReadAudioSamples (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesRead, BMDTimeValue *audioPacketTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; + - // Input control virtual HRESULT STDMETHODCALLTYPE StartStreams () = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams () = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams () = 0; @@ -164,7 +164,7 @@ public: }; // IDeckLinkVideoFrame_v7_1. Created by IDeckLinkOutput::CreateVideoFrame. -class IDeckLinkVideoFrame_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame_v7_1 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetWidth () = 0; @@ -176,19 +176,19 @@ public: }; // IDeckLinkVideoInputFrame_v7_1. Provided by the IDeckLinkInput_v7_1 frame arrival callback. -class IDeckLinkVideoInputFrame_v7_1 : public IDeckLinkVideoFrame_v7_1 +class BMD_PUBLIC IDeckLinkVideoInputFrame_v7_1 : public IDeckLinkVideoFrame_v7_1 { public: virtual HRESULT STDMETHODCALLTYPE GetFrameTime (BMDTimeValue *frameTime, BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; }; // IDeckLinkAudioInputPacket_v7_1. Provided by the IDeckLinkInput_v7_1 callback. -class IDeckLinkAudioInputPacket_v7_1 : public IUnknown +class BMD_PUBLIC IDeckLinkAudioInputPacket_v7_1 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetSampleCount () = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes (void* *buffer) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetAudioPacketTime (BMDTimeValue *packetTime, BMDTimeScale timeScale) = 0; }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h index c8b3da8..bb3192e 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_3.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -53,7 +53,7 @@ class IDeckLinkVideoInputFrame_v7_3; /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput_v7_3 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v7_3 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -107,7 +107,7 @@ protected: /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback_v7_3 : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback_v7_3 : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -122,7 +122,7 @@ protected: /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput_v7_3 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v7_3 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -158,7 +158,7 @@ protected: /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame_v7_3 : public IDeckLinkVideoFrame_v7_6 +class BMD_PUBLIC IDeckLinkVideoInputFrame_v7_3 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h index 8bc9329..d981206 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_6.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -83,7 +83,7 @@ class IDeckLinkVideoConversion_v7_6; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ -class IDeckLinkVideoOutputCallback_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback_v7_6 : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame_v7_6 *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; @@ -96,7 +96,7 @@ protected: /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback_v7_6 : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -109,7 +109,7 @@ protected: /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ -class IDeckLinkDisplayModeIterator_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator_v7_6 : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode_v7_6 **deckLinkDisplayMode) = 0; @@ -121,7 +121,7 @@ protected: /* Interface IDeckLinkDisplayMode - represents a display mode */ -class IDeckLinkDisplayMode_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode_v7_6 : public IUnknown { public: virtual HRESULT GetName (/* out */ const char **name) = 0; @@ -138,7 +138,7 @@ protected: /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v7_6 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -194,7 +194,7 @@ protected: /* Interface IDeckLinkInput_v7_6 - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v7_6 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* out */ BMDDisplayModeSupport *result) = 0; @@ -233,7 +233,7 @@ protected: /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ -class IDeckLinkTimecode_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkTimecode_v7_6 : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; @@ -248,7 +248,7 @@ protected: /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ -class IDeckLinkVideoFrame_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame_v7_6 : public IUnknown { public: virtual long GetWidth (void) = 0; @@ -268,7 +268,7 @@ protected: /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ -class IDeckLinkMutableVideoFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 +class BMD_PUBLIC IDeckLinkMutableVideoFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT SetFlags (BMDFrameFlags newFlags) = 0; @@ -284,7 +284,7 @@ protected: /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 +class BMD_PUBLIC IDeckLinkVideoInputFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; @@ -297,7 +297,7 @@ protected: /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ -class IDeckLinkScreenPreviewCallback_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkScreenPreviewCallback_v7_6 : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame_v7_6 *theFrame) = 0; @@ -309,7 +309,7 @@ protected: /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ -class IDeckLinkGLScreenPreviewHelper_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper_v7_6 : public IUnknown { public: @@ -326,7 +326,7 @@ protected: /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ -class IDeckLinkVideoConversion_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkVideoConversion_v7_6 : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame_v7_6* srcFrame, /* in */ IDeckLinkVideoFrame_v7_6* dstFrame) = 0; @@ -337,54 +337,54 @@ protected: /* Interface IDeckLinkConfiguration - Created by QueryInterface from IDeckLink. */ -class IDeckLinkConfiguration_v7_6 : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration_v7_6 : public IUnknown { public: virtual HRESULT GetConfigurationValidator (/* out */ IDeckLinkConfiguration_v7_6 **configObject) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; - + /* Video Output Configuration */ - + virtual HRESULT SetVideoOutputFormat (/* in */ BMDVideoConnection_v7_6 videoOutputConnection) = 0; virtual HRESULT IsVideoOutputActive (/* in */ BMDVideoConnection_v7_6 videoOutputConnection, /* out */ bool *active) = 0; - + virtual HRESULT SetAnalogVideoOutputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoOutputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT EnableFieldFlickerRemovalWhenPaused (/* in */ bool enable) = 0; virtual HRESULT IsEnabledFieldFlickerRemovalWhenPaused (/* out */ bool *enabled) = 0; - + virtual HRESULT Set444And3GBpsVideoOutput (/* in */ bool enable444VideoOutput, /* in */ bool enable3GbsOutput) = 0; virtual HRESULT Get444And3GBpsVideoOutput (/* out */ bool *is444VideoOutputEnabled, /* out */ bool *threeGbsOutputEnabled) = 0; - + virtual HRESULT SetVideoOutputConversionMode (/* in */ BMDVideoOutputConversionMode conversionMode) = 0; virtual HRESULT GetVideoOutputConversionMode (/* out */ BMDVideoOutputConversionMode *conversionMode) = 0; - + virtual HRESULT Set_HD1080p24_to_HD1080i5994_Conversion (/* in */ bool enable) = 0; virtual HRESULT Get_HD1080p24_to_HD1080i5994_Conversion (/* out */ bool *enabled) = 0; - + /* Video Input Configuration */ - + virtual HRESULT SetVideoInputFormat (/* in */ BMDVideoConnection_v7_6 videoInputFormat) = 0; virtual HRESULT GetVideoInputFormat (/* out */ BMDVideoConnection_v7_6 *videoInputFormat) = 0; - + virtual HRESULT SetAnalogVideoInputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoInputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT SetVideoInputConversionMode (/* in */ BMDVideoInputConversionMode conversionMode) = 0; virtual HRESULT GetVideoInputConversionMode (/* out */ BMDVideoInputConversionMode *conversionMode) = 0; - + virtual HRESULT SetBlackVideoOutputDuringCapture (/* in */ bool blackOutInCapture) = 0; virtual HRESULT GetBlackVideoOutputDuringCapture (/* out */ bool *blackOutInCapture) = 0; - + virtual HRESULT Set32PulldownSequenceInitialTimecodeFrame (/* in */ uint32_t aFrameTimecode) = 0; virtual HRESULT Get32PulldownSequenceInitialTimecodeFrame (/* out */ uint32_t *aFrameTimecode) = 0; - + virtual HRESULT SetVancSourceLineMapping (/* in */ uint32_t activeLine1VANCsource, /* in */ uint32_t activeLine2VANCsource, /* in */ uint32_t activeLine3VANCsource) = 0; virtual HRESULT GetVancSourceLineMapping (/* out */ uint32_t *activeLine1VANCsource, /* out */ uint32_t *activeLine2VANCsource, /* out */ uint32_t *activeLine3VANCsource) = 0; - + /* Audio Input Configuration */ - + virtual HRESULT SetAudioInputFormat (/* in */ BMDAudioConnection audioInputFormat) = 0; virtual HRESULT GetAudioInputFormat (/* out */ BMDAudioConnection *audioInputFormat) = 0; }; @@ -393,9 +393,9 @@ public: extern "C" { - IDeckLinkIterator* CreateDeckLinkIteratorInstance_v7_6 (void); - IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void); - IDeckLinkVideoConversion_v7_6* CreateVideoConversionInstance_v7_6 (void); + IDeckLinkIterator* BMD_PUBLIC CreateDeckLinkIteratorInstance_v7_6 (void); + IDeckLinkGLScreenPreviewHelper_v7_6* BMD_PUBLIC CreateOpenGLScreenPreviewHelper_v7_6 (void); + IDeckLinkVideoConversion_v7_6* BMD_PUBLIC CreateVideoConversionInstance_v7_6 (void); }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h index 406ce0a..c8e3faa 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v7_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -42,7 +42,7 @@ class IDeckLinkDeckControl_v7_9; /* Interface IDeckLinkDeckControl_v7_9 - Deck Control main interface */ -class IDeckLinkDeckControl_v7_9 : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl_v7_9 : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; @@ -77,7 +77,7 @@ public: virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback *callback) = 0; - + protected: virtual ~IDeckLinkDeckControl_v7_9 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h index 832664c..6cace7e 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_0.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -39,7 +39,7 @@ /* Interface IDeckLink_v8_0 - represents a DeckLink device */ -class IDeckLink_v8_0 : public IUnknown +class BMD_PUBLIC IDeckLink_v8_0 : public IUnknown { public: virtual HRESULT GetModelName (/* out */ const char **modelName) = 0; @@ -47,14 +47,14 @@ public: /* Interface IDeckLinkIterator_v8_0 - enumerates installed DeckLink hardware */ -class IDeckLinkIterator_v8_0 : public IUnknown +class BMD_PUBLIC IDeckLinkIterator_v8_0 : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink_v8_0 **deckLinkInstance) = 0; }; extern "C" { - IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance_v8_0 (void); + IDeckLinkIterator_v8_0* BMD_PUBLIC CreateDeckLinkIteratorInstance_v8_0 (void); }; #endif // defined __cplusplus diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h index 7f68919..c7362aa 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v8_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: - ** + ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. - ** + ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -52,21 +52,21 @@ enum _BMDDeckControlVTRControlState_v8_1 { /* Interface IDeckLinkDeckControlStatusCallback_v8_1 - Deck control state change callback. */ -class IDeckLinkDeckControlStatusCallback_v8_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControlStatusCallback_v8_1 : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState_v8_1 newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; - + protected: virtual ~IDeckLinkDeckControlStatusCallback_v8_1 () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDeckControl_v8_1 - Deck Control main interface */ -class IDeckLinkDeckControl_v8_1 : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl_v8_1 : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; @@ -102,7 +102,7 @@ public: virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback_v8_1 *callback) = 0; - + protected: virtual ~IDeckLinkDeckControl_v8_1 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h index 236d182..dd5f83f 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput_v9_2 : public IUnknown +class BMD_PUBLIC IDeckLinkInput_v9_2 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h index 98d115d..9a51bf2 100644 --- a/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h +++ b/plugins/decklink/linux/decklink-sdk/DeckLinkAPI_v9_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -40,7 +40,7 @@ /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput_v9_9 : public IUnknown +class BMD_PUBLIC IDeckLinkOutput_v9_9 : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; diff --git a/plugins/decklink/linux/decklink-sdk/LinuxCOM.h b/plugins/decklink/linux/decklink-sdk/LinuxCOM.h index 3041ea7..663602b 100644 --- a/plugins/decklink/linux/decklink-sdk/LinuxCOM.h +++ b/plugins/decklink/linux/decklink-sdk/LinuxCOM.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -29,7 +29,7 @@ #define __LINUX_COM_H_ struct REFIID -{ +{ unsigned char byte0; unsigned char byte1; unsigned char byte2; @@ -85,14 +85,19 @@ typedef void *LPVOID; #define IID_IUnknown (REFIID){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} #define IUnknownUUID IID_IUnknown +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + #ifdef __cplusplus -class IUnknown +class BMD_PUBLIC IUnknown { public: - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; - virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; - virtual ULONG STDMETHODCALLTYPE Release(void) = 0; + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; + virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; + virtual ULONG STDMETHODCALLTYPE Release(void) = 0; }; #endif -#endif +#endif + diff --git a/plugins/decklink/mac/CMakeLists.txt b/plugins/decklink/mac/CMakeLists.txt index d016470..04a2a1a 100644 --- a/plugins/decklink/mac/CMakeLists.txt +++ b/plugins/decklink/mac/CMakeLists.txt @@ -5,9 +5,9 @@ if(DISABLE_DECKLINK) return() endif() -find_library(COREFOUNDATION CoreFoundation) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}") -include_directories(${COREFOUNDATION}) +find_library(COREFOUNDATION CoreFoundation) set(mac-decklink-sdk_HEADERS decklink-sdk/DeckLinkAPI.h @@ -17,39 +17,59 @@ set(mac-decklink-sdk_HEADERS decklink-sdk/DeckLinkAPIModes.h decklink-sdk/DeckLinkAPIStreaming.h decklink-sdk/DeckLinkAPITypes.h - decklink-sdk/DeckLinkAPIVersion.h - ) + decklink-sdk/DeckLinkAPIVersion.h) set(mac-decklink-sdk_SOURCES decklink-sdk/DeckLinkAPIDispatch.cpp ) set(mac-decklink_HEADERS + ../decklink-devices.hpp + ../const.h + ../DecklinkOutput.hpp ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp ../decklink-device-mode.hpp ../audio-repack.h ../audio-repack.hpp + ../util.hpp ) set(mac-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../decklink-output.cpp + ../decklink-source.cpp + ../DecklinkOutput.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ../util.cpp + ) + +list(APPEND decklink_HEADERS ${decklink_UI_HEADERS}) + +include_directories( + ${COREFOUNDATION} + "${CMAKE_SOURCE_DIR}/UI/obs-frontend-api") + +list(APPEND mac-decklink_HEADERS ${decklink_UI_HEADERS}) add_library(mac-decklink MODULE ${mac-decklink_SOURCES} ${mac-decklink_HEADERS} ${mac-decklink-sdk_HEADERS} - ${mac-decklink-sdk_SOURCES}) + ${mac-decklink-sdk_SOURCES} + ) target_link_libraries(mac-decklink libobs diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h index 3b0fc11..368a72b 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + /* DeckLink API */ #include @@ -67,12 +71,16 @@ BMD_CONST REFIID IID_IDeckLinkIterator = /* 50FB36CD- BMD_CONST REFIID IID_IDeckLinkAPIInformation = /* 7BEA3C68-730D-4322-AF34-8A7152B532A4 */ {0x7B,0xEA,0x3C,0x68,0x73,0x0D,0x43,0x22,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4}; BMD_CONST REFIID IID_IDeckLinkOutput = /* CC5C8A6E-3F2F-4B3A-87EA-FD78AF300564 */ {0xCC,0x5C,0x8A,0x6E,0x3F,0x2F,0x4B,0x3A,0x87,0xEA,0xFD,0x78,0xAF,0x30,0x05,0x64}; BMD_CONST REFIID IID_IDeckLinkInput = /* AF22762B-DFAC-4846-AA79-FA8883560995 */ {0xAF,0x22,0x76,0x2B,0xDF,0xAC,0x48,0x46,0xAA,0x79,0xFA,0x88,0x83,0x56,0x09,0x95}; +BMD_CONST REFIID IID_IDeckLinkHDMIInputEDID = /* ABBBACBC-45BC-4665-9D92-ACE6E5A97902 */ {0xAB,0xBB,0xAC,0xBC,0x45,0xBC,0x46,0x65,0x9D,0x92,0xAC,0xE6,0xE5,0xA9,0x79,0x02}; BMD_CONST REFIID IID_IDeckLinkEncoderInput = /* 270587DA-6B7D-42E7-A1F0-6D853F581185 */ {0x27,0x05,0x87,0xDA,0x6B,0x7D,0x42,0xE7,0xA1,0xF0,0x6D,0x85,0x3F,0x58,0x11,0x85}; BMD_CONST REFIID IID_IDeckLinkVideoFrame = /* 3F716FE0-F023-4111-BE5D-EF4414C05B17 */ {0x3F,0x71,0x6F,0xE0,0xF0,0x23,0x41,0x11,0xBE,0x5D,0xEF,0x44,0x14,0xC0,0x5B,0x17}; BMD_CONST REFIID IID_IDeckLinkMutableVideoFrame = /* 69E2639F-40DA-4E19-B6F2-20ACE815C390 */ {0x69,0xE2,0x63,0x9F,0x40,0xDA,0x4E,0x19,0xB6,0xF2,0x20,0xAC,0xE8,0x15,0xC3,0x90}; BMD_CONST REFIID IID_IDeckLinkVideoFrame3DExtensions = /* DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7 */ {0xDA,0x0F,0x7E,0x4A,0xED,0xC7,0x48,0xA8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7}; BMD_CONST REFIID IID_IDeckLinkVideoFrameMetadataExtensions = /* D5973DC9-6432-46D0-8F0B-2496F8A1238F */ {0xD5,0x97,0x3D,0xC9,0x64,0x32,0x46,0xD0,0x8F,0x0B,0x24,0x96,0xF8,0xA1,0x23,0x8F}; BMD_CONST REFIID IID_IDeckLinkVideoInputFrame = /* 05CFE374-537C-4094-9A57-680525118F44 */ {0x05,0xCF,0xE3,0x74,0x53,0x7C,0x40,0x94,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacket = /* CC5BBF7E-029C-4D3B-9158-6000EF5E3670 */ {0xCC,0x5B,0xBF,0x7E,0x02,0x9C,0x4D,0x3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70}; +BMD_CONST REFIID IID_IDeckLinkAncillaryPacketIterator = /* 3FC8994B-88FB-4C17-968F-9AAB69D964A7 */ {0x3F,0xC8,0x99,0x4B,0x88,0xFB,0x4C,0x17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7}; +BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillaryPackets = /* 6C186C0F-459E-41D8-AEE2-4812D81AEE68 */ {0x6C,0x18,0x6C,0x0F,0x45,0x9E,0x41,0xD8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68}; BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillary = /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ {0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04}; BMD_CONST REFIID IID_IDeckLinkEncoderPacket = /* B693F36C-316E-4AF1-B6C2-F389A4BCA620 */ {0xB6,0x93,0xF3,0x6C,0x31,0x6E,0x4A,0xF1,0xB6,0xC2,0xF3,0x89,0xA4,0xBC,0xA6,0x20}; BMD_CONST REFIID IID_IDeckLinkEncoderVideoPacket = /* 4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0 */ {0x4E,0x7F,0xD9,0x44,0xE8,0xC7,0x4E,0xAC,0xB8,0xC0,0x7B,0x77,0xF8,0x0F,0x5A,0xE0}; @@ -117,9 +125,11 @@ enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameContainsHDRMetadata = 1 << 1, + bmdFrameContainsCintelMetadata = 1 << 2, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ + bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; @@ -163,10 +173,10 @@ enum _BMDDeckLinkCapturePassthroughMode { typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { - bmdOutputFrameCompleted, - bmdOutputFrameDisplayedLate, - bmdOutputFrameDropped, - bmdOutputFrameFlushed + bmdOutputFrameCompleted, + bmdOutputFrameDisplayedLate, + bmdOutputFrameDropped, + bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ @@ -203,9 +213,9 @@ enum _BMDAudioSampleType { typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { - bmdAudioOutputStreamContinuous, - bmdAudioOutputStreamContinuousDontResample, - bmdAudioOutputStreamTimestamped + bmdAudioOutputStreamContinuous, + bmdAudioOutputStreamContinuousDontResample, + bmdAudioOutputStreamTimestamped }; /* Enum BMDDisplayModeSupport - Output mode supported flags */ @@ -213,8 +223,17 @@ enum _BMDAudioOutputStreamType { typedef uint32_t BMDDisplayModeSupport; enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, - bmdDisplayModeSupported, - bmdDisplayModeSupportedWithConversion + bmdDisplayModeSupported, + bmdDisplayModeSupportedWithConversion +}; + +/* Enum BMDAncillaryPacketFormat - Ancillary packet format */ + +typedef uint32_t BMDAncillaryPacketFormat; +enum _BMDAncillaryPacketFormat { + bmdAncillaryPacketFormatUInt8 = 'ui08', + bmdAncillaryPacketFormatUInt16 = 'ui16', + bmdAncillaryPacketFormatYCbCr10 = 'v210' }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ @@ -336,11 +355,68 @@ enum _BMDDeviceInterface { bmdDeviceInterfaceThunderbolt = 'thun' }; +/* Enum BMDColorspace - Colorspace */ + +typedef uint32_t BMDColorspace; +enum _BMDColorspace { + bmdColorspaceRec601 = 'r601', + bmdColorspaceRec709 = 'r709', + bmdColorspaceRec2020 = '2020' +}; + +/* Enum BMDDynamicRange - SDR or HDR */ + +typedef uint32_t BMDDynamicRange; +enum _BMDDynamicRange { + bmdDynamicRangeSDR = 0, + bmdDynamicRangeHDRStaticPQ = 1 << 29, // SMPTE ST 2084 + bmdDynamicRangeHDRStaticHLG = 1 << 30 // ITU-R BT.2100-0 +}; + +/* Enum BMDDeckLinkHDMIInputEDIDID - DeckLink HDMI Input EDID ID */ + +typedef uint32_t BMDDeckLinkHDMIInputEDIDID; +enum _BMDDeckLinkHDMIInputEDIDID { + bmdDeckLinkHDMIInputEDIDDynamicRange = 'HIDy' // Parameter is of type BMDDynamicRange. Default is (bmdDynamicRangeSDR|bmdDynamicRangeHDRStaticPQ) +}; + /* Enum BMDDeckLinkFrameMetadataID - DeckLink Frame Metadata ID */ typedef uint32_t BMDDeckLinkFrameMetadataID; enum _BMDDeckLinkFrameMetadataID { + bmdDeckLinkFrameMetadataColorspace = 'cspc', // Colorspace of video frame (see BMDColorspace) bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = 'eotf', // EOTF in range 0-7 as per CEA 861.3 + bmdDeckLinkFrameMetadataCintelFilmType = 'cfty', // Current film type + bmdDeckLinkFrameMetadataCintelFilmGauge = 'cfga', // Current film gauge + bmdDeckLinkFrameMetadataCintelOffsetDetectedHorizontal = 'odfh', // Horizontal offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelOffsetDetectedVertical = 'odfv', // Vertical offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelKeykodeLow = 'ckkl', // Raw keykode value - low 64 bits + bmdDeckLinkFrameMetadataCintelKeykodeHigh = 'ckkh', // Raw keykode value - high 64 bits + bmdDeckLinkFrameMetadataCintelTile1Size = 'ct1s', // Size in bytes of compressed raw tile 1 + bmdDeckLinkFrameMetadataCintelTile2Size = 'ct2s', // Size in bytes of compressed raw tile 2 + bmdDeckLinkFrameMetadataCintelTile3Size = 'ct3s', // Size in bytes of compressed raw tile 3 + bmdDeckLinkFrameMetadataCintelTile4Size = 'ct4s', // Size in bytes of compressed raw tile 4 + bmdDeckLinkFrameMetadataCintelImageWidth = 'IWPx', // Width in pixels of image + bmdDeckLinkFrameMetadataCintelImageHeight = 'IHPx', // Height in pixels of image + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed = 'mrir', // Red in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed = 'mgir', // Green in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed = 'mbir', // Blue in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen = 'mrig', // Red in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen = 'mgig', // Green in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen = 'mbig', // Blue in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue = 'mrib', // Red in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue = 'mgib', // Green in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue = 'mbib', // Blue in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed = 'mlrr', // Red in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed = 'mlgr', // Green in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed = 'mlbr', // Blue in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen = 'mlrg', // Red in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen = 'mlgg', // Green in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen = 'mlbg', // Blue in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue = 'mlrb', // Red in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue = 'mlgb', // Green in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue = 'mlbb', // Blue in blue log masking parameter + bmdDeckLinkFrameMetadataCintelFilmFrameRate = 'cffr', // Film frame rate bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = 'hdrx', // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = 'hdry', // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = 'hdgx', // Green display primaries in range 0.0 - 1.0 @@ -352,7 +428,15 @@ enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = 'hdml', // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = 'hmil', // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = 'mcll', // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 - bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = 'fall' // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = 'fall', // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal = 'otah', // Horizontal offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical = 'otav', // Vertical offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelGainRed = 'LfRd', // Red gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainGreen = 'LfGr', // Green gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainBlue = 'LfBl', // Blue gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelLiftRed = 'GnRd', // Red lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftGreen = 'GnGr', // Green lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftBlue = 'GnBl' // Blue lift parameter to apply after log and gain }; /* Enum BMDDuplexMode - Duplex for configurable ports */ @@ -390,22 +474,24 @@ enum _BMDDeckLinkAttributeID { BMDDeckLinkHasLTCTimecodeInput = 'hltc', BMDDeckLinkSupportsDuplexModeConfiguration = 'dupx', BMDDeckLinkSupportsHDRMetadata = 'hdrm', + BMDDeckLinkSupportsColorspaceMetadata = 'cmet', /* Integers */ BMDDeckLinkMaximumAudioChannels = 'mach', - BMDDeckLinkMaximumAnalogAudioChannels = 'aach', + BMDDeckLinkMaximumAnalogAudioInputChannels = 'iach', + BMDDeckLinkMaximumAnalogAudioOutputChannels = 'aach', BMDDeckLinkNumberOfSubDevices = 'nsbd', BMDDeckLinkSubDeviceIndex = 'subi', BMDDeckLinkPersistentID = 'peid', BMDDeckLinkDeviceGroupID = 'dgid', BMDDeckLinkTopologicalID = 'toid', - BMDDeckLinkVideoOutputConnections = 'vocn', - BMDDeckLinkVideoInputConnections = 'vicn', - BMDDeckLinkAudioOutputConnections = 'aocn', - BMDDeckLinkAudioInputConnections = 'aicn', + BMDDeckLinkVideoOutputConnections = 'vocn', // Returns a BMDVideoConnection bit field + BMDDeckLinkVideoInputConnections = 'vicn', // Returns a BMDVideoConnection bit field + BMDDeckLinkAudioOutputConnections = 'aocn', // Returns a BMDAudioConnection bit field + BMDDeckLinkAudioInputConnections = 'aicn', // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = 'vios', // Returns a BMDVideoIOSupport bit field - BMDDeckLinkDeckControlConnections = 'dccn', + BMDDeckLinkDeckControlConnections = 'dccn', // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = 'dbus', // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = 'airc', BMDDeckLinkAudioInputXLRChannelCount = 'aixc', @@ -459,11 +545,14 @@ enum _BMDDeckLinkStatusID { bmdDeckLinkStatusReferenceSignalFlags = 'reff', bmdDeckLinkStatusDuplexMode = 'dupx', bmdDeckLinkStatusBusy = 'busy', + bmdDeckLinkStatusInterchangeablePanelType = 'icpt', + bmdDeckLinkStatusDeviceTemperature = 'dtmp', /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = 'visl', - bmdDeckLinkStatusReferenceSignalLocked = 'refl' + bmdDeckLinkStatusReferenceSignalLocked = 'refl', + bmdDeckLinkStatusReceivedEDID = 'edid' }; /* Enum BMDDeckLinkVideoStatusFlags - */ @@ -484,6 +573,14 @@ enum _BMDDuplexStatus { bmdDuplexStatusInactive = 'inac' }; +/* Enum BMDPanelType - The type of interchangeable panel */ + +typedef uint32_t BMDPanelType; +enum _BMDPanelType { + bmdPanelNotDetected = 'npnl', + bmdPanelTeranexMiniSmartPanel = 'tmsm' +}; + /* Enum BMDDeviceBusyState - Current device busy state */ typedef uint32_t BMDDeviceBusyState; @@ -533,12 +630,16 @@ class IDeckLinkIterator; class IDeckLinkAPIInformation; class IDeckLinkOutput; class IDeckLinkInput; +class IDeckLinkHDMIInputEDID; class IDeckLinkEncoderInput; class IDeckLinkVideoFrame; class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoFrameMetadataExtensions; class IDeckLinkVideoInputFrame; +class IDeckLinkAncillaryPacket; +class IDeckLinkAncillaryPacketIterator; +class IDeckLinkVideoFrameAncillaryPackets; class IDeckLinkVideoFrameAncillary; class IDeckLinkEncoderPacket; class IDeckLinkEncoderVideoPacket; @@ -559,7 +660,7 @@ class IDeckLinkDiscovery; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ -class IDeckLinkVideoOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; @@ -571,7 +672,7 @@ protected: /* Interface IDeckLinkInputCallback - Frame arrival callback. */ -class IDeckLinkInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -583,7 +684,7 @@ protected: /* Interface IDeckLinkEncoderInputCallback - Frame arrival callback. */ -class IDeckLinkEncoderInputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInputCallback : public IUnknown { public: virtual HRESULT VideoInputSignalChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; @@ -596,7 +697,7 @@ protected: /* Interface IDeckLinkMemoryAllocator - Memory allocator for video frames. */ -class IDeckLinkMemoryAllocator : public IUnknown +class BMD_PUBLIC IDeckLinkMemoryAllocator : public IUnknown { public: virtual HRESULT AllocateBuffer (/* in */ uint32_t bufferSize, /* out */ void **allocatedBuffer) = 0; @@ -608,7 +709,7 @@ public: /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ -class IDeckLinkAudioOutputCallback : public IUnknown +class BMD_PUBLIC IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; @@ -616,7 +717,7 @@ public: /* Interface IDeckLinkIterator - enumerates installed DeckLink hardware */ -class IDeckLinkIterator : public IUnknown +class BMD_PUBLIC IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink **deckLinkInstance) = 0; @@ -624,7 +725,7 @@ public: /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ -class IDeckLinkAPIInformation : public IUnknown +class BMD_PUBLIC IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool *value) = 0; @@ -638,7 +739,7 @@ protected: /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkOutput : public IUnknown +class BMD_PUBLIC IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -653,7 +754,7 @@ public: virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; - virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; + virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -695,7 +796,7 @@ protected: /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkInput : public IUnknown +class BMD_PUBLIC IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -732,9 +833,22 @@ protected: virtual ~IDeckLinkInput () {} // call Release method to drop reference count }; +/* Interface IDeckLinkHDMIInputEDID - Created by QueryInterface from IDeckLink. Releasing all references will restore EDID to default */ + +class BMD_PUBLIC IDeckLinkHDMIInputEDID : public IUnknown +{ +public: + virtual HRESULT SetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* in */ int64_t value) = 0; + virtual HRESULT GetInt (/* in */ BMDDeckLinkHDMIInputEDIDID cfgID, /* out */ int64_t *value) = 0; + virtual HRESULT WriteToEDID (void) = 0; + +protected: + virtual ~IDeckLinkHDMIInputEDID () {} // call Release method to drop reference count +}; + /* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */ -class IDeckLinkEncoderInput : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; @@ -771,7 +885,7 @@ protected: /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ -class IDeckLinkVideoFrame : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; @@ -782,7 +896,7 @@ public: virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) = 0; - virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; + virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred protected: virtual ~IDeckLinkVideoFrame () {} // call Release method to drop reference count @@ -790,7 +904,7 @@ protected: /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ -class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; @@ -806,7 +920,7 @@ protected: /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface implemented on IDeckLinkVideoFrame to support 3D frames */ -class IDeckLinkVideoFrame3DExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; @@ -818,7 +932,7 @@ protected: /* Interface IDeckLinkVideoFrameMetadataExtensions - Optional interface implemented on IDeckLinkVideoFrame to support frame metadata such as HDMI HDR information */ -class IDeckLinkVideoFrameMetadataExtensions : public IUnknown +class BMD_PUBLIC IDeckLinkVideoFrameMetadataExtensions : public IUnknown { public: virtual HRESULT GetInt (/* in */ BMDDeckLinkFrameMetadataID metadataID, /* out */ int64_t *value) = 0; @@ -832,7 +946,7 @@ protected: /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ -class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame +class BMD_PUBLIC IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, /* in */ BMDTimeScale timeScale) = 0; @@ -842,13 +956,56 @@ protected: virtual ~IDeckLinkVideoInputFrame () {} // call Release method to drop reference count }; -/* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ +/* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ -class IDeckLinkVideoFrameAncillary : public IUnknown +class BMD_PUBLIC IDeckLinkAncillaryPacket : public IUnknown { public: - virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; + virtual HRESULT GetBytes (/* in */ BMDAncillaryPacketFormat format /* For output, only one format need be offered */, /* out */ const void **data /* Optional */, /* out */ uint32_t *size /* Optional */) = 0; + virtual uint8_t GetDID (void) = 0; + virtual uint8_t GetSDID (void) = 0; + virtual uint32_t GetLineNumber (void) = 0; // On output, zero is auto + virtual uint8_t GetDataStreamIndex (void) = 0; // Usually zero. Can only be 1 if non-SD and the first data stream is completely full + +protected: + virtual ~IDeckLinkAncillaryPacket () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ + +class BMD_PUBLIC IDeckLinkAncillaryPacketIterator : public IUnknown +{ +public: + virtual HRESULT Next (/* out */ IDeckLinkAncillaryPacket **packet) = 0; + +protected: + virtual ~IDeckLinkAncillaryPacketIterator () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +class BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets : public IUnknown +{ +public: + + virtual HRESULT GetPacketIterator (/* out */ IDeckLinkAncillaryPacketIterator **iterator) = 0; + virtual HRESULT GetFirstPacketByID (/* in */ uint8_t DID, /* in */ uint8_t SDID, /* out */ IDeckLinkAncillaryPacket **packet) = 0; + virtual HRESULT AttachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; // Implement IDeckLinkAncillaryPacket to output your own + virtual HRESULT DetachPacket (/* in */ IDeckLinkAncillaryPacket *packet) = 0; + virtual HRESULT DetachAllPackets (void) = 0; + +protected: + virtual ~IDeckLinkVideoFrameAncillaryPackets () {} // call Release method to drop reference count +}; + +/* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +class BMD_PUBLIC IDeckLinkVideoFrameAncillary : public IUnknown +{ +public: + + virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; @@ -858,7 +1015,7 @@ protected: /* Interface IDeckLinkEncoderPacket - Interface to encapsulate an encoded packet. */ -class IDeckLinkEncoderPacket : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderPacket : public IUnknown { public: virtual HRESULT GetBytes (/* out */ void **buffer) = 0; @@ -872,7 +1029,7 @@ protected: /* Interface IDeckLinkEncoderVideoPacket - Provided by the IDeckLinkEncoderInput video packet arrival callback. */ -class IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderVideoPacket : public IDeckLinkEncoderPacket { public: virtual BMDPixelFormat GetPixelFormat (void) = 0; @@ -886,7 +1043,7 @@ protected: /* Interface IDeckLinkEncoderAudioPacket - Provided by the IDeckLinkEncoderInput audio packet arrival callback. */ -class IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket +class BMD_PUBLIC IDeckLinkEncoderAudioPacket : public IDeckLinkEncoderPacket { public: virtual BMDAudioFormat GetAudioFormat (void) = 0; @@ -897,7 +1054,7 @@ protected: /* Interface IDeckLinkH265NALPacket - Obtained through QueryInterface() on an IDeckLinkEncoderVideoPacket object */ -class IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket +class BMD_PUBLIC IDeckLinkH265NALPacket : public IDeckLinkEncoderVideoPacket { public: virtual HRESULT GetUnitType (/* out */ uint8_t *unitType) = 0; @@ -910,7 +1067,7 @@ protected: /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ -class IDeckLinkAudioInputPacket : public IUnknown +class BMD_PUBLIC IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; @@ -923,7 +1080,7 @@ protected: /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ -class IDeckLinkScreenPreviewCallback : public IUnknown +class BMD_PUBLIC IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; @@ -934,7 +1091,7 @@ protected: /* Interface IDeckLinkCocoaScreenPreviewCallback - Screen preview callback for Cocoa-based applications */ -class IDeckLinkCocoaScreenPreviewCallback : public IDeckLinkScreenPreviewCallback +class BMD_PUBLIC IDeckLinkCocoaScreenPreviewCallback : public IDeckLinkScreenPreviewCallback { public: @@ -944,7 +1101,7 @@ protected: /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ -class IDeckLinkGLScreenPreviewHelper : public IUnknown +class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper : public IUnknown { public: @@ -961,7 +1118,7 @@ protected: /* Interface IDeckLinkNotificationCallback - DeckLink Notification Callback Interface */ -class IDeckLinkNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkNotificationCallback : public IUnknown { public: virtual HRESULT Notify (/* in */ BMDNotifications topic, /* in */ uint64_t param1, /* in */ uint64_t param2) = 0; @@ -969,7 +1126,7 @@ public: /* Interface IDeckLinkNotification - DeckLink Notification interface */ -class IDeckLinkNotification : public IUnknown +class BMD_PUBLIC IDeckLinkNotification : public IUnknown { public: virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0; @@ -978,7 +1135,7 @@ public: /* Interface IDeckLinkAttributes - DeckLink Attribute interface */ -class IDeckLinkAttributes : public IUnknown +class BMD_PUBLIC IDeckLinkAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool *value) = 0; @@ -992,7 +1149,7 @@ protected: /* Interface IDeckLinkStatus - DeckLink Status interface */ -class IDeckLinkStatus : public IUnknown +class BMD_PUBLIC IDeckLinkStatus : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkStatusID statusID, /* out */ bool *value) = 0; @@ -1007,7 +1164,7 @@ protected: /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ -class IDeckLinkKeyer : public IUnknown +class BMD_PUBLIC IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; @@ -1022,7 +1179,7 @@ protected: /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ -class IDeckLinkVideoConversion : public IUnknown +class BMD_PUBLIC IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; @@ -1033,7 +1190,7 @@ protected: /* Interface IDeckLinkDeviceNotificationCallback - DeckLink device arrival/removal notification callbacks */ -class IDeckLinkDeviceNotificationCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeviceNotificationCallback : public IUnknown { public: virtual HRESULT DeckLinkDeviceArrived (/* in */ IDeckLink* deckLinkDevice) = 0; @@ -1045,7 +1202,7 @@ protected: /* Interface IDeckLinkDiscovery - DeckLink device discovery */ -class IDeckLinkDiscovery : public IUnknown +class BMD_PUBLIC IDeckLinkDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IDeckLinkDeviceNotificationCallback* deviceNotificationCallback) = 0; @@ -1059,12 +1216,13 @@ protected: extern "C" { - IDeckLinkIterator* CreateDeckLinkIteratorInstance (void); - IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void); - IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void); - IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void); - IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* /* (NSView*) */ parentView); - IDeckLinkVideoConversion* CreateVideoConversionInstance (void); + IDeckLinkIterator* BMD_PUBLIC CreateDeckLinkIteratorInstance (void); + IDeckLinkDiscovery* BMD_PUBLIC CreateDeckLinkDiscoveryInstance (void); + IDeckLinkAPIInformation* BMD_PUBLIC CreateDeckLinkAPIInformationInstance (void); + IDeckLinkGLScreenPreviewHelper* BMD_PUBLIC CreateOpenGLScreenPreviewHelper (void); + IDeckLinkCocoaScreenPreviewCallback* BMD_PUBLIC CreateCocoaScreenPreview (void* /* (NSView*) */ parentView); + IDeckLinkVideoConversion* BMD_PUBLIC CreateVideoConversionInstance (void); + IDeckLinkVideoFrameAncillaryPackets* BMD_PUBLIC CreateVideoFrameAncillaryPacketsInstance (void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h index 31b652b..a76d33a 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,12 +37,16 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations // Interface ID Declarations -BMD_CONST REFIID IID_IDeckLinkConfiguration = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; +BMD_CONST REFIID IID_IDeckLinkConfiguration = /* EF90380B-4AE5-4346-9077-E288E149F129 */ {0xEF,0x90,0x38,0x0B,0x4A,0xE5,0x43,0x46,0x90,0x77,0xE2,0x88,0xE1,0x49,0xF1,0x29}; BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration = /* 138050E5-C60A-4552-BF3F-0F358049327E */ {0x13,0x80,0x50,0xE5,0xC6,0x0A,0x45,0x52,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E}; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ @@ -54,10 +58,6 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigSwapSerialRxTx = 'ssrt', - /* Video Input/Output Flags */ - - bmdDeckLinkConfigUse1080pNotPsF = 'fpro', - /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = '3dpf', @@ -78,6 +78,12 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigLowLatencyVideoOutput = 'llvo', bmdDeckLinkConfigDownConversionOnAllAnalogOutput = 'caao', bmdDeckLinkConfigSMPTELevelAOutput = 'smta', + bmdDeckLinkConfigRec2020Output = 'rec2', // Ensure output is Rec.2020 colorspace + bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = 'SDQS', + + /* Video Output Flags */ + + bmdDeckLinkConfigOutput1080pAsPsF = 'pfpr', /* Video Output Integers */ @@ -106,6 +112,10 @@ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigUseDedicatedLTCInput = 'dltc', // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = '3dds', + /* Video Input Flags */ + + bmdDeckLinkConfigCapture1080pAsPsF = 'cfpr', + /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = 'vicn', @@ -206,7 +216,7 @@ class IDeckLinkEncoderConfiguration; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ -class IDeckLinkConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; @@ -225,7 +235,7 @@ protected: /* Interface IDeckLinkEncoderConfiguration - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */ -class IDeckLinkEncoderConfiguration : public IUnknown +class BMD_PUBLIC IDeckLinkEncoderConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h index ca7f815..2c989a0 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h index 29695cf..0808367 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_5.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h new file mode 100644 index 0000000..e49a3ee --- /dev/null +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIConfiguration_v10_9.h @@ -0,0 +1,62 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPICONFIGURATION_v10_9_H +#define BMD_DECKLINKAPICONFIGURATION_v10_9_H + +#include "DeckLinkAPIConfiguration.h" + +// Interface ID Declarations + +BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_9 = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2}; + +// +// Forward Declarations + +class IDeckLinkConfiguration_v10_9; + +/* Interface IDeckLinkConfiguration_v10_9 - DeckLink Configuration interface */ + +class IDeckLinkConfiguration_v10_9 : public IUnknown +{ +public: + virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; + virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0; + virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; + virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0; + virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; + virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0; + virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ CFStringRef value) = 0; + virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ CFStringRef *value) = 0; + virtual HRESULT WriteConfigurationToPreferences (void) = 0; + +protected: + virtual ~IDeckLinkConfiguration_v10_9 () {} // call Release method to drop reference count +}; + + +#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_9_H) */ diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h index 6237ab7..b1b7797 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDeckControl.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -149,7 +153,7 @@ class IDeckLinkDeckControl; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ -class IDeckLinkDeckControlStatusCallback : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; @@ -163,7 +167,7 @@ protected: /* Interface IDeckLinkDeckControl - Deck Control main interface */ -class IDeckLinkDeckControl : public IUnknown +class BMD_PUBLIC IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h index 3715a03..c8331ad 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDiscovery.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -50,7 +54,7 @@ class IDeckLink; /* Interface IDeckLink - represents a DeckLink device */ -class IDeckLink : public IUnknown +class BMD_PUBLIC IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ CFStringRef *modelName) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp index 92e41b2..7bb93ce 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -42,6 +42,7 @@ typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(v typedef IDeckLinkCocoaScreenPreviewCallback* (*CreateCocoaScreenPreviewFunc)(void*); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); +typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static CFBundleRef gDeckLinkAPIBundleRef = NULL; @@ -51,9 +52,10 @@ static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateCocoaScreenPreviewFunc gCreateCocoaPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc= NULL; +static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL; -void InitDeckLinkAPI (void) +static void InitDeckLinkAPI (void) { CFURLRef bundleURL; @@ -63,12 +65,13 @@ void InitDeckLinkAPI (void) gDeckLinkAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); if (gDeckLinkAPIBundleRef != NULL) { - gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0002")); + gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0003")); gCreateAPIInformationFunc = (CreateAPIInformationFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkAPIInformationInstance_0001")); gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateOpenGLScreenPreviewHelper_0001")); gCreateCocoaPreviewFunc = (CreateCocoaScreenPreviewFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateCocoaScreenPreview_0001")); gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoConversionInstance_0001")); - gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkDiscoveryInstance_0001")); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkDiscoveryInstance_0002")); + gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoFrameAncillaryPacketsInstance_0001")); } CFRelease(bundleURL); } @@ -79,70 +82,80 @@ bool IsDeckLinkAPIPresent (void) // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller if (gDeckLinkAPIBundleRef != NULL) return true; - + return false; } IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; - + return gCreateIteratorFunc(); } IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; - + return gCreateAPIInformationFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; - + return gCreateOpenGLPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateCocoaPreviewFunc == NULL) return NULL; - + return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; - + return gCreateVideoConversionFunc(); } IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateDeckLinkDiscoveryFunc == NULL) return NULL; - + return gCreateDeckLinkDiscoveryFunc(); } +IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance (void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoFrameAncillaryPacketsFunc == NULL) + return NULL; + + return gCreateVideoFrameAncillaryPacketsFunc(); +} + #define kBMDStreamingAPI_BundlePath "/Library/Application Support/Blackmagic Design/Streaming/BMDStreamingAPI.bundle" @@ -154,7 +167,7 @@ static CFBundleRef gBMDStreamingAPIBundleRef = NULL; static CreateDiscoveryFunc gCreateDiscoveryFunc = NULL; static CreateNALParserFunc gCreateNALParserFunc = NULL; -void InitBMDStreamingAPI(void) +static void InitBMDStreamingAPI(void) { CFURLRef bundleURL; @@ -164,7 +177,7 @@ void InitBMDStreamingAPI(void) gBMDStreamingAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); if (gBMDStreamingAPIBundleRef != NULL) { - gCreateDiscoveryFunc = (CreateDiscoveryFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingDiscoveryInstance_0001")); + gCreateDiscoveryFunc = (CreateDiscoveryFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingDiscoveryInstance_0002")); gCreateNALParserFunc = (CreateNALParserFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingH264NALParser_0001")); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp new file mode 100644 index 0000000..c412a10 --- /dev/null +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v10_8.cpp @@ -0,0 +1,193 @@ +/* -LICENSE-START- +** Copyright (c) 2009 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ +/* DeckLinkAPIDispatch.cpp */ + +#include "DeckLinkAPI.h" +#include + +#if BLACKMAGIC_DECKLINK_API_MAGIC != 1 +#error The DeckLink API version of DeckLinkAPIDispatch.cpp is not the same version as DeckLinkAPI.h +#endif + +#define kDeckLinkAPI_BundlePath "/Library/Frameworks/DeckLinkAPI.framework" + + +typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); +typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); +typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); +typedef IDeckLinkCocoaScreenPreviewCallback* (*CreateCocoaScreenPreviewFunc)(void*); +typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); +typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void); + +static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; +static CFBundleRef gDeckLinkAPIBundleRef = NULL; +static CreateIteratorFunc gCreateIteratorFunc = NULL; +static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; +static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; +static CreateCocoaScreenPreviewFunc gCreateCocoaPreviewFunc = NULL; +static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; +static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL; + + +static void InitDeckLinkAPI(void) +{ + CFURLRef bundleURL; + + bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kDeckLinkAPI_BundlePath), kCFURLPOSIXPathStyle, true); + if (bundleURL != NULL) + { + gDeckLinkAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); + if (gDeckLinkAPIBundleRef != NULL) + { + gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0002")); + gCreateAPIInformationFunc = (CreateAPIInformationFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkAPIInformationInstance_0001")); + gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateOpenGLScreenPreviewHelper_0001")); + gCreateCocoaPreviewFunc = (CreateCocoaScreenPreviewFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateCocoaScreenPreview_0001")); + gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoConversionInstance_0001")); + gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkDiscoveryInstance_0001")); + } + CFRelease(bundleURL); + } +} + +bool IsDeckLinkAPIPresent(void) +{ + // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller + if (gDeckLinkAPIBundleRef != NULL) + return true; + + return false; +} + +IDeckLinkIterator* CreateDeckLinkIteratorInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateIteratorFunc == NULL) + return NULL; + + return gCreateIteratorFunc(); +} + +IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateAPIInformationFunc == NULL) + return NULL; + + return gCreateAPIInformationFunc(); +} + +IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateOpenGLPreviewFunc == NULL) + return NULL; + + return gCreateOpenGLPreviewFunc(); +} + +IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview(void* parentView) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateCocoaPreviewFunc == NULL) + return NULL; + + return gCreateCocoaPreviewFunc(parentView); +} + +IDeckLinkVideoConversion* CreateVideoConversionInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateVideoConversionFunc == NULL) + return NULL; + + return gCreateVideoConversionFunc(); +} + +IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance(void) +{ + pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); + + if (gCreateDeckLinkDiscoveryFunc == NULL) + return NULL; + + return gCreateDeckLinkDiscoveryFunc(); +} + + +#define kBMDStreamingAPI_BundlePath "/Library/Application Support/Blackmagic Design/Streaming/BMDStreamingAPI.bundle" + +typedef IBMDStreamingDiscovery* (*CreateDiscoveryFunc)(void); +typedef IBMDStreamingH264NALParser* (*CreateNALParserFunc)(void); + +static pthread_once_t gBMDStreamingOnceControl = PTHREAD_ONCE_INIT; +static CFBundleRef gBMDStreamingAPIBundleRef = NULL; +static CreateDiscoveryFunc gCreateDiscoveryFunc = NULL; +static CreateNALParserFunc gCreateNALParserFunc = NULL; + +static void InitBMDStreamingAPI(void) +{ + CFURLRef bundleURL; + + bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kBMDStreamingAPI_BundlePath), kCFURLPOSIXPathStyle, true); + if (bundleURL != NULL) + { + gBMDStreamingAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); + if (gBMDStreamingAPIBundleRef != NULL) + { + gCreateDiscoveryFunc = (CreateDiscoveryFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingDiscoveryInstance_0001")); + gCreateNALParserFunc = (CreateNALParserFunc)CFBundleGetFunctionPointerForName(gBMDStreamingAPIBundleRef, CFSTR("CreateBMDStreamingH264NALParser_0001")); + } + + CFRelease(bundleURL); + } +} + +IBMDStreamingDiscovery* CreateBMDStreamingDiscoveryInstance() +{ + pthread_once(&gBMDStreamingOnceControl, InitBMDStreamingAPI); + + if (gCreateDiscoveryFunc == NULL) + return NULL; + + return gCreateDiscoveryFunc(); +} + +IBMDStreamingH264NALParser* CreateBMDStreamingH264NALParser() +{ + pthread_once(&gBMDStreamingOnceControl, InitBMDStreamingAPI); + + if (gCreateNALParserFunc == NULL) + return NULL; + + return gCreateNALParserFunc(); +} diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp index 8b0c0cc..ba918b0 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v7_6.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -44,7 +44,7 @@ static CreateCocoaScreenPreviewFunc_v7_6 gCreateCocoaPreviewFunc = NULL; static CreateVideoConversionInstanceFunc_v7_6 gCreateVideoConversionFunc = NULL; -void InitDeckLinkAPI_v7_6 (void) +static void InitDeckLinkAPI_v7_6 (void) { CFURLRef bundleURL; @@ -66,40 +66,40 @@ void InitDeckLinkAPI_v7_6 (void) IDeckLinkIterator* CreateDeckLinkIteratorInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateIteratorFunc == NULL) return NULL; - + return gCreateIteratorFunc(); } IDeckLinkGLScreenPreviewHelper_v7_6* CreateOpenGLScreenPreviewHelper_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; - + return gCreateOpenGLPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback_v7_6* CreateCocoaScreenPreview_v7_6 (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateCocoaPreviewFunc == NULL) return NULL; - + return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion_v7_6* CreateVideoConversionInstance_v7_6 (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI_v7_6); - + if (gCreateVideoConversionFunc == NULL) return NULL; - + return gCreateVideoConversionFunc(); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp index 898d739..3791bf4 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIDispatch_v8_0.cpp @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -50,7 +50,7 @@ static CreateCocoaScreenPreviewFunc gCreateCocoaPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; -void InitDeckLinkAPI (void) +static void InitDeckLinkAPI (void) { CFURLRef bundleURL; @@ -75,57 +75,57 @@ bool IsDeckLinkAPIPresent (void) // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller if (gDeckLinkAPIBundleRef != NULL) return true; - + return false; } IDeckLinkIterator_v8_0* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateIteratorFunc == NULL) return NULL; - + return gCreateIteratorFunc(); } IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateAPIInformationFunc == NULL) return NULL; - + return gCreateAPIInformationFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateOpenGLPreviewFunc == NULL) return NULL; - + return gCreateOpenGLPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateCocoaPreviewFunc == NULL) return NULL; - + return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); - + if (gCreateVideoConversionFunc == NULL) return NULL; - + return gCreateVideoConversionFunc(); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h index 3bc3429..9a242e0 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIModes.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -65,12 +69,12 @@ enum _BMDDisplayMode { bmdModeHD1080p25 = 'Hp25', bmdModeHD1080p2997 = 'Hp29', bmdModeHD1080p30 = 'Hp30', - bmdModeHD1080i50 = 'Hi50', - bmdModeHD1080i5994 = 'Hi59', - bmdModeHD1080i6000 = 'Hi60', // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = 'Hp50', bmdModeHD1080p5994 = 'Hp59', bmdModeHD1080p6000 = 'Hp60', // N.B. This _really_ is 60.00 Hz. + bmdModeHD1080i50 = 'Hi50', + bmdModeHD1080i5994 = 'Hi59', + bmdModeHD1080i6000 = 'Hi60', // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ @@ -78,19 +82,24 @@ enum _BMDDisplayMode { bmdModeHD720p5994 = 'hp59', bmdModeHD720p60 = 'hp60', - /* 2k Modes */ + /* 2K Modes */ bmdMode2k2398 = '2k23', bmdMode2k24 = '2k24', bmdMode2k25 = '2k25', - /* DCI Modes (output only) */ + /* 2K DCI Modes */ bmdMode2kDCI2398 = '2d23', bmdMode2kDCI24 = '2d24', bmdMode2kDCI25 = '2d25', + bmdMode2kDCI2997 = '2d29', + bmdMode2kDCI30 = '2d30', + bmdMode2kDCI50 = '2d50', + bmdMode2kDCI5994 = '2d59', + bmdMode2kDCI60 = '2d60', - /* 4k Modes */ + /* 4K UHD Modes */ bmdMode4K2160p2398 = '4k23', bmdMode4K2160p24 = '4k24', @@ -101,11 +110,43 @@ enum _BMDDisplayMode { bmdMode4K2160p5994 = '4k59', bmdMode4K2160p60 = '4k60', - /* DCI Modes (output only) */ + /* 4K DCI Modes */ bmdMode4kDCI2398 = '4d23', bmdMode4kDCI24 = '4d24', bmdMode4kDCI25 = '4d25', + bmdMode4kDCI2997 = '4d29', + bmdMode4kDCI30 = '4d30', + bmdMode4kDCI50 = '4d50', + bmdMode4kDCI5994 = '4d59', + bmdMode4kDCI60 = '4d60', + + /* 8K UHD Modes */ + + bmdMode8K4320p2398 = '8k23', + bmdMode8K4320p24 = '8k24', + bmdMode8K4320p25 = '8k25', + bmdMode8K4320p2997 = '8k29', + bmdMode8K4320p30 = '8k30', + bmdMode8K4320p50 = '8k50', + bmdMode8K4320p5994 = '8k59', + bmdMode8K4320p60 = '8k60', + + /* 8K DCI Modes */ + + bmdMode8kDCI2398 = '8d23', + bmdMode8kDCI24 = '8d24', + bmdMode8kDCI25 = '8d25', + bmdMode8kDCI2997 = '8d29', + bmdMode8kDCI30 = '8d30', + bmdMode8kDCI50 = '8d50', + bmdMode8kDCI5994 = '8d59', + bmdMode8kDCI60 = '8d60', + + /* RAW Modes for Cintel (input only) */ + + bmdModeCintelRAW = 'rwci', // Frame size up to 4096x3072, variable frame rate + bmdModeCintelCompressedRAW = 'rwcc', // Frame size up to 4096x3072, variable frame rate /* Special Modes */ @@ -140,7 +181,12 @@ enum _BMDPixelFormat { /* AVID DNxHR */ - bmdFormatDNxHR = 'AVdh' + bmdFormatDNxHR = 'AVdh', + + /* Cintel formats */ + + bmdFormat12BitRAWGRBG = 'r12p', // 12-bit RAW data for bayer pattern GRBG + bmdFormat12BitRAWJPEG = 'r16p' // 12-bit RAW data arranged in tiles and JPEG compressed }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ @@ -149,7 +195,8 @@ typedef uint32_t BMDDisplayModeFlags; enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = 1 << 0, bmdDisplayModeColorspaceRec601 = 1 << 1, - bmdDisplayModeColorspaceRec709 = 1 << 2 + bmdDisplayModeColorspaceRec709 = 1 << 2, + bmdDisplayModeColorspaceRec2020 = 1 << 3 }; // Forward Declarations @@ -159,7 +206,7 @@ class IDeckLinkDisplayMode; /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ -class IDeckLinkDisplayModeIterator : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; @@ -170,7 +217,7 @@ protected: /* Interface IDeckLinkDisplayMode - represents a display mode */ -class IDeckLinkDisplayMode : public IUnknown +class BMD_PUBLIC IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ CFStringRef *name) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h index 6bb5168..c2d357c 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIStreaming.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations @@ -92,8 +96,8 @@ enum _BMDStreamingEncodingFrameRate { typedef uint32_t BMDStreamingEncodingSupport; enum _BMDStreamingEncodingSupport { bmdStreamingEncodingModeNotSupported = 0, - bmdStreamingEncodingModeSupported, - bmdStreamingEncodingModeSupportedWithChanges + bmdStreamingEncodingModeSupported, + bmdStreamingEncodingModeSupportedWithChanges }; /* Enum BMDStreamingVideoCodec - Video codecs */ @@ -188,7 +192,7 @@ class IBMDStreamingH264NALParser; /* Interface IBMDStreamingDeviceNotificationCallback - Device notification callbacks. */ -class IBMDStreamingDeviceNotificationCallback : public IUnknown +class BMD_PUBLIC IBMDStreamingDeviceNotificationCallback : public IUnknown { public: virtual HRESULT StreamingDeviceArrived (/* in */ IDeckLink* device) = 0; @@ -201,7 +205,7 @@ protected: /* Interface IBMDStreamingH264InputCallback - H264 input callbacks. */ -class IBMDStreamingH264InputCallback : public IUnknown +class BMD_PUBLIC IBMDStreamingH264InputCallback : public IUnknown { public: virtual HRESULT H264NALPacketArrived (/* in */ IBMDStreamingH264NALPacket* nalPacket) = 0; @@ -217,7 +221,7 @@ protected: /* Interface IBMDStreamingDiscovery - Installs device notifications */ -class IBMDStreamingDiscovery : public IUnknown +class BMD_PUBLIC IBMDStreamingDiscovery : public IUnknown { public: virtual HRESULT InstallDeviceNotifications (/* in */ IBMDStreamingDeviceNotificationCallback* theCallback) = 0; @@ -229,7 +233,7 @@ protected: /* Interface IBMDStreamingVideoEncodingMode - Represents an encoded video mode. */ -class IBMDStreamingVideoEncodingMode : public IUnknown +class BMD_PUBLIC IBMDStreamingVideoEncodingMode : public IUnknown { public: virtual HRESULT GetName (/* out */ CFStringRef *name) = 0; @@ -252,7 +256,7 @@ protected: /* Interface IBMDStreamingMutableVideoEncodingMode - Represents a mutable encoded video mode. */ -class IBMDStreamingMutableVideoEncodingMode : public IBMDStreamingVideoEncodingMode +class BMD_PUBLIC IBMDStreamingMutableVideoEncodingMode : public IBMDStreamingVideoEncodingMode { public: virtual HRESULT SetSourceRect (/* in */ uint32_t posX, /* in */ uint32_t posY, /* in */ uint32_t width, /* in */ uint32_t height) = 0; @@ -268,7 +272,7 @@ protected: /* Interface IBMDStreamingVideoEncodingModePresetIterator - Enumerates encoding mode presets */ -class IBMDStreamingVideoEncodingModePresetIterator : public IUnknown +class BMD_PUBLIC IBMDStreamingVideoEncodingModePresetIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IBMDStreamingVideoEncodingMode** videoEncodingMode) = 0; @@ -279,7 +283,7 @@ protected: /* Interface IBMDStreamingDeviceInput - Created by QueryInterface from IDeckLink */ -class IBMDStreamingDeviceInput : public IUnknown +class BMD_PUBLIC IBMDStreamingDeviceInput : public IUnknown { public: @@ -309,7 +313,7 @@ protected: /* Interface IBMDStreamingH264NALPacket - Represent an H.264 NAL packet */ -class IBMDStreamingH264NALPacket : public IUnknown +class BMD_PUBLIC IBMDStreamingH264NALPacket : public IUnknown { public: virtual long GetPayloadSize (void) = 0; @@ -324,7 +328,7 @@ protected: /* Interface IBMDStreamingAudioPacket - Represents a chunk of audio data */ -class IBMDStreamingAudioPacket : public IUnknown +class BMD_PUBLIC IBMDStreamingAudioPacket : public IUnknown { public: virtual BMDStreamingAudioCodec GetCodec (void) = 0; @@ -339,7 +343,7 @@ protected: /* Interface IBMDStreamingMPEG2TSPacket - Represent an MPEG2 Transport Stream packet */ -class IBMDStreamingMPEG2TSPacket : public IUnknown +class BMD_PUBLIC IBMDStreamingMPEG2TSPacket : public IUnknown { public: virtual long GetPayloadSize (void) = 0; @@ -351,7 +355,7 @@ protected: /* Interface IBMDStreamingH264NALParser - For basic NAL parsing */ -class IBMDStreamingH264NALParser : public IUnknown +class BMD_PUBLIC IBMDStreamingH264NALParser : public IUnknown { public: virtual HRESULT IsNALSequenceParameterSet (/* in */ IBMDStreamingH264NALPacket* nal) = 0; @@ -366,8 +370,8 @@ protected: extern "C" { - IBMDStreamingDiscovery* CreateBMDStreamingDiscoveryInstance (void); - IBMDStreamingH264NALParser* CreateBMDStreamingH264NALParser (void); + IBMDStreamingDiscovery* BMD_PUBLIC CreateBMDStreamingDiscoveryInstance (void); + IBMDStreamingH264NALParser* BMD_PUBLIC CreateBMDStreamingH264NALParser (void); } diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h index 04da49f..1564c6e 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPITypes.h @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,6 +37,10 @@ #endif #endif +#ifndef BMD_PUBLIC + #define BMD_PUBLIC +#endif + // Type Declarations typedef int64_t BMDTimeValue; @@ -54,7 +58,8 @@ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, - bmdTimecodeFieldMark = 1 << 1 + bmdTimecodeFieldMark = 1 << 1, + bmdTimecodeColorFrame = 1 << 2 }; /* Enum BMDVideoConnection - Video connection types */ @@ -96,7 +101,7 @@ class IDeckLinkTimecode; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ -class IDeckLinkTimecode : public IUnknown +class BMD_PUBLIC IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h index 55b24b1..f9db7ff 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPIVersion.h @@ -7,14 +7,14 @@ * ** execute, and transmit the Software, and to prepare derivative works of the * ** Software, and to permit third-parties to whom the Software is furnished to * ** do so, all subject to the following: - * ** + * ** * ** The copyright notices in the Software and this entire statement, including * ** the above license grant, this restriction and the following disclaimer, * ** must be included in all copies of the Software, in whole or in part, and * ** all derivative works of the Software, unless such copies or derivative * ** works are solely in the form of machine-executable object code generated by * ** a source language processor. - * ** + * ** * ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -30,7 +30,8 @@ #ifndef __DeckLink_API_Version_h__ #define __DeckLink_API_Version_h__ -#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a080000 -#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.8" +#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a0b0400 +#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.11.4" #endif // __DeckLink_API_Version_h__ + diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h index 29ff820..e5c95e4 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -37,7 +37,7 @@ typedef uint32_t BMDDeckLinkConfigurationID_v10_2; enum _BMDDeckLinkConfigurationID_v10_2 { /* Video output flags */ - + bmdDeckLinkConfig3GBpsVideoOutput_v10_2 = '3gbs', }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_9.h new file mode 100644 index 0000000..aec8c7c --- /dev/null +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v10_9.h @@ -0,0 +1,45 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + +#ifndef BMD_DECKLINKAPI_v10_9_H +#define BMD_DECKLINKAPI_v10_9_H + +#include "DeckLinkAPI.h" + +// Type Declarations + +/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ + +typedef uint32_t BMDDeckLinkConfigurationID_v10_9; +enum _BMDDeckLinkConfigurationID_v10_9 { + + /* Flags */ + + bmdDeckLinkConfig1080pNotPsF_v10_9 = 'fpro', +}; + +#endif /* defined(BMD_DECKLINKAPI_v10_9_H) */ diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h index 50e0d11..68d0beb 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -102,37 +102,37 @@ public: // Display mode predicates virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1* *iterator) = 0; - - + + // Video output virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput (BMDDisplayMode displayMode) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator (IDeckLinkMemoryAllocator* theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame (int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrameFromBuffer (void* buffer, int32_t width, int32_t height, int32_t rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1* *outFrame) = 0; - + virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync (IDeckLinkVideoFrame_v7_1* theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame (IDeckLinkVideoFrame_v7_1* theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback (IDeckLinkVideoOutputCallback_v7_1* theCallback) = 0; - - + + // Audio output virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput () = 0; - + virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll () = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples (void* buffer, uint32_t sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples () = 0; - + virtual HRESULT STDMETHODCALLTYPE SetAudioCallback (IDeckLinkAudioOutputCallback* theCallback) = 0; - - + + // Output control virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback (BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback (BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale) = 0; @@ -145,17 +145,17 @@ class IDeckLinkInput_v7_1 : public IUnknown public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator (IDeckLinkDisplayModeIterator_v7_1 **iterator) = 0; - + // Video input virtual HRESULT STDMETHODCALLTYPE EnableVideoInput (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput () = 0; - + // Audio input virtual HRESULT STDMETHODCALLTYPE EnableAudioInput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, uint32_t channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput () = 0; virtual HRESULT STDMETHODCALLTYPE ReadAudioSamples (void* buffer, uint32_t sampleFrameCount, uint32_t *sampleFramesRead, BMDTimeValue *audioPacketTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount (uint32_t *bufferedSampleCount) = 0; - + // Input control virtual HRESULT STDMETHODCALLTYPE StartStreams () = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams () = 0; @@ -188,7 +188,7 @@ class IDeckLinkAudioInputPacket_v7_1 : public IUnknown public: virtual long STDMETHODCALLTYPE GetSampleCount () = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes (void* *buffer) = 0; - + virtual HRESULT STDMETHODCALLTYPE GetAudioPacketTime (BMDTimeValue *packetTime, BMDTimeScale timeScale) = 0; }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h index c8b3da8..430a905 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_3.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h index 93d7c04..a90d497 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_6.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -356,49 +356,49 @@ class IDeckLinkConfiguration_v7_6 : public IUnknown public: virtual HRESULT GetConfigurationValidator (/* out */ IDeckLinkConfiguration_v7_6 **configObject) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; - + /* Video Output Configuration */ - + virtual HRESULT SetVideoOutputFormat (/* in */ BMDVideoConnection_v7_6 videoOutputConnection) = 0; virtual HRESULT IsVideoOutputActive (/* in */ BMDVideoConnection_v7_6 videoOutputConnection, /* out */ bool *active) = 0; - + virtual HRESULT SetAnalogVideoOutputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoOutputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT EnableFieldFlickerRemovalWhenPaused (/* in */ bool enable) = 0; virtual HRESULT IsEnabledFieldFlickerRemovalWhenPaused (/* out */ bool *enabled) = 0; - + virtual HRESULT Set444And3GBpsVideoOutput (/* in */ bool enable444VideoOutput, /* in */ bool enable3GbsOutput) = 0; virtual HRESULT Get444And3GBpsVideoOutput (/* out */ bool *is444VideoOutputEnabled, /* out */ bool *threeGbsOutputEnabled) = 0; - + virtual HRESULT SetVideoOutputConversionMode (/* in */ BMDVideoOutputConversionMode conversionMode) = 0; virtual HRESULT GetVideoOutputConversionMode (/* out */ BMDVideoOutputConversionMode *conversionMode) = 0; - + virtual HRESULT Set_HD1080p24_to_HD1080i5994_Conversion (/* in */ bool enable) = 0; virtual HRESULT Get_HD1080p24_to_HD1080i5994_Conversion (/* out */ bool *enabled) = 0; - + /* Video Input Configuration */ - + virtual HRESULT SetVideoInputFormat (/* in */ BMDVideoConnection_v7_6 videoInputFormat) = 0; virtual HRESULT GetVideoInputFormat (/* out */ BMDVideoConnection_v7_6 *videoInputFormat) = 0; - + virtual HRESULT SetAnalogVideoInputFlags (/* in */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT GetAnalogVideoInputFlags (/* out */ BMDAnalogVideoFlags *analogVideoFlags) = 0; - + virtual HRESULT SetVideoInputConversionMode (/* in */ BMDVideoInputConversionMode conversionMode) = 0; virtual HRESULT GetVideoInputConversionMode (/* out */ BMDVideoInputConversionMode *conversionMode) = 0; - + virtual HRESULT SetBlackVideoOutputDuringCapture (/* in */ bool blackOutInCapture) = 0; virtual HRESULT GetBlackVideoOutputDuringCapture (/* out */ bool *blackOutInCapture) = 0; - + virtual HRESULT Set32PulldownSequenceInitialTimecodeFrame (/* in */ uint32_t aFrameTimecode) = 0; virtual HRESULT Get32PulldownSequenceInitialTimecodeFrame (/* out */ uint32_t *aFrameTimecode) = 0; - + virtual HRESULT SetVancSourceLineMapping (/* in */ uint32_t activeLine1VANCsource, /* in */ uint32_t activeLine2VANCsource, /* in */ uint32_t activeLine3VANCsource) = 0; virtual HRESULT GetVancSourceLineMapping (/* out */ uint32_t *activeLine1VANCsource, /* out */ uint32_t *activeLine2VANCsource, /* out */ uint32_t *activeLine3VANCsource) = 0; - + /* Audio Input Configuration */ - + virtual HRESULT SetAudioInputFormat (/* in */ BMDAudioConnection audioInputFormat) = 0; virtual HRESULT GetAudioInputFormat (/* out */ BMDAudioConnection *audioInputFormat) = 0; }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h index 327a382..c882f61 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v7_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h index b4e8f21..ea06183 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_0.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h index e1444cd..0eea695 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v8_1.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: - ** + ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. - ** + ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -59,7 +59,7 @@ public: virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState_v8_1 newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; - + protected: virtual ~IDeckLinkDeckControlStatusCallback_v8_1 () {}; // call Release method to drop reference count }; @@ -102,7 +102,7 @@ public: virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback_v8_1 *callback) = 0; - + protected: virtual ~IDeckLinkDeckControl_v8_1 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h index 236d182..ab2aa03 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_2.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h index 3fa3a50..966c8ad 100644 --- a/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h +++ b/plugins/decklink/mac/decklink-sdk/DeckLinkAPI_v9_9.h @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -47,51 +47,51 @@ class IDeckLinkOutput_v9_9 : public IUnknown public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0; - + virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; - + /* Video Output */ - + virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0; virtual HRESULT DisableVideoOutput (void) = 0; - + virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; - + virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback *theCallback) = 0; virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t *bufferedFrameCount) = 0; - + /* Audio Output */ - + virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT DisableAudioOutput (void) = 0; - + virtual HRESULT WriteAudioSamplesSync (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT BeginAudioPreroll (void) = 0; virtual HRESULT EndAudioPreroll (void) = 0; virtual HRESULT ScheduleAudioSamples (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t *sampleFramesWritten) = 0; - + virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t *bufferedSampleFrameCount) = 0; virtual HRESULT FlushBufferedAudioSamples (void) = 0; - + virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback *theCallback) = 0; - + /* Output Control */ - + virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0; virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue *actualStopTime, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool *active) = 0; virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *streamTime, /* out */ double *playbackSpeed) = 0; virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus *referenceStatus) = 0; - + /* Hardware Timing */ - + virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0; - + protected: virtual ~IDeckLinkOutput_v9_9 () {}; // call Release method to drop reference count }; diff --git a/plugins/decklink/mac/platform.cpp b/plugins/decklink/mac/platform.cpp index 02e8a59..21ca4ec 100644 --- a/plugins/decklink/mac/platform.cpp +++ b/plugins/decklink/mac/platform.cpp @@ -1,22 +1,17 @@ #include "../platform.hpp" +#include bool DeckLinkStringToStdString(decklink_string_t input, std::string& output) { const CFStringRef string = static_cast(input); - const CFIndex length = CFStringGetLength(string); - const CFIndex maxLength = CFStringGetMaximumSizeForEncoding(length, - kCFStringEncodingASCII) + 1; - char * const buffer = new char[maxLength]; + char *buffer = cfstr_copy_cstr(string, kCFStringEncodingASCII); - const bool result = CFStringGetCString(string, buffer, maxLength, - kCFStringEncodingASCII); - - if (result) + if (buffer) output = std::string(buffer); - delete[] buffer; + bfree(buffer); CFRelease(string); - return result; + return (buffer != NULL); } diff --git a/plugins/decklink/plugin-main.cpp b/plugins/decklink/plugin-main.cpp index 78c9b25..023de7b 100644 --- a/plugins/decklink/plugin-main.cpp +++ b/plugins/decklink/plugin-main.cpp @@ -1,287 +1,18 @@ -#include "decklink.hpp" -#include "decklink-device.hpp" -#include "decklink-device-discovery.hpp" - #include +#include "decklink-devices.hpp" OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US") - -#define DEVICE_HASH "device_hash" -#define DEVICE_NAME "device_name" -#define MODE_ID "mode_id" -#define MODE_NAME "mode_name" -#define CHANNEL_FORMAT "channel_format" -#define PIXEL_FORMAT "pixel_format" -#define COLOR_SPACE "color_space" -#define COLOR_RANGE "color_range" -#define BUFFERING "buffering" - -#define TEXT_DEVICE obs_module_text("Device") -#define TEXT_MODE obs_module_text("Mode") -#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat") -#define TEXT_COLOR_SPACE obs_module_text("ColorSpace") -#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default") -#define TEXT_COLOR_RANGE obs_module_text("ColorRange") -#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default") -#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial") -#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full") -#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat") -#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None") -#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch") -#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch") -#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch") -#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch") -#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch") -#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch") -#define TEXT_BUFFERING obs_module_text("Buffering") - -static DeckLinkDeviceDiscovery *deviceEnum = nullptr; - -static void decklink_enable_buffering(DeckLink *decklink, bool enabled) +MODULE_EXPORT const char *obs_module_description(void) { - obs_source_t *source = decklink->GetSource(); - obs_source_set_async_unbuffered(source, !enabled); - decklink->buffering = enabled; + return "Blackmagic DeckLink source"; } -static void *decklink_create(obs_data_t *settings, obs_source_t *source) -{ - DeckLink *decklink = new DeckLink(source, deviceEnum); +extern struct obs_source_info create_decklink_source_info(); +struct obs_source_info decklink_source_info; - obs_source_set_async_decoupled(source, true); - decklink_enable_buffering(decklink, - obs_data_get_bool(settings, BUFFERING)); - - obs_source_update(source, settings); - return decklink; -} - -static void decklink_destroy(void *data) -{ - DeckLink *decklink = (DeckLink *)data; - delete decklink; -} - -static void decklink_update(void *data, obs_data_t *settings) -{ - DeckLink *decklink = (DeckLink *)data; - const char *hash = obs_data_get_string(settings, DEVICE_HASH); - long long id = obs_data_get_int(settings, MODE_ID); - BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, - PIXEL_FORMAT); - video_colorspace colorSpace = (video_colorspace)obs_data_get_int(settings, - COLOR_SPACE); - video_range_type colorRange = (video_range_type)obs_data_get_int(settings, - COLOR_RANGE); - int chFmtInt = (int)obs_data_get_int(settings, CHANNEL_FORMAT); - - if (chFmtInt == 7) { - chFmtInt = SPEAKERS_5POINT1; - } else if (chFmtInt < SPEAKERS_UNKNOWN || chFmtInt > SPEAKERS_7POINT1) { - chFmtInt = 2; - } - - speaker_layout channelFormat = (speaker_layout)chFmtInt; - - decklink_enable_buffering(decklink, - obs_data_get_bool(settings, BUFFERING)); - - ComPtr device; - device.Set(deviceEnum->FindByHash(hash)); - - decklink->SetPixelFormat(pixelFormat); - decklink->SetColorSpace(colorSpace); - decklink->SetColorRange(colorRange); - decklink->SetChannelFormat(channelFormat); - decklink->Activate(device, id); -} - -static void decklink_get_defaults(obs_data_t *settings) -{ - obs_data_set_default_bool(settings, BUFFERING, false); - obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV); - obs_data_set_default_int(settings, COLOR_SPACE, VIDEO_CS_DEFAULT); - obs_data_set_default_int(settings, COLOR_RANGE, VIDEO_RANGE_DEFAULT); - obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO); -} - -static const char *decklink_get_name(void*) -{ - return obs_module_text("BlackmagicDevice"); -} - -static bool decklink_device_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - const char *name = obs_data_get_string(settings, DEVICE_NAME); - const char *hash = obs_data_get_string(settings, DEVICE_HASH); - const char *mode = obs_data_get_string(settings, MODE_NAME); - long long modeId = obs_data_get_int(settings, MODE_ID); - - size_t itemCount = obs_property_list_item_count(list); - bool itemFound = false; - - for (size_t i = 0; i < itemCount; i++) { - const char *curHash = obs_property_list_item_string(list, i); - if (strcmp(hash, curHash) == 0) { - itemFound = true; - break; - } - } - - if (!itemFound) { - obs_property_list_insert_string(list, 0, name, hash); - obs_property_list_item_disable(list, 0, true); - } - - obs_property_t *modeList = obs_properties_get(props, MODE_ID); - obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); - - obs_property_list_clear(modeList); - - obs_property_list_clear(channelList); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_NONE, - SPEAKERS_UNKNOWN); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_0CH, - SPEAKERS_STEREO); - - ComPtr device; - device.Set(deviceEnum->FindByHash(hash)); - - if (!device) { - obs_property_list_add_int(modeList, mode, modeId); - obs_property_list_item_disable(modeList, 0, true); - } else { - const std::vector &modes = - device->GetModes(); - - for (DeckLinkDeviceMode *mode : modes) { - obs_property_list_add_int(modeList, - mode->GetName().c_str(), - mode->GetId()); - } - - if (device->GetMaxChannel() >= 8) { - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_1CH, - SPEAKERS_2POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_4_0CH, - SPEAKERS_4POINT0); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_4_1CH, - SPEAKERS_4POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_5_1CH, - SPEAKERS_5POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_7_1CH, - SPEAKERS_7POINT1); - } - } - - return true; -} - -static void fill_out_devices(obs_property_t *list) -{ - deviceEnum->Lock(); - - const std::vector &devices = deviceEnum->GetDevices(); - for (DeckLinkDevice *device : devices) { - obs_property_list_add_string(list, - device->GetDisplayName().c_str(), - device->GetHash().c_str()); - } - - deviceEnum->Unlock(); -} - -static bool color_format_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings); - -static bool mode_id_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - long long id = obs_data_get_int(settings, MODE_ID); - - list = obs_properties_get(props, PIXEL_FORMAT); - obs_property_set_visible(list, id != MODE_ID_AUTO); - - return color_format_changed(props, nullptr, settings); -} - -static bool color_format_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - long long id = obs_data_get_int(settings, MODE_ID); - BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, - PIXEL_FORMAT); - - list = obs_properties_get(props, COLOR_SPACE); - obs_property_set_visible(list, - id != MODE_ID_AUTO && pixelFormat == bmdFormat8BitYUV); - - list = obs_properties_get(props, COLOR_RANGE); - obs_property_set_visible(list, - id == MODE_ID_AUTO || pixelFormat == bmdFormat8BitYUV); - - return true; -} - -static obs_properties_t *decklink_get_properties(void *data) -{ - obs_properties_t *props = obs_properties_create(); - - obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, - TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - obs_property_set_modified_callback(list, decklink_device_changed); - - fill_out_devices(list); - - list = obs_properties_add_list(props, MODE_ID, TEXT_MODE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_modified_callback(list, mode_id_changed); - - list = obs_properties_add_list(props, PIXEL_FORMAT, - TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_modified_callback(list, color_format_changed); - - obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); - obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); - - list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT, VIDEO_CS_DEFAULT); - obs_property_list_add_int(list, "BT.601", VIDEO_CS_601); - obs_property_list_add_int(list, "BT.709", VIDEO_CS_709); - - list = obs_properties_add_list(props, COLOR_RANGE, TEXT_COLOR_RANGE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT, VIDEO_RANGE_DEFAULT); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL, VIDEO_RANGE_PARTIAL); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL, VIDEO_RANGE_FULL); - - list = obs_properties_add_list(props, CHANNEL_FORMAT, - TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE, - SPEAKERS_UNKNOWN); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH, - SPEAKERS_STEREO); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_1CH, - SPEAKERS_2POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_0CH, - SPEAKERS_4POINT0); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_1CH, - SPEAKERS_4POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_5_1CH, - SPEAKERS_5POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH, - SPEAKERS_7POINT1); - - obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); - - UNUSED_PARAMETER(data); - return props; -} +extern struct obs_output_info create_decklink_output_info(); +struct obs_output_info decklink_output_info; bool obs_module_load(void) { @@ -289,19 +20,11 @@ bool obs_module_load(void) if (!deviceEnum->Init()) return true; - struct obs_source_info info = {}; - info.id = "decklink-input"; - info.type = OBS_SOURCE_TYPE_INPUT; - info.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | - OBS_SOURCE_DO_NOT_DUPLICATE; - info.create = decklink_create; - info.destroy = decklink_destroy; - info.get_defaults = decklink_get_defaults; - info.get_name = decklink_get_name; - info.get_properties = decklink_get_properties; - info.update = decklink_update; + decklink_source_info = create_decklink_source_info(); + obs_register_source(&decklink_source_info); - obs_register_source(&info); + decklink_output_info = create_decklink_output_info(); + obs_register_output(&decklink_output_info); return true; } diff --git a/plugins/decklink/util.cpp b/plugins/decklink/util.cpp new file mode 100644 index 0000000..c3055e8 --- /dev/null +++ b/plugins/decklink/util.cpp @@ -0,0 +1,43 @@ +#include "util.hpp" + +const char *bmd_video_connection_to_name(BMDVideoConnection connection) +{ + switch (connection) { + case bmdVideoConnectionSDI: + return "SDI"; + case bmdVideoConnectionHDMI: + return "HDMI"; + case bmdVideoConnectionOpticalSDI: + return "Optical SDI"; + case bmdVideoConnectionComponent: + return "Component"; + case bmdVideoConnectionComposite: + return "Composite"; + case bmdVideoConnectionSVideo: + return "S-Video"; + default: + return "Unknown"; + } +} + +const char *bmd_audio_connection_to_name(BMDAudioConnection connection) +{ + switch (connection) { + case bmdAudioConnectionEmbedded: + return "Embedded"; + case bmdAudioConnectionAESEBU: + return "AES/EBU"; + case bmdAudioConnectionAnalog: + return "Analog"; + case bmdAudioConnectionAnalogXLR: + return "Analog XLR"; + case bmdAudioConnectionAnalogRCA: + return "Analog RCA"; + case bmdAudioConnectionMicrophone: + return "Microphone"; + case bmdAudioConnectionHeadphones: + return "Headphones"; + default: + return "Unknown"; + } +} \ No newline at end of file diff --git a/plugins/decklink/util.hpp b/plugins/decklink/util.hpp new file mode 100644 index 0000000..f264a0b --- /dev/null +++ b/plugins/decklink/util.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "decklink-device.hpp" + +const char *bmd_video_connection_to_name(BMDVideoConnection connection); + +const char *bmd_audio_connection_to_name(BMDAudioConnection connection); \ No newline at end of file diff --git a/plugins/decklink/win/CMakeLists.txt b/plugins/decklink/win/CMakeLists.txt index af94645..005dbb8 100644 --- a/plugins/decklink/win/CMakeLists.txt +++ b/plugins/decklink/win/CMakeLists.txt @@ -16,37 +16,52 @@ set(win-decklink-sdk_HEADERS ) set(win-decklink_HEADERS + ../decklink-devices.hpp + ../DecklinkOutput.hpp + ../const.h ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp ../decklink-device-mode.hpp ../audio-repack.h ../audio-repack.hpp + ../util.hpp ) set(win-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../DecklinkOutput.cpp + ../decklink-source.cpp + ../decklink-output.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ../util.cpp + ) add_idl_files(win-decklink-sdk_GENERATED_FILES - ${win-decklink-sdk_IDLS}) + ${win-decklink-sdk_IDLS} + ) include_directories( - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_BINARY_DIR} +) add_library(win-decklink MODULE ${win-decklink_SOURCES} ${win-decklink_HEADERS} ${win-decklink-sdk_HEADERS} - ${win-decklink-sdk_GENERATED_FILES}) + ${win-decklink-sdk_GENERATED_FILES} + ) target_link_libraries(win-decklink libobs) diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl index 786d68d..07ee9fe 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI.idl @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -95,9 +95,11 @@ typedef [v1_enum] enum _BMDPacketType { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, bmdFrameContainsHDRMetadata = 1 << 1, + bmdFrameContainsCintelMetadata = 1 << 2, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ + bmdFrameCapturedAsPsF = 1 << 30, bmdFrameHasNoInputSource = 1 << 31 }; @@ -136,10 +138,10 @@ typedef [v1_enum] enum _BMDPacketType { /* Enum BMDOutputFrameCompletionResult - Frame Completion Callback */ typedef [v1_enum] enum _BMDOutputFrameCompletionResult { - bmdOutputFrameCompleted, - bmdOutputFrameDisplayedLate, - bmdOutputFrameDropped, - bmdOutputFrameFlushed + bmdOutputFrameCompleted, + bmdOutputFrameDisplayedLate, + bmdOutputFrameDropped, + bmdOutputFrameFlushed } BMDOutputFrameCompletionResult; /* Enum BMDReferenceStatus - GenLock input status */ @@ -171,19 +173,27 @@ typedef [v1_enum] enum _BMDAudioSampleType { /* Enum BMDAudioOutputStreamType - Audio output stream type */ typedef [v1_enum] enum _BMDAudioOutputStreamType { - bmdAudioOutputStreamContinuous, - bmdAudioOutputStreamContinuousDontResample, - bmdAudioOutputStreamTimestamped + bmdAudioOutputStreamContinuous, + bmdAudioOutputStreamContinuousDontResample, + bmdAudioOutputStreamTimestamped } BMDAudioOutputStreamType; /* Enum BMDDisplayModeSupport - Output mode supported flags */ typedef [v1_enum] enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, - bmdDisplayModeSupported, - bmdDisplayModeSupportedWithConversion + bmdDisplayModeSupported, + bmdDisplayModeSupportedWithConversion } BMDDisplayModeSupport; +/* Enum BMDAncillaryPacketFormat - Ancillary packet format */ + +typedef [v1_enum] enum _BMDAncillaryPacketFormat { + bmdAncillaryPacketFormatUInt8 = /* 'ui08' */ 0x75693038, + bmdAncillaryPacketFormatUInt16 = /* 'ui16' */ 0x75693136, + bmdAncillaryPacketFormatYCbCr10 = /* 'v210' */ 0x76323130 +} BMDAncillaryPacketFormat; + /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ typedef [v1_enum] enum _BMDTimecodeFormat { @@ -292,10 +302,64 @@ typedef [v1_enum] enum _BMDDeviceInterface { bmdDeviceInterfaceThunderbolt = /* 'thun' */ 0x7468756E } BMDDeviceInterface; +/* Enum BMDColorspace - Colorspace */ + +typedef [v1_enum] enum _BMDColorspace { + bmdColorspaceRec601 = /* 'r601' */ 0x72363031, + bmdColorspaceRec709 = /* 'r709' */ 0x72373039, + bmdColorspaceRec2020 = /* '2020' */ 0x32303230 +} BMDColorspace; + +/* Enum BMDDynamicRange - SDR or HDR */ + +typedef [v1_enum] enum _BMDDynamicRange { + bmdDynamicRangeSDR = 0, + bmdDynamicRangeHDRStaticPQ = 1 << 29, // SMPTE ST 2084 + bmdDynamicRangeHDRStaticHLG = 1 << 30 // ITU-R BT.2100-0 +} BMDDynamicRange; + +/* Enum BMDDeckLinkHDMIInputEDIDID - DeckLink HDMI Input EDID ID */ + +typedef [v1_enum] enum _BMDDeckLinkHDMIInputEDIDID { + bmdDeckLinkHDMIInputEDIDDynamicRange = /* 'HIDy' */ 0x48494479 // Parameter is of type BMDDynamicRange. Default is (bmdDynamicRangeSDR|bmdDynamicRangeHDRStaticPQ) +} BMDDeckLinkHDMIInputEDIDID; + /* Enum BMDDeckLinkFrameMetadataID - DeckLink Frame Metadata ID */ typedef [v1_enum] enum _BMDDeckLinkFrameMetadataID { + bmdDeckLinkFrameMetadataColorspace = /* 'cspc' */ 0x63737063, // Colorspace of video frame (see BMDColorspace) bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc = /* 'eotf' */ 0x656F7466, // EOTF in range 0-7 as per CEA 861.3 + bmdDeckLinkFrameMetadataCintelFilmType = /* 'cfty' */ 0x63667479, // Current film type + bmdDeckLinkFrameMetadataCintelFilmGauge = /* 'cfga' */ 0x63666761, // Current film gauge + bmdDeckLinkFrameMetadataCintelOffsetDetectedHorizontal = /* 'odfh' */ 0x6F646668, // Horizontal offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelOffsetDetectedVertical = /* 'odfv' */ 0x6F646676, // Vertical offset (pixels) detected in image + bmdDeckLinkFrameMetadataCintelKeykodeLow = /* 'ckkl' */ 0x636B6B6C, // Raw keykode value - low 64 bits + bmdDeckLinkFrameMetadataCintelKeykodeHigh = /* 'ckkh' */ 0x636B6B68, // Raw keykode value - high 64 bits + bmdDeckLinkFrameMetadataCintelTile1Size = /* 'ct1s' */ 0x63743173, // Size in bytes of compressed raw tile 1 + bmdDeckLinkFrameMetadataCintelTile2Size = /* 'ct2s' */ 0x63743273, // Size in bytes of compressed raw tile 2 + bmdDeckLinkFrameMetadataCintelTile3Size = /* 'ct3s' */ 0x63743373, // Size in bytes of compressed raw tile 3 + bmdDeckLinkFrameMetadataCintelTile4Size = /* 'ct4s' */ 0x63743473, // Size in bytes of compressed raw tile 4 + bmdDeckLinkFrameMetadataCintelImageWidth = /* 'IWPx' */ 0x49575078, // Width in pixels of image + bmdDeckLinkFrameMetadataCintelImageHeight = /* 'IHPx' */ 0x49485078, // Height in pixels of image + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed = /* 'mrir' */ 0x6D726972, // Red in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed = /* 'mgir' */ 0x6D676972, // Green in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed = /* 'mbir' */ 0x6D626972, // Blue in red linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen = /* 'mrig' */ 0x6D726967, // Red in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen = /* 'mgig' */ 0x6D676967, // Green in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen = /* 'mbig' */ 0x6D626967, // Blue in green linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue = /* 'mrib' */ 0x6D726962, // Red in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue = /* 'mgib' */ 0x6D676962, // Green in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue = /* 'mbib' */ 0x6D626962, // Blue in blue linear masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed = /* 'mlrr' */ 0x6D6C7272, // Red in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed = /* 'mlgr' */ 0x6D6C6772, // Green in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed = /* 'mlbr' */ 0x6D6C6272, // Blue in red log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen = /* 'mlrg' */ 0x6D6C7267, // Red in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen = /* 'mlgg' */ 0x6D6C6767, // Green in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen = /* 'mlbg' */ 0x6D6C6267, // Blue in green log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue = /* 'mlrb' */ 0x6D6C7262, // Red in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue = /* 'mlgb' */ 0x6D6C6762, // Green in blue log masking parameter + bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue = /* 'mlbb' */ 0x6D6C6262, // Blue in blue log masking parameter + bmdDeckLinkFrameMetadataCintelFilmFrameRate = /* 'cffr' */ 0x63666672, // Film frame rate bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX = /* 'hdrx' */ 0x68647278, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY = /* 'hdry' */ 0x68647279, // Red display primaries in range 0.0 - 1.0 bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX = /* 'hdgx' */ 0x68646778, // Green display primaries in range 0.0 - 1.0 @@ -307,7 +371,15 @@ typedef [v1_enum] enum _BMDDeckLinkFrameMetadataID { bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance = /* 'hdml' */ 0x68646D6C, // Max display mastering luminance in range 1 cd/m2 - 65535 cd/m2 bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance = /* 'hmil' */ 0x686D696C, // Min display mastering luminance in range 0.0001 cd/m2 - 6.5535 cd/m2 bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel = /* 'mcll' */ 0x6D636C6C, // Maximum Content Light Level in range 1 cd/m2 - 65535 cd/m2 - bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel = /* 'fall' */ 0x66616C6C, // Maximum Frame Average Light Level in range 1 cd/m2 - 65535 cd/m2 + bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal = /* 'otah' */ 0x6F746168, // Horizontal offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical = /* 'otav' */ 0x6F746176, // Vertical offset (pixels) to be applied to image + bmdDeckLinkFrameMetadataCintelGainRed = /* 'LfRd' */ 0x4C665264, // Red gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainGreen = /* 'LfGr' */ 0x4C664772, // Green gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelGainBlue = /* 'LfBl' */ 0x4C66426C, // Blue gain parameter to apply after log + bmdDeckLinkFrameMetadataCintelLiftRed = /* 'GnRd' */ 0x476E5264, // Red lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftGreen = /* 'GnGr' */ 0x476E4772, // Green lift parameter to apply after log and gain + bmdDeckLinkFrameMetadataCintelLiftBlue = /* 'GnBl' */ 0x476E426C // Blue lift parameter to apply after log and gain } BMDDeckLinkFrameMetadataID; /* Enum BMDDuplexMode - Duplex for configurable ports */ @@ -343,22 +415,24 @@ typedef [v1_enum] enum _BMDDeckLinkAttributeID { BMDDeckLinkHasLTCTimecodeInput = /* 'hltc' */ 0x686C7463, BMDDeckLinkSupportsDuplexModeConfiguration = /* 'dupx' */ 0x64757078, BMDDeckLinkSupportsHDRMetadata = /* 'hdrm' */ 0x6864726D, + BMDDeckLinkSupportsColorspaceMetadata = /* 'cmet' */ 0x636D6574, /* Integers */ BMDDeckLinkMaximumAudioChannels = /* 'mach' */ 0x6D616368, - BMDDeckLinkMaximumAnalogAudioChannels = /* 'aach' */ 0x61616368, + BMDDeckLinkMaximumAnalogAudioInputChannels = /* 'iach' */ 0x69616368, + BMDDeckLinkMaximumAnalogAudioOutputChannels = /* 'aach' */ 0x61616368, BMDDeckLinkNumberOfSubDevices = /* 'nsbd' */ 0x6E736264, BMDDeckLinkSubDeviceIndex = /* 'subi' */ 0x73756269, BMDDeckLinkPersistentID = /* 'peid' */ 0x70656964, BMDDeckLinkDeviceGroupID = /* 'dgid' */ 0x64676964, BMDDeckLinkTopologicalID = /* 'toid' */ 0x746F6964, - BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, - BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, - BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, - BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, + BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, // Returns a BMDVideoConnection bit field + BMDDeckLinkAudioOutputConnections = /* 'aocn' */ 0x616F636E, // Returns a BMDAudioConnection bit field + BMDDeckLinkAudioInputConnections = /* 'aicn' */ 0x6169636E, // Returns a BMDAudioConnection bit field BMDDeckLinkVideoIOSupport = /* 'vios' */ 0x76696F73, // Returns a BMDVideoIOSupport bit field - BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, + BMDDeckLinkDeckControlConnections = /* 'dccn' */ 0x6463636E, // Returns a BMDDeckControlConnection bit field BMDDeckLinkDeviceInterface = /* 'dbus' */ 0x64627573, // Returns a BMDDeviceInterface BMDDeckLinkAudioInputRCAChannelCount = /* 'airc' */ 0x61697263, BMDDeckLinkAudioInputXLRChannelCount = /* 'aixc' */ 0x61697863, @@ -410,11 +484,14 @@ typedef [v1_enum] enum _BMDDeckLinkStatusID { bmdDeckLinkStatusReferenceSignalFlags = /* 'reff' */ 0x72656666, bmdDeckLinkStatusDuplexMode = /* 'dupx' */ 0x64757078, bmdDeckLinkStatusBusy = /* 'busy' */ 0x62757379, + bmdDeckLinkStatusInterchangeablePanelType = /* 'icpt' */ 0x69637074, + bmdDeckLinkStatusDeviceTemperature = /* 'dtmp' */ 0x64746D70, /* Flags */ bmdDeckLinkStatusVideoInputSignalLocked = /* 'visl' */ 0x7669736C, - bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C + bmdDeckLinkStatusReferenceSignalLocked = /* 'refl' */ 0x7265666C, + bmdDeckLinkStatusReceivedEDID = /* 'edid' */ 0x65646964 } BMDDeckLinkStatusID; /* Enum BMDDeckLinkVideoStatusFlags - */ @@ -433,6 +510,13 @@ typedef [v1_enum] enum _BMDDuplexStatus { bmdDuplexStatusInactive = /* 'inac' */ 0x696E6163 } BMDDuplexStatus; +/* Enum BMDPanelType - The type of interchangeable panel */ + +typedef [v1_enum] enum _BMDPanelType { + bmdPanelNotDetected = /* 'npnl' */ 0x6E706E6C, + bmdPanelTeranexMiniSmartPanel = /* 'tmsm' */ 0x746D736D +} BMDPanelType; + /* Enum BMDDeviceBusyState - Current device busy state */ [v1_enum] enum _BMDDeviceBusyState { @@ -476,12 +560,16 @@ interface IDeckLinkIterator; interface IDeckLinkAPIInformation; interface IDeckLinkOutput; interface IDeckLinkInput; +interface IDeckLinkHDMIInputEDID; interface IDeckLinkEncoderInput; interface IDeckLinkVideoFrame; interface IDeckLinkMutableVideoFrame; interface IDeckLinkVideoFrame3DExtensions; interface IDeckLinkVideoFrameMetadataExtensions; interface IDeckLinkVideoInputFrame; +interface IDeckLinkAncillaryPacket; +interface IDeckLinkAncillaryPacketIterator; +interface IDeckLinkVideoFrameAncillaryPackets; interface IDeckLinkVideoFrameAncillary; interface IDeckLinkEncoderPacket; interface IDeckLinkEncoderVideoPacket; @@ -542,7 +630,7 @@ interface IDeckLinkDiscovery; [ object, uuid(B36EB6E7-9D29-4AA8-92EF-843B87A289E8), - local, + local, helpstring("Memory allocator for video frames.") ] interface IDeckLinkMemoryAllocator : IUnknown { @@ -558,7 +646,7 @@ interface IDeckLinkDiscovery; [ object, uuid(403C681B-7F46-4A12-B993-2BB127084EE6), - local, + local, helpstring("Optional callback to allow audio samples to be pulled as required.") ] interface IDeckLinkAudioOutputCallback : IUnknown { @@ -595,7 +683,7 @@ interface IDeckLinkDiscovery; [ object, uuid(CC5C8A6E-3F2F-4B3A-87EA-FD78AF300564), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput : IUnknown { @@ -611,7 +699,7 @@ interface IDeckLinkDiscovery; HRESULT SetVideoOutputFrameMemoryAllocator([in] IDeckLinkMemoryAllocator *theAllocator); HRESULT CreateVideoFrame([in] int width, [in] int height, [in] int rowBytes, [in] BMDPixelFormat pixelFormat, [in] BMDFrameFlags flags, [out] IDeckLinkMutableVideoFrame **outFrame); - HRESULT CreateAncillaryData([in] BMDPixelFormat pixelFormat, [out] IDeckLinkVideoFrameAncillary **outBuffer); + HRESULT CreateAncillaryData([in] BMDPixelFormat pixelFormat, [out] IDeckLinkVideoFrameAncillary **outBuffer); // Use of IDeckLinkVideoFrameAncillaryPackets is preferred HRESULT DisplayVideoFrameSync([in] IDeckLinkVideoFrame *theFrame); HRESULT ScheduleVideoFrame([in] IDeckLinkVideoFrame *theFrame, [in] BMDTimeValue displayTime, [in] BMDTimeValue displayDuration, [in] BMDTimeScale timeScale); @@ -687,6 +775,19 @@ interface IDeckLinkDiscovery; HRESULT GetHardwareReferenceClock([in] BMDTimeScale desiredTimeScale, [out] BMDTimeValue *hardwareTime, [out] BMDTimeValue *timeInFrame, [out] BMDTimeValue *ticksPerFrame); }; +/* Interface IDeckLinkHDMIInputEDID - Created by QueryInterface from IDeckLink. Releasing all references will restore EDID to default */ + +[ + object, + uuid(ABBBACBC-45BC-4665-9D92-ACE6E5A97902), + helpstring("Created by QueryInterface from IDeckLink. Releasing all references will restore EDID to default") +] interface IDeckLinkHDMIInputEDID : IUnknown +{ + HRESULT SetInt([in] BMDDeckLinkHDMIInputEDIDID cfgID, [in] LONGLONG value); + HRESULT GetInt([in] BMDDeckLinkHDMIInputEDIDID cfgID, [out] LONGLONG *value); + HRESULT WriteToEDID(void); +}; + /* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */ [ @@ -729,7 +830,7 @@ interface IDeckLinkDiscovery; [ object, uuid(3F716FE0-F023-4111-BE5D-EF4414C05B17), - local, + local, helpstring("Interface to encapsulate a video frame; can be caller-implemented.") ] interface IDeckLinkVideoFrame : IUnknown { @@ -741,7 +842,7 @@ interface IDeckLinkDiscovery; HRESULT GetBytes([out] void **buffer); HRESULT GetTimecode([in] BMDTimecodeFormat format, [out] IDeckLinkTimecode **timecode); - HRESULT GetAncillaryData([out] IDeckLinkVideoFrameAncillary **ancillary); + HRESULT GetAncillaryData([out] IDeckLinkVideoFrameAncillary **ancillary); // Use of IDeckLinkVideoFrameAncillaryPackets is preferred }; /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ @@ -749,7 +850,7 @@ interface IDeckLinkDiscovery; [ object, uuid(69E2639F-40DA-4E19-B6F2-20ACE815C390), - local, + local, helpstring("Created by IDeckLinkOutput::CreateVideoFrame.") ] interface IDeckLinkMutableVideoFrame : IDeckLinkVideoFrame { @@ -766,7 +867,7 @@ interface IDeckLinkDiscovery; [ object, uuid(DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7), - local, + local, helpstring("Optional interface implemented on IDeckLinkVideoFrame to support 3D frames") ] interface IDeckLinkVideoFrame3DExtensions : IUnknown { @@ -779,7 +880,7 @@ interface IDeckLinkDiscovery; [ object, uuid(D5973DC9-6432-46D0-8F0B-2496F8A1238F), - local, + local, helpstring("Optional interface implemented on IDeckLinkVideoFrame to support frame metadata such as HDMI HDR information") ] interface IDeckLinkVideoFrameMetadataExtensions : IUnknown { @@ -794,7 +895,7 @@ interface IDeckLinkDiscovery; [ object, uuid(05CFE374-537C-4094-9A57-680525118F44), - local, + local, helpstring("Provided by the IDeckLinkVideoInput frame arrival callback.") ] interface IDeckLinkVideoInputFrame : IDeckLinkVideoFrame { @@ -802,17 +903,61 @@ interface IDeckLinkDiscovery; HRESULT GetHardwareReferenceTimestamp([in] BMDTimeScale timeScale, [out] BMDTimeValue *frameTime, [out] BMDTimeValue *frameDuration); }; -/* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ +/* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */ + +[ + object, + uuid(CC5BBF7E-029C-4D3B-9158-6000EF5E3670), + helpstring("On output, user needs to implement this interface") +] interface IDeckLinkAncillaryPacket : IUnknown +{ + + HRESULT GetBytes([in] BMDAncillaryPacketFormat format /* For output, only one format need be offered */, [out] const void **data /* Optional */, [out] unsigned int *size /* Optional */); + unsigned char GetDID(void); + unsigned char GetSDID(void); + unsigned int GetLineNumber(void); // On output, zero is auto + unsigned char GetDataStreamIndex(void); // Usually zero. Can only be 1 if non-SD and the first data stream is completely full +}; + +/* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */ + +[ + object, + uuid(3FC8994B-88FB-4C17-968F-9AAB69D964A7), + helpstring("Enumerates ancillary packets") +] interface IDeckLinkAncillaryPacketIterator : IUnknown +{ + HRESULT Next([out] IDeckLinkAncillaryPacket **packet); +}; + +/* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ + +[ + object, + uuid(6C186C0F-459E-41D8-AEE2-4812D81AEE68), + local, + helpstring("Obtained through QueryInterface() on an IDeckLinkVideoFrame object.") +] interface IDeckLinkVideoFrameAncillaryPackets : IUnknown +{ + + HRESULT GetPacketIterator([out] IDeckLinkAncillaryPacketIterator **iterator); + HRESULT GetFirstPacketByID([in] unsigned char DID, [in] unsigned char SDID, [out] IDeckLinkAncillaryPacket **packet); + HRESULT AttachPacket([in] IDeckLinkAncillaryPacket *packet); // Implement IDeckLinkAncillaryPacket to output your own + HRESULT DetachPacket([in] IDeckLinkAncillaryPacket *packet); + HRESULT DetachAllPackets(void); +}; + +/* Interface IDeckLinkVideoFrameAncillary - Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ [ object, uuid(732E723C-D1A4-4E29-9E8E-4A88797A0004), - local, - helpstring("Obtained through QueryInterface() on an IDeckLinkVideoFrame object.") + local, + helpstring("Use of IDeckLinkVideoFrameAncillaryPackets is preferred. Obtained through QueryInterface() on an IDeckLinkVideoFrame object.") ] interface IDeckLinkVideoFrameAncillary : IUnknown { - HRESULT GetBufferForVerticalBlankingLine([in] unsigned int lineNumber, [out] void **buffer); + HRESULT GetBufferForVerticalBlankingLine([in] unsigned int lineNumber, [out] void **buffer); // Pixels/rowbytes is same as display mode, except for above HD where it's 1920 pixels for UHD modes and 2048 pixels for DCI modes BMDPixelFormat GetPixelFormat(void); BMDDisplayMode GetDisplayMode(void); }; @@ -822,7 +967,7 @@ interface IDeckLinkDiscovery; [ object, uuid(B693F36C-316E-4AF1-B6C2-F389A4BCA620), - local, + local, helpstring("Interface to encapsulate an encoded packet.") ] interface IDeckLinkEncoderPacket : IUnknown { @@ -837,7 +982,7 @@ interface IDeckLinkDiscovery; [ object, uuid(4E7FD944-E8C7-4EAC-B8C0-7B77F80F5AE0), - local, + local, helpstring("Provided by the IDeckLinkEncoderInput video packet arrival callback.") ] interface IDeckLinkEncoderVideoPacket : IDeckLinkEncoderPacket { @@ -852,7 +997,7 @@ interface IDeckLinkDiscovery; [ object, uuid(49E8EDC8-693B-4E14-8EF6-12C658F5A07A), - local, + local, helpstring("Provided by the IDeckLinkEncoderInput audio packet arrival callback.") ] interface IDeckLinkEncoderAudioPacket : IDeckLinkEncoderPacket { @@ -864,7 +1009,7 @@ interface IDeckLinkDiscovery; [ object, uuid(639C8E0B-68D5-4BDE-A6D4-95F3AEAFF2E7), - local, + local, helpstring("Obtained through QueryInterface() on an IDeckLinkEncoderVideoPacket object") ] interface IDeckLinkH265NALPacket : IDeckLinkEncoderVideoPacket { @@ -878,7 +1023,7 @@ interface IDeckLinkDiscovery; [ object, uuid(E43D5870-2894-11DE-8C30-0800200C9A66), - local, + local, helpstring("Provided by the IDeckLinkInput callback.") ] interface IDeckLinkAudioInputPacket : IUnknown { @@ -892,7 +1037,7 @@ interface IDeckLinkDiscovery; [ object, uuid(B1D3F49A-85FE-4C5D-95C8-0B5D5DCCD438), - local, + local, helpstring("Screen preview callback") ] interface IDeckLinkScreenPreviewCallback : IUnknown { @@ -904,7 +1049,7 @@ interface IDeckLinkDiscovery; [ object, uuid(504E2209-CAC7-4C1A-9FB4-C5BB6274D22F), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkGLScreenPreviewHelper : IUnknown { @@ -922,7 +1067,7 @@ interface IDeckLinkDiscovery; [ object, uuid(2094B522-D1A1-40C0-9AC7-1C012218EF02), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkDX9ScreenPreviewHelper : IUnknown { @@ -937,7 +1082,7 @@ interface IDeckLinkDiscovery; [ object, uuid(b002a1ec-070d-4288-8289-bd5d36e5ff0d), - local, + local, helpstring("DeckLink Notification Callback Interface") ] interface IDeckLinkNotificationCallback : IUnknown { @@ -949,7 +1094,7 @@ interface IDeckLinkDiscovery; [ object, uuid(0a1fb207-e215-441b-9b19-6fa1575946c5), - local, + local, helpstring("DeckLink Notification interface") ] interface IDeckLinkNotification : IUnknown { @@ -962,7 +1107,7 @@ interface IDeckLinkDiscovery; [ object, uuid(ABC11843-D966-44CB-96E2-A1CB5D3135C4), - local, + local, helpstring("DeckLink Attribute interface") ] interface IDeckLinkAttributes : IUnknown { @@ -977,7 +1122,7 @@ interface IDeckLinkDiscovery; [ object, uuid(5F558200-4028-49BC-BEAC-DB3FA4A96E46), - local, + local, helpstring("DeckLink Status interface") ] interface IDeckLinkStatus : IUnknown { @@ -993,7 +1138,7 @@ interface IDeckLinkDiscovery; [ object, uuid(89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3), - local, + local, helpstring("DeckLink Keyer interface") ] interface IDeckLinkKeyer : IUnknown { @@ -1009,7 +1154,7 @@ interface IDeckLinkDiscovery; [ object, uuid(3BBCB8A2-DA2C-42D9-B5D8-88083644E99A), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkVideoConversion : IUnknown { @@ -1045,7 +1190,7 @@ interface IDeckLinkDiscovery; importlib("stdole2.tlb"); [ - uuid(1F2E109A-8F4F-49E4-9203-135595CB6FA5), + uuid(87D2693F-8D4A-45C7-B43F-10ACBA25E68F), helpstring("CDeckLinkIterator Class") ] coclass CDeckLinkIterator { @@ -1085,15 +1230,26 @@ importlib("stdole2.tlb"); }; [ - uuid(1073A05C-D885-47E9-B3C6-129B3F9F648B), + uuid(652615D4-26CD-4514-B161-2FD5072ED008), helpstring("CDeckLinkDiscovery Class") ] coclass CDeckLinkDiscovery { [default] interface IDeckLinkDiscovery; }; +[ + uuid(F891AD29-D0C2-46E9-A926-4E2D0DD8CFAD), + helpstring("CDeckLinkVideoFrameAncillaryPackets Class") +] coclass CDeckLinkVideoFrameAncillaryPackets +{ + [default] interface IDeckLinkVideoFrameAncillaryPackets; +}; + // import deprecated interfaces +#include "DeckLinkAPI_v10_9.idl" +#include "DeckLinkAPIStreaming_v10_8.idl" +#include "DeckLinkAPI_v10_8.idl" #include "DeckLinkAPI_v10_6.idl" #include "DeckLinkAPI_v10_5.idl" #include "DeckLinkAPI_v10_4.idl" diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl index 4e271e4..42a5234 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIConfiguration.idl @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -50,10 +50,6 @@ typedef [v1_enum] enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigSwapSerialRxTx = /* 'ssrt' */ 0x73737274, - /* Video Input/Output Flags */ - - bmdDeckLinkConfigUse1080pNotPsF = /* 'fpro' */ 0x6670726F, - /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066, @@ -74,6 +70,12 @@ typedef [v1_enum] enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F, bmdDeckLinkConfigDownConversionOnAllAnalogOutput = /* 'caao' */ 0x6361616F, bmdDeckLinkConfigSMPTELevelAOutput = /* 'smta' */ 0x736D7461, + bmdDeckLinkConfigRec2020Output = /* 'rec2' */ 0x72656332, // Ensure output is Rec.2020 colorspace + bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = /* 'SDQS' */ 0x53445153, + + /* Video Output Flags */ + + bmdDeckLinkConfigOutput1080pAsPsF = /* 'pfpr' */ 0x70667072, /* Video Output Integers */ @@ -102,6 +104,10 @@ typedef [v1_enum] enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigUseDedicatedLTCInput = /* 'dltc' */ 0x646C7463, // Use timecode from LTC input instead of SDI stream bmdDeckLinkConfigSDIInput3DPayloadOverride = /* '3dds' */ 0x33646473, + /* Video Input Flags */ + + bmdDeckLinkConfigCapture1080pAsPsF = /* 'cfpr' */ 0x63667072, + /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E, @@ -203,8 +209,8 @@ interface IDeckLinkEncoderConfiguration; [ object, - uuid(CB71734A-FE37-4E8D-8E13-802133A1C3F2), - local, + uuid(EF90380B-4AE5-4346-9077-E288E149F129), + local, helpstring("DeckLink Configuration interface") ] interface IDeckLinkConfiguration : IUnknown { @@ -224,7 +230,7 @@ interface IDeckLinkEncoderConfiguration; [ object, uuid(138050E5-C60A-4552-BF3F-0F358049327E), - local, + local, helpstring("DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput") ] interface IDeckLinkEncoderConfiguration : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl index cba3f2e..ef85d82 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDeckControl.idl @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl index 8b9db8e..66f65bc 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIDiscovery.idl @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl index e4f4c35..4e9eb26 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIModes.idl @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -63,12 +63,12 @@ typedef [v1_enum] enum _BMDDisplayMode { bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235, bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239, bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330, - bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, - bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, - bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530, bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539, bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz. + bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, + bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, + bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ @@ -76,19 +76,24 @@ typedef [v1_enum] enum _BMDDisplayMode { bmdModeHD720p5994 = /* 'hp59' */ 0x68703539, bmdModeHD720p60 = /* 'hp60' */ 0x68703630, - /* 2k Modes */ + /* 2K Modes */ bmdMode2k2398 = /* '2k23' */ 0x326B3233, bmdMode2k24 = /* '2k24' */ 0x326B3234, bmdMode2k25 = /* '2k25' */ 0x326B3235, - /* DCI Modes (output only) */ + /* 2K DCI Modes */ bmdMode2kDCI2398 = /* '2d23' */ 0x32643233, bmdMode2kDCI24 = /* '2d24' */ 0x32643234, bmdMode2kDCI25 = /* '2d25' */ 0x32643235, + bmdMode2kDCI2997 = /* '2d29' */ 0x32643239, + bmdMode2kDCI30 = /* '2d30' */ 0x32643330, + bmdMode2kDCI50 = /* '2d50' */ 0x32643530, + bmdMode2kDCI5994 = /* '2d59' */ 0x32643539, + bmdMode2kDCI60 = /* '2d60' */ 0x32643630, - /* 4k Modes */ + /* 4K UHD Modes */ bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233, bmdMode4K2160p24 = /* '4k24' */ 0x346B3234, @@ -99,11 +104,43 @@ typedef [v1_enum] enum _BMDDisplayMode { bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539, bmdMode4K2160p60 = /* '4k60' */ 0x346B3630, - /* DCI Modes (output only) */ + /* 4K DCI Modes */ bmdMode4kDCI2398 = /* '4d23' */ 0x34643233, bmdMode4kDCI24 = /* '4d24' */ 0x34643234, bmdMode4kDCI25 = /* '4d25' */ 0x34643235, + bmdMode4kDCI2997 = /* '4d29' */ 0x34643239, + bmdMode4kDCI30 = /* '4d30' */ 0x34643330, + bmdMode4kDCI50 = /* '4d50' */ 0x34643530, + bmdMode4kDCI5994 = /* '4d59' */ 0x34643539, + bmdMode4kDCI60 = /* '4d60' */ 0x34643630, + + /* 8K UHD Modes */ + + bmdMode8K4320p2398 = /* '8k23' */ 0x386B3233, + bmdMode8K4320p24 = /* '8k24' */ 0x386B3234, + bmdMode8K4320p25 = /* '8k25' */ 0x386B3235, + bmdMode8K4320p2997 = /* '8k29' */ 0x386B3239, + bmdMode8K4320p30 = /* '8k30' */ 0x386B3330, + bmdMode8K4320p50 = /* '8k50' */ 0x386B3530, + bmdMode8K4320p5994 = /* '8k59' */ 0x386B3539, + bmdMode8K4320p60 = /* '8k60' */ 0x386B3630, + + /* 8K DCI Modes */ + + bmdMode8kDCI2398 = /* '8d23' */ 0x38643233, + bmdMode8kDCI24 = /* '8d24' */ 0x38643234, + bmdMode8kDCI25 = /* '8d25' */ 0x38643235, + bmdMode8kDCI2997 = /* '8d29' */ 0x38643239, + bmdMode8kDCI30 = /* '8d30' */ 0x38643330, + bmdMode8kDCI50 = /* '8d50' */ 0x38643530, + bmdMode8kDCI5994 = /* '8d59' */ 0x38643539, + bmdMode8kDCI60 = /* '8d60' */ 0x38643630, + + /* RAW Modes for Cintel (input only) */ + + bmdModeCintelRAW = /* 'rwci' */ 0x72776369, // Frame size up to 4096x3072, variable frame rate + bmdModeCintelCompressedRAW = /* 'rwcc' */ 0x72776363, // Frame size up to 4096x3072, variable frame rate /* Special Modes */ @@ -136,7 +173,12 @@ typedef [v1_enum] enum _BMDPixelFormat { /* AVID DNxHR */ - bmdFormatDNxHR = /* 'AVdh' */ 0x41566468 + bmdFormatDNxHR = /* 'AVdh' */ 0x41566468, + + /* Cintel formats */ + + bmdFormat12BitRAWGRBG = /* 'r12p' */ 0x72313270, // 12-bit RAW data for bayer pattern GRBG + bmdFormat12BitRAWJPEG = /* 'r16p' */ 0x72313670 // 12-bit RAW data arranged in tiles and JPEG compressed } BMDPixelFormat; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ @@ -144,7 +186,8 @@ typedef [v1_enum] enum _BMDPixelFormat { [v1_enum] enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = 1 << 0, bmdDisplayModeColorspaceRec601 = 1 << 1, - bmdDisplayModeColorspaceRec709 = 1 << 2 + bmdDisplayModeColorspaceRec709 = 1 << 2, + bmdDisplayModeColorspaceRec2020 = 1 << 3 }; // Forward Declarations diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl index b609e5d..56855fc 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming.idl @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -77,8 +77,8 @@ typedef [v1_enum] enum _BMDStreamingEncodingFrameRate { typedef [v1_enum] enum _BMDStreamingEncodingSupport { bmdStreamingEncodingModeNotSupported = 0, - bmdStreamingEncodingModeSupported, - bmdStreamingEncodingModeSupportedWithChanges + bmdStreamingEncodingModeSupported, + bmdStreamingEncodingModeSupportedWithChanges } BMDStreamingEncodingSupport; /* Enum BMDStreamingVideoCodec - Video codecs */ @@ -346,7 +346,7 @@ interface IBMDStreamingH264NALParser; importlib("stdole2.tlb"); [ - uuid(0CAA31F6-8A26-40B0-86A4-BF58DCCA710C), + uuid(23A4EDF5-A0E5-432C-94EF-3BABB5F81C82), helpstring("CBMDStreamingDiscovery Class") ] coclass CBMDStreamingDiscovery { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming_v10_8.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming_v10_8.idl new file mode 100644 index 0000000..7d8aa5a --- /dev/null +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIStreaming_v10_8.idl @@ -0,0 +1,40 @@ +/* -LICENSE-START- +** Copyright (c) 2016 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ + + + +/* Coclasses */ + +importlib("stdole2.tlb"); + +[ + uuid(0CAA31F6-8A26-40B0-86A4-BF58DCCA710C), + helpstring("CBMDStreamingDiscovery Class") +] coclass CBMDStreamingDiscovery_v10_8 +{ + [default] interface IBMDStreamingDiscovery; +}; diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl index 35915b4..aeb52ab 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPITypes.idl @@ -1,5 +1,5 @@ /* -LICENSE-START- -** Copyright (c) 2016 Blackmagic Design +** Copyright (c) 2018 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -53,7 +53,8 @@ cpp_quote("#endif") [v1_enum] enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0, - bmdTimecodeFieldMark = 1 << 1 + bmdTimecodeFieldMark = 1 << 1, + bmdTimecodeColorFrame = 1 << 2 }; /* Enum BMDVideoConnection - Video connection types */ diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h b/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h index dfd1799..f9db7ff 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPIVersion.h @@ -7,14 +7,14 @@ * ** execute, and transmit the Software, and to prepare derivative works of the * ** Software, and to permit third-parties to whom the Software is furnished to * ** do so, all subject to the following: - * ** + * ** * ** The copyright notices in the Software and this entire statement, including * ** the above license grant, this restriction and the following disclaimer, * ** must be included in all copies of the Software, in whole or in part, and * ** all derivative works of the Software, unless such copies or derivative * ** works are solely in the form of machine-executable object code generated by * ** a source language processor. - * ** + * ** * ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -30,8 +30,8 @@ #ifndef __DeckLink_API_Version_h__ #define __DeckLink_API_Version_h__ -#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a080000 -#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.8" +#define BLACKMAGIC_DECKLINK_API_VERSION 0x0a0b0400 +#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "10.11.4" #endif // __DeckLink_API_Version_h__ diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl index 12b6250..02a58a2 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_2.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -57,7 +57,7 @@ interface IDeckLinkConfiguration_v10_2; [ object, uuid(C679A35B-610C-4D09-B748-1D0478100FC0), - local, + local, helpstring("DeckLink Configuration interface") ] interface IDeckLinkConfiguration_v10_2 : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl index 7348e49..1e89d39 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_5.idl @@ -45,7 +45,7 @@ interface IDeckLinkEncoderConfiguration_v10_5; [ object, uuid(67455668-0848-45DF-8D8E-350A77C9A028), - local, + local, helpstring("DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput") ] interface IDeckLinkEncoderConfiguration_v10_5 : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_8.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_8.idl new file mode 100644 index 0000000..c23b993 --- /dev/null +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_8.idl @@ -0,0 +1,46 @@ +/* -LICENSE-START- +** Copyright (c) 2016 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ +/* DeckLinkAPI_v10_8.idl */ + + +importlib("stdole2.tlb"); + +[ + uuid(1F2E109A-8F4F-49E4-9203-135595CB6FA5), + helpstring("CDeckLinkIterator_v10_8 Class") +] coclass CDeckLinkIterator_v10_8 +{ + [default] interface IDeckLinkIterator; +}; + +[ + uuid(1073A05C-D885-47E9-B3C6-129B3F9F648B), + helpstring("CDeckLinkDiscovery_v10_8 Class") +] coclass CDeckLinkDiscovery_v10_8 +{ + [default] interface IDeckLinkDiscovery; +}; diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_9.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_9.idl new file mode 100644 index 0000000..5506387 --- /dev/null +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v10_9.idl @@ -0,0 +1,61 @@ +/* -LICENSE-START- +** Copyright (c) 2017 Blackmagic Design +** +** Permission is hereby granted, free of charge, to any person or organization +** obtaining a copy of the software and accompanying documentation covered by +** this license (the "Software") to use, reproduce, display, distribute, +** execute, and transmit the Software, and to prepare derivative works of the +** Software, and to permit third-parties to whom the Software is furnished to +** do so, all subject to the following: +** +** The copyright notices in the Software and this entire statement, including +** the above license grant, this restriction and the following disclaimer, +** must be included in all copies of the Software, in whole or in part, and +** all derivative works of the Software, unless such copies or derivative +** works are solely in the form of machine-executable object code generated by +** a source language processor. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +** DEALINGS IN THE SOFTWARE. +** -LICENSE-END- +*/ +/* DeckLinkAPI_v10_9.idl */ + +/* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ + +typedef [v1_enum] enum _BMDDeckLinkConfigurationID_v10_9 { + + /* Video output flags */ + + bmdDeckLinkConfig1080pNotPsF_v10_9 = /* 'fpro' */ 0x6670726F, + +} BMDDeckLinkConfigurationID_v10_9; + +// Forward Declarations + +interface IDeckLinkConfiguration_v10_9; + +/* Interface IDeckLinkConfiguration_v10_9 - DeckLink Configuration interface */ + +[ + object, + uuid(CB71734A-FE37-4E8D-8E13-802133A1C3F2), + local, + helpstring("DeckLink Configuration interface") +] interface IDeckLinkConfiguration_v10_9 : IUnknown +{ + HRESULT SetFlag([in] BMDDeckLinkConfigurationID cfgID, [in] BOOL value); + HRESULT GetFlag([in] BMDDeckLinkConfigurationID cfgID, [out] BOOL *value); + HRESULT SetInt([in] BMDDeckLinkConfigurationID cfgID, [in] LONGLONG value); + HRESULT GetInt([in] BMDDeckLinkConfigurationID cfgID, [out] LONGLONG *value); + HRESULT SetFloat([in] BMDDeckLinkConfigurationID cfgID, [in] double value); + HRESULT GetFloat([in] BMDDeckLinkConfigurationID cfgID, [out] double *value); + HRESULT SetString([in] BMDDeckLinkConfigurationID cfgID, [in] BSTR value); + HRESULT GetString([in] BMDDeckLinkConfigurationID cfgID, [out] BSTR *value); + HRESULT WriteConfigurationToPreferences(void); +}; diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl index e21b5a8..0fcc88e 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_1.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -31,7 +31,7 @@ interface IDeckLinkVideoFrame_v7_1; interface IDeckLinkVideoInputFrame_v7_1; interface IDeckLinkAudioInputPacket_v7_1; - + [object, uuid(B28131B6-59AC-4857-B5AC-CD75D5883E2F), helpstring("IDeckLinkDisplayModeIterator_v7_1 enumerates over supported input/output display modes.")] interface IDeckLinkDisplayModeIterator_v7_1 : IUnknown @@ -50,21 +50,21 @@ long GetHeight (); HRESULT GetFrameRate ([out] BMDTimeValue *frameDuration, [out] BMDTimeScale *timeScale); }; - + [object, uuid(EBD01AFA-E4B0-49C6-A01D-EDB9D1B55FD9), helpstring("IDeckLinkVideoOutputCallback. Frame completion callback.")] interface IDeckLinkVideoOutputCallback_v7_1 : IUnknown { HRESULT ScheduledFrameCompleted ([in] IDeckLinkVideoFrame_v7_1* completedFrame, [in] BMDOutputFrameCompletionResult result); }; - + [object, uuid(7F94F328-5ED4-4E9F-9729-76A86BDC99CC), helpstring("IDeckLinkInputCallback_v7_1. Frame arrival callback.")] interface IDeckLinkInputCallback_v7_1 : IUnknown { HRESULT VideoInputFrameArrived ([in] IDeckLinkVideoInputFrame_v7_1* videoFrame, [in] IDeckLinkAudioInputPacket_v7_1* audioPacket); }; - + [object, uuid(AE5B3E9B-4E1E-4535-B6E8-480FF52F6CE5), local, helpstring("IDeckLinkOutput_v7_1. Created by QueryInterface from IDeckLink.")] @@ -72,11 +72,11 @@ { HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, [out] BMDDisplayModeSupport *result); HRESULT GetDisplayModeIterator ([out] IDeckLinkDisplayModeIterator_v7_1 **iterator); - + // Video output HRESULT EnableVideoOutput (BMDDisplayMode displayMode); HRESULT DisableVideoOutput (); - + HRESULT SetVideoOutputFrameMemoryAllocator ([in] IDeckLinkMemoryAllocator* theAllocator); HRESULT CreateVideoFrame (int width, int height, int rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1** outFrame); HRESULT CreateVideoFrameFromBuffer (void* buffer, int width, int height, int rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1** outFrame); @@ -84,22 +84,22 @@ HRESULT DisplayVideoFrameSync (IDeckLinkVideoFrame_v7_1* theFrame); HRESULT ScheduleVideoFrame (IDeckLinkVideoFrame_v7_1* theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale); HRESULT SetScheduledFrameCompletionCallback ([in] IDeckLinkVideoOutputCallback_v7_1* theCallback); - + // Audio output HRESULT EnableAudioOutput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned int channelCount); HRESULT DisableAudioOutput (); - + HRESULT WriteAudioSamplesSync (void* buffer, unsigned int sampleFrameCount, [out] unsigned int *sampleFramesWritten); - + HRESULT BeginAudioPreroll (); HRESULT EndAudioPreroll (); HRESULT ScheduleAudioSamples (void* buffer, unsigned int sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, [out] unsigned int *sampleFramesWritten); - + HRESULT GetBufferedAudioSampleFrameCount ( [out] unsigned int *bufferedSampleCount); HRESULT FlushBufferedAudioSamples (); - + HRESULT SetAudioCallback ( [in] IDeckLinkAudioOutputCallback* theCallback); - + // Output control HRESULT StartScheduledPlayback (BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed); HRESULT StopScheduledPlayback (BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale); @@ -112,24 +112,24 @@ { HRESULT DoesSupportVideoMode (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, [out] BMDDisplayModeSupport *result); HRESULT GetDisplayModeIterator ([out] IDeckLinkDisplayModeIterator_v7_1 **iterator); - + // Video input HRESULT EnableVideoInput (BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags); HRESULT DisableVideoInput (); - + // Audio input HRESULT EnableAudioInput (BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned int channelCount); HRESULT DisableAudioInput (); HRESULT ReadAudioSamples (void* buffer, unsigned int sampleFrameCount, [out] unsigned int *sampleFramesRead, [out] BMDTimeValue *audioPacketTime, BMDTimeScale timeScale); HRESULT GetBufferedAudioSampleFrameCount ( [out] unsigned int *bufferedSampleCount); - + // Input control HRESULT StartStreams (); HRESULT StopStreams (); HRESULT PauseStreams (); HRESULT SetCallback ([in] IDeckLinkInputCallback_v7_1* theCallback); }; - + [object, uuid(333F3A10-8C2D-43CF-B79D-46560FEEA1CE), local, helpstring("IDeckLinkVideoFrame_v7_1. Created by IDeckLinkVideoOutput::CreateVideoFrame.")] interface IDeckLinkVideoFrame_v7_1 : IUnknown @@ -141,14 +141,14 @@ BMDFrameFlags GetFlags (); HRESULT GetBytes (void* *buffer); }; - + [object, uuid(C8B41D95-8848-40EE-9B37-6E3417FB114B), local, helpstring("IDeckLinkVideoInputFrame_v7_1. Provided by the IDeckLinkVideoInput frame arrival callback.")] interface IDeckLinkVideoInputFrame_v7_1 : IDeckLinkVideoFrame_v7_1 { HRESULT GetFrameTime (BMDTimeValue *frameTime, BMDTimeValue *frameDuration, BMDTimeScale timeScale); }; - + [object, uuid(C86DE4F6-A29F-42E3-AB3A-1363E29F0788), local, helpstring("IDeckLinkAudioInputPacket_v7_1. Provided by the IDeckLinkInput callback.")] interface IDeckLinkAudioInputPacket_v7_1 : IUnknown @@ -157,4 +157,4 @@ HRESULT GetBytes (void* *buffer); HRESULT GetAudioPacketTime (BMDTimeValue *packetTime, BMDTimeScale timeScale); }; - + diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl index 9dfa497..3f3e00b 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_3.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -55,7 +55,7 @@ interface IDeckLinkVideoInputFrame_v7_3; [ object, uuid(271C65E3-C323-4344-A30F-D908BCB20AA3), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput_v7_3 : IUnknown { @@ -146,7 +146,7 @@ interface IDeckLinkVideoInputFrame_v7_3; [ object, uuid(CF317790-2894-11DE-8C30-0800200C9A66), - local, + local, helpstring("Provided by the IDeckLinkVideoInput frame arrival callback.") ] interface IDeckLinkVideoInputFrame_v7_3 : IDeckLinkVideoFrame_v7_6 { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl index 8ed7b73..c9b9b18 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_6.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -114,7 +114,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(29228142-EB8C-4141-A621-F74026450955), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput_v7_6 : IUnknown { @@ -225,7 +225,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(A8D8238E-6B18-4196-99E1-5AF717B83D32), - local, + local, helpstring("Interface to encapsulate a video frame; can be caller-implemented.") ] interface IDeckLinkVideoFrame_v7_6 : IUnknown { @@ -246,7 +246,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(46FCEE00-B4E6-43D0-91C0-023A7FCEB34F), - local, + local, helpstring("Created by IDeckLinkOutput::CreateVideoFrame.") ] interface IDeckLinkMutableVideoFrame_v7_6 : IDeckLinkVideoFrame_v7_6 { @@ -263,7 +263,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(9A74FA41-AE9F-47AC-8CF4-01F42DD59965), - local, + local, helpstring("Provided by the IDeckLinkVideoInput frame arrival callback.") ] interface IDeckLinkVideoInputFrame_v7_6 : IDeckLinkVideoFrame_v7_6 { @@ -277,7 +277,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(373F499D-4B4D-4518-AD22-6354E5A5825E), - local, + local, helpstring("Screen preview callback") ] interface IDeckLinkScreenPreviewCallback_v7_6 : IUnknown { @@ -290,7 +290,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(BA575CD9-A15E-497B-B2C2-F9AFE7BE4EBA), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkGLScreenPreviewHelper_v7_6 : IUnknown { @@ -308,7 +308,7 @@ interface IDeckLinkConfiguration_v7_6; [ object, uuid(3EB504C9-F97D-40FE-A158-D407D48CB53B), - local, + local, helpstring("Created with CoCreateInstance().") ] interface IDeckLinkVideoConversion_v7_6 : IUnknown { diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl index 722984a..b222899 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v7_9.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl index a2da181..079f60b 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_0.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl index 59b9a21..5d163ae 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v8_1.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl index e918e04..6c6179a 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_2.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT diff --git a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl index a5e1439..c908e42 100644 --- a/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl +++ b/plugins/decklink/win/decklink-sdk/DeckLinkAPI_v9_9.idl @@ -7,14 +7,14 @@ ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: -** +** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. -** +** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT @@ -33,7 +33,7 @@ [ object, uuid(A3EF0963-0862-44ED-92A9-EE89ABF431C7), - local, + local, helpstring("Created by QueryInterface from IDeckLink.") ] interface IDeckLinkOutput_v9_9 : IUnknown { diff --git a/plugins/image-source/color-source.c b/plugins/image-source/color-source.c index 14f3b01..7cfa159 100644 --- a/plugins/image-source/color-source.c +++ b/plugins/image-source/color-source.c @@ -99,9 +99,12 @@ static uint32_t color_source_getheight(void *data) static void color_source_defaults(obs_data_t *settings) { + struct obs_video_info ovi; + obs_get_video_info(&ovi); + obs_data_set_default_int(settings, "color", 0xFFFFFFFF); - obs_data_set_default_int(settings, "width", 400); - obs_data_set_default_int(settings, "height", 400); + obs_data_set_default_int(settings, "width", ovi.base_width); + obs_data_set_default_int(settings, "height", ovi.base_height); } struct obs_source_info color_source_info = { diff --git a/plugins/image-source/data/locale/ar-SA.ini b/plugins/image-source/data/locale/ar-SA.ini index cac5ca7..2414988 100644 --- a/plugins/image-source/data/locale/ar-SA.ini +++ b/plugins/image-source/data/locale/ar-SA.ini @@ -3,4 +3,7 @@ File="ملف الصورة" UnloadWhenNotShowing="إلغاء تحميل الصورة إذا لم تظهر" +ColorSource.Color="اللون" +ColorSource.Width="العرض" +ColorSource.Height="الإرتفاع" diff --git a/plugins/image-source/data/locale/bg-BG.ini b/plugins/image-source/data/locale/bg-BG.ini index 44df8ff..b061182 100644 --- a/plugins/image-source/data/locale/bg-BG.ini +++ b/plugins/image-source/data/locale/bg-BG.ini @@ -1,5 +1,23 @@ ImageInput="Изображение" File="Файл с изображение" +SlideShow.TransitionSpeed="Бързина на прехода (милисекунди)" +SlideShow.SlideTime="Време между слайдовете (милисекунди)" +SlideShow.Files="Файлове с изображения" +SlideShow.CustomSize.Auto="Автоматично" +SlideShow.Randomize="Произволно изпълнение" +SlideShow.Loop="Повтаряне" +SlideShow.Transition="Преход" +SlideShow.Transition.Cut="Изрязване" +SlideShow.Transition.Fade="Затъмняване" +SlideShow.PlaybackBehavior.AlwaysPlay="Винаги да се пуска, дори когато не се вижда" +SlideShow.SlideMode.Auto="Автоматично" +SlideShow.PlayPause="Пускане/пауза" +SlideShow.Restart="Рестартиране" +SlideShow.Stop="Спиране" +ColorSource="Източник за цвят" +ColorSource.Color="Цвят" +ColorSource.Width="Широчина" +ColorSource.Height="Височина" diff --git a/plugins/image-source/data/locale/da-DK.ini b/plugins/image-source/data/locale/da-DK.ini index 3623fab..8466ef2 100644 --- a/plugins/image-source/data/locale/da-DK.ini +++ b/plugins/image-source/data/locale/da-DK.ini @@ -2,27 +2,27 @@ ImageInput="Billede" File="Billedfil" UnloadWhenNotShowing="Fjern billede fra hukommelsen når det ikke vises" -SlideShow="Billede diasshow" -SlideShow.TransitionSpeed="Overgangshastighed (millisekunder)" -SlideShow.SlideTime="Tid mellem dias (millisekunder)" +SlideShow="Billedediasshow" +SlideShow.TransitionSpeed="Overgangshastighed (ms)" +SlideShow.SlideTime="Tid mellem dias (ms)" SlideShow.Files="Billedfiler" SlideShow.CustomSize="Afgrænsningsstørrelse/Formatforhold" SlideShow.CustomSize.Auto="Automatisk" SlideShow.Randomize="Tilfældig afspilning" -SlideShow.Loop="Gentagelse" +SlideShow.Loop="Løkke" SlideShow.Transition="Overgang" SlideShow.Transition.Cut="Klip" -SlideShow.Transition.Fade="Overgang" +SlideShow.Transition.Fade="Toning" SlideShow.Transition.Swipe="Stryg" SlideShow.Transition.Slide="Glide" SlideShow.PlaybackBehavior="Synlighedsadfærd" SlideShow.PlaybackBehavior.StopRestart="Stop når ikke synlig, genstart når synlig" SlideShow.PlaybackBehavior.PauseUnpause="Sæt på pause når ikke synlig, genoptag når synlig" -SlideShow.PlaybackBehavior.AlwaysPlay="Afspil altid også når usynlig" -SlideShow.SlideMode="Diasshowtilstand" +SlideShow.PlaybackBehavior.AlwaysPlay="Afspil altid, også når usynlig" +SlideShow.SlideMode="Diastilstand" SlideShow.SlideMode.Auto="Automatisk" -SlideShow.SlideMode.Manual="Manuel (styr diasshow via genvejstaster)" -SlideShow.PlayPause="Afspil/pause" +SlideShow.SlideMode.Manual="Manuelt (styr diasshow via genvejstaster)" +SlideShow.PlayPause="Afspil/Pause" SlideShow.Restart="Genstart" SlideShow.Stop="Stop" SlideShow.NextSlide="Næste dias" diff --git a/plugins/image-source/data/locale/de-DE.ini b/plugins/image-source/data/locale/de-DE.ini index 0914f65..435b868 100644 --- a/plugins/image-source/data/locale/de-DE.ini +++ b/plugins/image-source/data/locale/de-DE.ini @@ -1,12 +1,12 @@ ImageInput="Bild" File="Bilddatei" -UnloadWhenNotShowing="Entlade Bild, wenn es nicht angezeigt wird" +UnloadWhenNotShowing="Bild entladen, wenn es nicht angezeigt wird" SlideShow="Diashow" SlideShow.TransitionSpeed="Übergangsgeschwindigkeit (Millisekunden)" SlideShow.SlideTime="Zeit zwischen Bildern (Millisekunden)" SlideShow.Files="Bilddateien" -SlideShow.CustomSize="Rahmen Größe/Seitenverhältnis" +SlideShow.CustomSize="Rahmengröße/Seitenverhältnis" SlideShow.CustomSize.Auto="Automatisch" SlideShow.Randomize="Zufällige Wiedergabe" SlideShow.Loop="Endlosschleife" @@ -16,14 +16,14 @@ SlideShow.Transition.Fade="Überblenden" SlideShow.Transition.Swipe="Swipe" SlideShow.Transition.Slide="Slide" SlideShow.PlaybackBehavior="Sichtbarkeitsverhalten" -SlideShow.PlaybackBehavior.StopRestart="Anhalten wenn nicht sichtbar, neu starten wenn sichtbar" -SlideShow.PlaybackBehavior.PauseUnpause="Pausieren wenn nicht sichtbar, fortsetzen wenn sichtbar" +SlideShow.PlaybackBehavior.StopRestart="Anhalten, wenn nicht sichtbar, neustarten, wenn sichtbar" +SlideShow.PlaybackBehavior.PauseUnpause="Pausieren, wenn nicht sichtbar, fortsetzen, wenn sichtbar" SlideShow.PlaybackBehavior.AlwaysPlay="Immer abspielen, auch wenn nicht sichtbar" SlideShow.SlideMode="Diashowmodus" SlideShow.SlideMode.Auto="Automatisch" SlideShow.SlideMode.Manual="Manuell (Hotkeys verwenden, um Diashow zu steuern)" -SlideShow.PlayPause="Abspielen/Pausieren" -SlideShow.Restart="Neu starten" +SlideShow.PlayPause="Abspielen/pausieren" +SlideShow.Restart="Neustarten" SlideShow.Stop="Stop" SlideShow.NextSlide="Nächstes Bild" SlideShow.PreviousSlide="Vorheriges Bild" diff --git a/plugins/image-source/data/locale/fa-IR.ini b/plugins/image-source/data/locale/fa-IR.ini new file mode 100644 index 0000000..ae1e29a --- /dev/null +++ b/plugins/image-source/data/locale/fa-IR.ini @@ -0,0 +1,33 @@ +ImageInput="تصویر" +File="پوشه تصویر" +UnloadWhenNotShowing="لغو بارگیری عکس زمانی که نشان داده نشد" + +SlideShow="نمایش اسلایدی تصویر" +SlideShow.TransitionSpeed="سرعت انتقال (میلی ثانیه)" +SlideShow.SlideTime="زمان بین اسلاید (میلی ثانیه)" +SlideShow.Files="پوشه تصاویر" +SlideShow.CustomSize.Auto="خودکار" +SlideShow.Randomize="پخش تصادفی" +SlideShow.Loop="چرخه" +SlideShow.Transition="انتقال" +SlideShow.Transition.Cut="برش" +SlideShow.Transition.Fade="محو شدن" +SlideShow.Transition.Swipe="کشیدن" +SlideShow.Transition.Slide="اسلاید" +SlideShow.PlaybackBehavior="کنش های دیداری" +SlideShow.PlaybackBehavior.StopRestart="توقف زمانی که قابل مشاهده نیست، راه اندازی مجدد زمانی که قابل مشاهده است" +SlideShow.PlaybackBehavior.PauseUnpause="توقف زمانی که قابل مشاهده نیست، راه اندازی مجدد زمانی که قابل مشاهده است" +SlideShow.SlideMode="حالت اسلاید" +SlideShow.SlideMode.Auto="خودکار" +SlideShow.PlayPause="پخش/توقف" +SlideShow.Restart="راه اندازی مجدد" +SlideShow.Stop="توقف" +SlideShow.NextSlide="اسلاید بعدی" +SlideShow.PreviousSlide="اسلاید قبلی" +SlideShow.HideWhenDone="پنهان کردن هنگامی که نمایش پرده ای انجام می شود" + +ColorSource="رنگ منبع" +ColorSource.Color="رنگ" +ColorSource.Width="عرض" +ColorSource.Height="ارتفاع" + diff --git a/plugins/image-source/data/locale/fr-FR.ini b/plugins/image-source/data/locale/fr-FR.ini index edb8113..936d9b8 100644 --- a/plugins/image-source/data/locale/fr-FR.ini +++ b/plugins/image-source/data/locale/fr-FR.ini @@ -4,9 +4,9 @@ UnloadWhenNotShowing="Décharger l'image quand elle n'est pas affichée" SlideShow="Diaporama" SlideShow.TransitionSpeed="Vitesse de transition (millisecondes)" -SlideShow.SlideTime="Temps entre chaque diapositive (millisecondes)" +SlideShow.SlideTime="Temps entre chaque diapositive (en millisecondes)" SlideShow.Files="Fichiers image" -SlideShow.CustomSize="Taille Limite/Ratio d'aspect" +SlideShow.CustomSize="Taille du Cadre/Rapport d'aspect" SlideShow.CustomSize.Auto="Automatique" SlideShow.Randomize="Lecture aléatoire" SlideShow.Loop="Boucle" @@ -18,11 +18,11 @@ SlideShow.Transition.Slide="Glissement" SlideShow.PlaybackBehavior="Comportement de Visibilité" SlideShow.PlaybackBehavior.StopRestart="Arrêter si non visible, redémarrer si visible" SlideShow.PlaybackBehavior.PauseUnpause="Suspendre si non visible, reprendre si visible" -SlideShow.PlaybackBehavior.AlwaysPlay="Toujours jouer même lorsqu'elle n'est pas visible" +SlideShow.PlaybackBehavior.AlwaysPlay="Toujours jouer même si non visible" SlideShow.SlideMode="Mode Diapo" SlideShow.SlideMode.Auto="Automatique" SlideShow.SlideMode.Manual="Manuel (utiliser les raccourcis clavier pour contrôler le diapo)" -SlideShow.PlayPause="Lire/Pause" +SlideShow.PlayPause="Lecture/Pause" SlideShow.Restart="Relancer" SlideShow.Stop="Arrêter" SlideShow.NextSlide="Diapo suivante" diff --git a/plugins/image-source/data/locale/it-IT.ini b/plugins/image-source/data/locale/it-IT.ini index 5b1f5ef..aae5605 100644 --- a/plugins/image-source/data/locale/it-IT.ini +++ b/plugins/image-source/data/locale/it-IT.ini @@ -1,35 +1,35 @@ ImageInput="Immagine" -File="File Immagine" -UnloadWhenNotShowing="Non caricare immagine se non si vede" +File="File immagine" +UnloadWhenNotShowing="Disattiva l'immagine quando non è visibile" -SlideShow="Presentazione immagini" -SlideShow.TransitionSpeed="Velocità di transizione (millisecondi)" -SlideShow.SlideTime="Tempo tra le diapositive (millisecondi)" -SlideShow.Files="Files Immagini" +SlideShow="Presentazione di immagini" +SlideShow.TransitionSpeed="Velocità di transizione (in millisecondi)" +SlideShow.SlideTime="Tempo tra le diapositive (in millisecondi)" +SlideShow.Files="File di immagini" SlideShow.CustomSize="Dimensioni/proporzioni" -SlideShow.CustomSize.Auto="Automatico" -SlideShow.Randomize="Randomizzare la riproduzione" -SlideShow.Loop="Loop" +SlideShow.CustomSize.Auto="Automatiche" +SlideShow.Randomize="Riproduzione casuale" +SlideShow.Loop="Ripetizione" SlideShow.Transition="Transizione" SlideShow.Transition.Cut="Taglio" SlideShow.Transition.Fade="Dissolvenza" SlideShow.Transition.Swipe="Scorri" SlideShow.Transition.Slide="Scivola" SlideShow.PlaybackBehavior="Comportamento visibilità" -SlideShow.PlaybackBehavior.StopRestart="Interrompi quando non visibile, riavvia quando visibile" +SlideShow.PlaybackBehavior.StopRestart="Interrompi quando non visibile, ricomincia quando visibile" SlideShow.PlaybackBehavior.PauseUnpause="Pausa quando non visibile, riprendi quando visibile" SlideShow.PlaybackBehavior.AlwaysPlay="Continua sempre anche quando non visibile" -SlideShow.SlideMode="Modalità Slide" -SlideShow.SlideMode.Auto="Automatico" -SlideShow.SlideMode.Manual="Manuale (usa i tasti di scelta rapida per controllare la presentazione)" -SlideShow.PlayPause="Play/Pausa" -SlideShow.Restart="Riavvia" -SlideShow.Stop="Stop" -SlideShow.NextSlide="Prossima Slide" -SlideShow.PreviousSlide="Slide Precedente" +SlideShow.SlideMode="Modalità diapositiva" +SlideShow.SlideMode.Auto="Automatica" +SlideShow.SlideMode.Manual="Manuale (usa le scorciatoie per controllare la presentazione)" +SlideShow.PlayPause="Riproduci/pausa" +SlideShow.Restart="Ricomincia" +SlideShow.Stop="Interrompi" +SlideShow.NextSlide="Diapositiva successiva" +SlideShow.PreviousSlide="Diapositiva precedente" SlideShow.HideWhenDone="Nascondi quando la presentazione è terminata" -ColorSource="Origine del colore" +ColorSource="Fonte di colore" ColorSource.Color="Colore" ColorSource.Width="Larghezza" ColorSource.Height="Altezza" diff --git a/plugins/image-source/data/locale/mn-MN.ini b/plugins/image-source/data/locale/mn-MN.ini new file mode 100644 index 0000000..272950e --- /dev/null +++ b/plugins/image-source/data/locale/mn-MN.ini @@ -0,0 +1,22 @@ +ImageInput="Зураг" + +SlideShow.CustomSize.Auto="Aвтомат" +SlideShow.Randomize="Тоглуулагчыг түүвэрээр ажилууллах" +SlideShow.Loop="Давталт" +SlideShow.Transition="Шилжилт" +SlideShow.Transition.Cut="Шууд" +SlideShow.Transition.Fade="Алга болно" +SlideShow.Transition.Swipe="Хажуунаас" +SlideShow.Transition.Slide="Гулгаж" +SlideShow.PlaybackBehavior="Харагдах байдал" +SlideShow.PlaybackBehavior.StopRestart="Ил харагдахгүй бол зогсоох, харагдах үед дахин эхлүүлнэ" +SlideShow.PlaybackBehavior.PauseUnpause="Ил харагдахгүй бол пауз авах, харагдах үед паузаа болих" +SlideShow.PlaybackBehavior.AlwaysPlay="Ил харагдахгүй бол үргэлжлүүлэн тоглуулах" +SlideShow.SlideMode.Auto="Aвтомат" +SlideShow.PlayPause="Тоглуулах/Түр зогсоох" +SlideShow.Stop="Зогсоох" + +ColorSource.Color="Өнгө" +ColorSource.Width="Өргөн" +ColorSource.Height="Өндөр" + diff --git a/plugins/image-source/data/locale/pt-PT.ini b/plugins/image-source/data/locale/pt-PT.ini index 01eec59..46de88f 100644 --- a/plugins/image-source/data/locale/pt-PT.ini +++ b/plugins/image-source/data/locale/pt-PT.ini @@ -6,11 +6,20 @@ SlideShow="Imagens em Deslize" SlideShow.TransitionSpeed="Velocidade de transição (milissegundos)" SlideShow.SlideTime="Tempo entre Deslizes (milissegundos)" SlideShow.Files="Arquivos de Imagem" +SlideShow.CustomSize.Auto="Automático" SlideShow.Randomize="Reprodução aleatória" +SlideShow.Loop="Loop" SlideShow.Transition="Transição" SlideShow.Transition.Cut="Cortar" SlideShow.Transition.Fade="Desvanecer" SlideShow.Transition.Swipe="Deslizar" SlideShow.Transition.Slide="Deslize" +SlideShow.PlaybackBehavior.AlwaysPlay="Reproduzir sempre, mesmo quando não está visível" +SlideShow.SlideMode.Auto="Automático" +SlideShow.PlayPause="Play/Pausa" +SlideShow.Stop="Stop" +ColorSource.Color="Cor" +ColorSource.Width="Largura" +ColorSource.Height="Altura" diff --git a/plugins/image-source/data/locale/ro-RO.ini b/plugins/image-source/data/locale/ro-RO.ini index 12e7f38..9619905 100644 --- a/plugins/image-source/data/locale/ro-RO.ini +++ b/plugins/image-source/data/locale/ro-RO.ini @@ -2,19 +2,25 @@ ImageInput="Imagine" File="Fișier imagine" UnloadWhenNotShowing="Eliberează din memorie imaginea când nu este afișată" -SlideShow="Slide Show de imagini" -SlideShow.TransitionSpeed="Viteza de tranziție (milisecunde)" +SlideShow="Diaporamă" +SlideShow.TransitionSpeed="Viteză de tranziție (milisecunde)" SlideShow.SlideTime="Timpul dintre diapozitive (milisecunde)" -SlideShow.Files="Fișiere Imagine" +SlideShow.Files="Fișiere imagine" SlideShow.CustomSize.Auto="Automat" SlideShow.Loop="Buclă" SlideShow.Transition="Tranziție" SlideShow.Transition.Cut="Decupare" -SlideShow.Transition.Slide="Diapozitiv" +SlideShow.Transition.Fade="Estompare" +SlideShow.Transition.Swipe="Glisare" +SlideShow.Transition.Slide="Culisare" SlideShow.SlideMode.Auto="Automat" -SlideShow.NextSlide="Următorul diapozitiv" -SlideShow.PreviousSlide="Diapozitiv anterior" +SlideShow.PlayPause="Redă/Pune pe pauză" +SlideShow.Restart="Repornește" +SlideShow.Stop="Oprește" +SlideShow.NextSlide="Diapozitivul următor" +SlideShow.PreviousSlide="Diapozitivul anterior" +ColorSource="Sursă de culoare" ColorSource.Color="Culoare" ColorSource.Width="Lățime" ColorSource.Height="Înălțime" diff --git a/plugins/image-source/data/locale/ru-RU.ini b/plugins/image-source/data/locale/ru-RU.ini index b74e4ee..3f626a5 100644 --- a/plugins/image-source/data/locale/ru-RU.ini +++ b/plugins/image-source/data/locale/ru-RU.ini @@ -11,7 +11,7 @@ SlideShow.CustomSize.Auto="Автоматически" SlideShow.Randomize="Случайное воспроизведение" SlideShow.Loop="Повтор" SlideShow.Transition="Переход" -SlideShow.Transition.Cut="Обрезка" +SlideShow.Transition.Cut="Обрезать" SlideShow.Transition.Fade="Затухание" SlideShow.Transition.Swipe="Перемещение" SlideShow.Transition.Slide="Сдвиг" diff --git a/plugins/image-source/data/locale/sr-CS.ini b/plugins/image-source/data/locale/sr-CS.ini index 579ba01..64ad4f4 100644 --- a/plugins/image-source/data/locale/sr-CS.ini +++ b/plugins/image-source/data/locale/sr-CS.ini @@ -6,11 +6,31 @@ SlideShow="Prikazivanje slajdova" SlideShow.TransitionSpeed="Brzina prelaza (milisekunde)" SlideShow.SlideTime="Vreme između slajdova (milisekunde)" SlideShow.Files="Datoteke slika" +SlideShow.CustomSize="Veličina/proporcija" +SlideShow.CustomSize.Auto="Automatska" SlideShow.Randomize="Nasumična reprodukcija" +SlideShow.Loop="Ponavljaj" SlideShow.Transition="Prelaz" SlideShow.Transition.Cut="Isecanje" SlideShow.Transition.Fade="Zatamnjenje" SlideShow.Transition.Swipe="Prevlačenje" SlideShow.Transition.Slide="Klizanje" +SlideShow.PlaybackBehavior="Funkcionisanje u zavisnosti od vidljivosti" +SlideShow.PlaybackBehavior.StopRestart="Zaustavi kada nije vidljiv, počni ispočetka kada je vidljiv" +SlideShow.PlaybackBehavior.PauseUnpause="Pauziraj kada nije vidljiv, nastavi kada postane vidljiv" +SlideShow.PlaybackBehavior.AlwaysPlay="Uvek emituj, čak i kada nije vidljiv" +SlideShow.SlideMode="Slajd režim" +SlideShow.SlideMode.Auto="Automatski" +SlideShow.SlideMode.Manual="Ručni (Koristite prečice na tastaturi da kontrolišete prezentaciju)" +SlideShow.PlayPause="Pusti/Pauziraj" +SlideShow.Restart="Počni ispočetka" +SlideShow.Stop="Zaustavi" +SlideShow.NextSlide="Sledeći slajd" +SlideShow.PreviousSlide="Prethodni slajd" +SlideShow.HideWhenDone="Sakrij kada se prezentacija završi" +ColorSource="Izvor boje" +ColorSource.Color="Boja" +ColorSource.Width="Širina" +ColorSource.Height="Visina" diff --git a/plugins/image-source/data/locale/sr-SP.ini b/plugins/image-source/data/locale/sr-SP.ini index 1101941..8e4d21a 100644 --- a/plugins/image-source/data/locale/sr-SP.ini +++ b/plugins/image-source/data/locale/sr-SP.ini @@ -6,11 +6,31 @@ SlideShow="Приказивање слајдова" SlideShow.TransitionSpeed="Брзина прелаза (милисекунде)" SlideShow.SlideTime="Време између слајдова (милисекунде)" SlideShow.Files="Датотеке слика" +SlideShow.CustomSize="Величина/пропорција" +SlideShow.CustomSize.Auto="Аутоматска" SlideShow.Randomize="Насумична репродукција" +SlideShow.Loop="Понављај" SlideShow.Transition="Прелаз" SlideShow.Transition.Cut="Исецање" SlideShow.Transition.Fade="Затамњење" SlideShow.Transition.Swipe="Превлачење" SlideShow.Transition.Slide="Клизање" +SlideShow.PlaybackBehavior="Функционисање у зависности од видљивости" +SlideShow.PlaybackBehavior.StopRestart="Заустави када није видљив, почни испочетка када је видљив" +SlideShow.PlaybackBehavior.PauseUnpause="Паузирај када није видљив, настави када постане видљив" +SlideShow.PlaybackBehavior.AlwaysPlay="Увек емитуј, чак и када није видљив" +SlideShow.SlideMode="Слајд режим" +SlideShow.SlideMode.Auto="Аутоматски" +SlideShow.SlideMode.Manual="Ручни (користите пречице на тастатури да контролишете презентацију)" +SlideShow.PlayPause="Пусти/Паузирај" +SlideShow.Restart="Почни испочетка" +SlideShow.Stop="Заустави" +SlideShow.NextSlide="Следећи слајд" +SlideShow.PreviousSlide="Претходни слајд" +SlideShow.HideWhenDone="Сакриј када се презентација заврши" +ColorSource="Извор боје" +ColorSource.Color="Боја" +ColorSource.Width="Ширина" +ColorSource.Height="Висина" diff --git a/plugins/image-source/data/locale/zh-CN.ini b/plugins/image-source/data/locale/zh-CN.ini index 1942e61..2ea81e6 100644 --- a/plugins/image-source/data/locale/zh-CN.ini +++ b/plugins/image-source/data/locale/zh-CN.ini @@ -3,17 +3,17 @@ File="图像文件" UnloadWhenNotShowing="当不显示时卸载图像" SlideShow="图像幻灯片放映" -SlideShow.TransitionSpeed="过渡速度(毫秒)" +SlideShow.TransitionSpeed="转场速度(毫秒)" SlideShow.SlideTime="幻灯片之间时间(毫秒)" SlideShow.Files="图像文件" -SlideShow.CustomSize="边框大小/高宽比" +SlideShow.CustomSize="边框大小/宽高比" SlideShow.CustomSize.Auto="自动" SlideShow.Randomize="随机播放" SlideShow.Loop="循环" -SlideShow.Transition="转换" +SlideShow.Transition="转换特效" SlideShow.Transition.Cut="剪切" SlideShow.Transition.Fade="淡出" -SlideShow.Transition.Swipe="滑动" +SlideShow.Transition.Swipe="滑出" SlideShow.Transition.Slide="幻灯片" SlideShow.PlaybackBehavior="可见性的行为" SlideShow.PlaybackBehavior.StopRestart="不可见时停止, 可见时重新开始" @@ -27,7 +27,7 @@ SlideShow.Restart="重新开始" SlideShow.Stop="停止" SlideShow.NextSlide="下一张幻灯片" SlideShow.PreviousSlide="上一张幻灯片" -SlideShow.HideWhenDone="幻灯片完成时隐藏" +SlideShow.HideWhenDone="幻灯片放映完成后隐藏" ColorSource="色源" ColorSource.Color="色彩" diff --git a/plugins/image-source/image-source.c b/plugins/image-source/image-source.c index b464325..1c3fa34 100644 --- a/plugins/image-source/image-source.c +++ b/plugins/image-source/image-source.c @@ -25,7 +25,7 @@ struct image_source { uint64_t last_time; bool active; - gs_image_file_t image; + gs_image_file2_t if2; }; @@ -48,20 +48,20 @@ static void image_source_load(struct image_source *context) char *file = context->file; obs_enter_graphics(); - gs_image_file_free(&context->image); + gs_image_file2_free(&context->if2); obs_leave_graphics(); if (file && *file) { debug("loading texture '%s'", file); context->file_timestamp = get_modified_timestamp(file); - gs_image_file_init(&context->image, file); + gs_image_file2_init(&context->if2, file); context->update_time_elapsed = 0; obs_enter_graphics(); - gs_image_file_init_texture(&context->image); + gs_image_file2_init_texture(&context->if2); obs_leave_graphics(); - if (!context->image.loaded) + if (!context->if2.image.loaded) warn("failed to load texture '%s'", file); } } @@ -69,7 +69,7 @@ static void image_source_load(struct image_source *context) static void image_source_unload(struct image_source *context) { obs_enter_graphics(); - gs_image_file_free(&context->image); + gs_image_file2_free(&context->if2); obs_leave_graphics(); } @@ -135,26 +135,26 @@ static void image_source_destroy(void *data) static uint32_t image_source_getwidth(void *data) { struct image_source *context = data; - return context->image.cx; + return context->if2.image.cx; } static uint32_t image_source_getheight(void *data) { struct image_source *context = data; - return context->image.cy; + return context->if2.image.cy; } static void image_source_render(void *data, gs_effect_t *effect) { struct image_source *context = data; - if (!context->image.texture) + if (!context->if2.image.texture) return; gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), - context->image.texture); - gs_draw_sprite(context->image.texture, 0, - context->image.cx, context->image.cy); + context->if2.image.texture); + gs_draw_sprite(context->if2.image.texture, 0, + context->if2.image.cx, context->if2.image.cy); } static void image_source_tick(void *data, float seconds) @@ -175,20 +175,20 @@ static void image_source_tick(void *data, float seconds) if (obs_source_active(context->source)) { if (!context->active) { - if (context->image.is_animated_gif) + if (context->if2.image.is_animated_gif) context->last_time = frame_time; context->active = true; } } else { if (context->active) { - if (context->image.is_animated_gif) { - context->image.cur_frame = 0; - context->image.cur_loop = 0; - context->image.cur_time = 0; + if (context->if2.image.is_animated_gif) { + context->if2.image.cur_frame = 0; + context->if2.image.cur_loop = 0; + context->if2.image.cur_time = 0; obs_enter_graphics(); - gs_image_file_update_texture(&context->image); + gs_image_file2_update_texture(&context->if2); obs_leave_graphics(); } @@ -198,13 +198,13 @@ static void image_source_tick(void *data, float seconds) return; } - if (context->last_time && context->image.is_animated_gif) { + if (context->last_time && context->if2.image.is_animated_gif) { uint64_t elapsed = frame_time - context->last_time; - bool updated = gs_image_file_tick(&context->image, elapsed); + bool updated = gs_image_file2_tick(&context->if2, elapsed); if (updated) { obs_enter_graphics(); - gs_image_file_update_texture(&context->image); + gs_image_file2_update_texture(&context->if2); obs_leave_graphics(); } } @@ -214,12 +214,14 @@ static void image_source_tick(void *data, float seconds) static const char *image_filter = - "All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif);;" + "All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif *.psd);;" "BMP Files (*.bmp);;" "Targa Files (*.tga);;" "PNG Files (*.png);;" "JPEG Files (*.jpeg *.jpg);;" - "GIF Files (*.gif)"; + "GIF Files (*.gif);;" + "PSD Files (*.psd);;" + "All Files (*.*)"; static obs_properties_t *image_source_properties(void *data) { @@ -248,6 +250,12 @@ static obs_properties_t *image_source_properties(void *data) return props; } +uint64_t image_source_get_memory_usage(void *data) +{ + struct image_source *s = data; + return s->if2.mem_usage; +} + static struct obs_source_info image_source_info = { .id = "image_source", .type = OBS_SOURCE_TYPE_INPUT, @@ -268,6 +276,10 @@ static struct obs_source_info image_source_info = { OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("image-source", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Image/color/slideshow sources"; +} extern struct obs_source_info slideshow_info; extern struct obs_source_info color_source_info; diff --git a/plugins/image-source/obs-slideshow.c b/plugins/image-source/obs-slideshow.c index e8316e9..4dc1038 100644 --- a/plugins/image-source/obs-slideshow.c +++ b/plugins/image-source/obs-slideshow.c @@ -57,6 +57,11 @@ /* ------------------------------------------------------------------------- */ +extern uint64_t image_source_get_memory_usage(void *data); + +#define BYTES_TO_MBYTES (1024 * 1024) +#define MAX_MEM_USAGE (250 * BYTES_TO_MBYTES) + struct image_file_data { char *path; obs_source_t *source; @@ -91,6 +96,7 @@ struct slideshow { uint32_t cx; uint32_t cy; + uint64_t mem_usage; pthread_mutex_t mutex; DARRAY(struct image_file_data) files; @@ -203,6 +209,9 @@ static void add_file(struct slideshow *ss, struct darray *array, if (new_cx > *cx) *cx = new_cx; if (new_cy > *cy) *cy = new_cy; + + void *source_data = obs_obj_get_data(new_source); + ss->mem_usage += image_source_get_memory_usage(source_data); } *array = new_files.da; @@ -308,6 +317,8 @@ static void ss_update(void *data, obs_data_t *settings) /* ------------------------------------- */ /* create new list of sources */ + ss->mem_usage = 0; + for (size_t i = 0; i < count; i++) { obs_data_t *item = obs_data_array_item(array, i); const char *path = obs_data_get_string(item, "value"); @@ -335,6 +346,9 @@ static void ss_update(void *data, obs_data_t *settings) dstr_cat(&dir_path, ent->d_name); add_file(ss, &new_files.da, dir_path.array, &cx, &cy); + + if (ss->mem_usage >= MAX_MEM_USAGE) + break; } dstr_free(&dir_path); @@ -344,6 +358,9 @@ static void ss_update(void *data, obs_data_t *settings) } obs_data_release(item); + + if (ss->mem_usage >= MAX_MEM_USAGE) + break; } /* ------------------------------------- */ @@ -358,10 +375,15 @@ static void ss_update(void *data, obs_data_t *settings) ss->transition = new_tr; } - if (new_duration < 50) - new_duration = 50; - if (new_speed > (new_duration - 50)) - new_speed = new_duration - 50; + if (strcmp(tr_name, "cut_transition") != 0) { + if (new_duration < 100) + new_duration = 100; + + new_duration += new_speed; + } else { + if (new_duration < 50) + new_duration = 50; + } ss->tr_speed = new_speed; ss->tr_name = tr_name; @@ -475,7 +497,7 @@ static void ss_next_slide(void *data) { struct slideshow *ss = data; - if (!ss->files.num) + if (!ss->files.num || obs_transition_get_time(ss->transition) < 1.0f) return; if (++ss->cur_item >= ss->files.num) @@ -488,7 +510,7 @@ static void ss_previous_slide(void *data) { struct slideshow *ss = data; - if (!ss->files.num) + if (!ss->files.num || obs_transition_get_time(ss->transition) < 1.0f) return; if (ss->cur_item == 0) diff --git a/plugins/linux-alsa/data/locale/da-DK.ini b/plugins/linux-alsa/data/locale/da-DK.ini index 40fe514..0e2e8c4 100644 --- a/plugins/linux-alsa/data/locale/da-DK.ini +++ b/plugins/linux-alsa/data/locale/da-DK.ini @@ -1,3 +1,3 @@ -AlsaInput="Lyd-optagelsesenhed (ALSA)" +AlsaInput="Lydoptagelsesenhed (ALSA)" Device="Enhed" diff --git a/plugins/linux-alsa/data/locale/eu-ES.ini b/plugins/linux-alsa/data/locale/eu-ES.ini index 9c5f61c..8321130 100644 --- a/plugins/linux-alsa/data/locale/eu-ES.ini +++ b/plugins/linux-alsa/data/locale/eu-ES.ini @@ -1,3 +1,3 @@ -AlsaInput="Audio kapturako gailua (ALSA)" +AlsaInput="Audioa kapturatzeko gailua (ALSA)" Device="Gailua" diff --git a/plugins/linux-alsa/data/locale/fa-IR.ini b/plugins/linux-alsa/data/locale/fa-IR.ini new file mode 100644 index 0000000..944ac80 --- /dev/null +++ b/plugins/linux-alsa/data/locale/fa-IR.ini @@ -0,0 +1,3 @@ +AlsaInput="دستگاه ضبط صدا (ALSA)" +Device="دستگاه" + diff --git a/plugins/linux-alsa/linux-alsa.c b/plugins/linux-alsa/linux-alsa.c index b0cad0c..73635b1 100644 --- a/plugins/linux-alsa/linux-alsa.c +++ b/plugins/linux-alsa/linux-alsa.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-alsa", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Linux ALSA audio input capture"; +} extern struct obs_source_info alsa_input_capture; diff --git a/plugins/linux-capture/CMakeLists.txt b/plugins/linux-capture/CMakeLists.txt index d4f1649..7080346 100644 --- a/plugins/linux-capture/CMakeLists.txt +++ b/plugins/linux-capture/CMakeLists.txt @@ -6,7 +6,7 @@ if(NOT X11_Xcomposite_FOUND) return() endif() -find_package(XCB COMPONENTS XCB SHM XFIXES XINERAMA REQUIRED) +find_package(XCB COMPONENTS XCB RANDR SHM XFIXES XINERAMA REQUIRED) find_package(X11_XCB REQUIRED) include_directories(SYSTEM diff --git a/plugins/linux-capture/data/locale/da-DK.ini b/plugins/linux-capture/data/locale/da-DK.ini index b1b3df3..303ab88 100644 --- a/plugins/linux-capture/data/locale/da-DK.ini +++ b/plugins/linux-capture/data/locale/da-DK.ini @@ -11,6 +11,6 @@ CropRight="Beskær højre (pixels)" CropBottom="Beskær bund (pixels)" SwapRedBlue="Ombyt rød og blå" LockX="Lås X server under optagelse" -IncludeXBorder="Inkluder X-kant" +IncludeXBorder="Inkludér X-kant" ExcludeAlpha="Benyt alpha-fri teksturformat (Mesa løsning)" diff --git a/plugins/linux-capture/data/locale/de-DE.ini b/plugins/linux-capture/data/locale/de-DE.ini index e879a93..e72024c 100644 --- a/plugins/linux-capture/data/locale/de-DE.ini +++ b/plugins/linux-capture/data/locale/de-DE.ini @@ -2,7 +2,7 @@ X11SharedMemoryScreenInput="Bildschirmaufnahme (XSHM)" Screen="Bildschirm" CaptureCursor="Mauszeiger aufnehmen" AdvancedSettings="Erweiterte Einstellungen" -XServer="X Server" +XServer="X-Server" XCCapture="Fensteraufnahme (Xcomposite)" Window="Fenster" CropTop="Oben abschneiden (Pixel)" @@ -10,7 +10,7 @@ CropLeft="Links abschneiden (Pixel)" CropRight="Rechts abschneiden (Pixel)" CropBottom="Unten abschneiden (Pixel)" SwapRedBlue="Rot und Blau tauschen" -LockX="X Server sperren während der Aufnahme" -IncludeXBorder="X Rahmen anzeigen" -ExcludeAlpha="Verwenden von alphalosem Texturformat (Mesa Problemumgehung)" +LockX="X-Server während der Aufnahme sperren" +IncludeXBorder="X-Rahmen anzeigen" +ExcludeAlpha="Alphaloses Texturformat verwenden (Mesa-Problemumgehung)" diff --git a/plugins/linux-capture/data/locale/fa-IR.ini b/plugins/linux-capture/data/locale/fa-IR.ini new file mode 100644 index 0000000..d6ed785 --- /dev/null +++ b/plugins/linux-capture/data/locale/fa-IR.ini @@ -0,0 +1,15 @@ +X11SharedMemoryScreenInput="صفحه نمایش ضبط (XSHM)" +Screen="صفحه نمایش" +CaptureCursor="گرفتن مکان نما" +AdvancedSettings="تنظیمات پیشرفته" +XServer="سرویس دهنده" +XCCapture="ضبط پنجره (Xcomposite)" +Window="ویندوز" +CropTop="برش بالا (پیکسل)" +CropLeft="برش چپ (پیکسل)" +CropRight="برش راست (پیکسل)" +CropBottom="برش پایین (پیکسل)" +LockX="سرور اکس قفل هنگام گرفتن" +IncludeXBorder="شامل X مرز" +ExcludeAlpha="استفاده از فرمت های بافت آلفا کمتر (مسا حلی)" + diff --git a/plugins/linux-capture/data/locale/fr-FR.ini b/plugins/linux-capture/data/locale/fr-FR.ini index 9258fa3..910dd26 100644 --- a/plugins/linux-capture/data/locale/fr-FR.ini +++ b/plugins/linux-capture/data/locale/fr-FR.ini @@ -11,6 +11,6 @@ CropRight="Rogner à droite (pixels)" CropBottom="Rogner en bas (pixels)" SwapRedBlue="Intervertir le rouge et le bleu" LockX="Verrouiller le serveur X lors de la capture" -IncludeXBorder="Inclure la bordure de X" -ExcludeAlpha="Utiliser un format de texture sans alpha (contournement pour Mesa)" +IncludeXBorder="Inclure la bordure de la fenêtre X" +ExcludeAlpha="Utiliser un format de texture sans alpha (palliatif pour Mesa)" diff --git a/plugins/linux-capture/data/locale/it-IT.ini b/plugins/linux-capture/data/locale/it-IT.ini index a2f3307..b1f1f40 100644 --- a/plugins/linux-capture/data/locale/it-IT.ini +++ b/plugins/linux-capture/data/locale/it-IT.ini @@ -1,16 +1,16 @@ -X11SharedMemoryScreenInput="Cattura schermo (XSHM)" +X11SharedMemoryScreenInput="Cattura dello schermo (XSHM)" Screen="Schermo" CaptureCursor="Cattura il cursore" -AdvancedSettings="Configurazioni Avanzate" +AdvancedSettings="Impostazioni avanzate" XServer="X Server" -XCCapture="Cattura la finestra (xcomposite)" +XCCapture="Cattura la finestra (Xcomposite)" Window="Finestra" -CropTop="Crop Superiore (pixels)" -CropLeft="Crop Sinistro (pixels)" -CropRight="Crop Destro (pixels)" -CropBottom="Crop Inferiore (pixels)" -SwapRedBlue="Inverti rosso e blu" -LockX="Blocca l' X Server durante l'acquisizione" -IncludeXBorder="Includi X bordi" -ExcludeAlpha="Usa formato texture alfa-less (soluzione di Mesa)" +CropTop="Ritaglia dall'alto (in pixel)" +CropLeft="Ritaglia da sinistra (in pixel)" +CropRight="Ritaglia da destra (in pixel)" +CropBottom="Ritaglia dal basso (in pixel)" +SwapRedBlue="Inverti i colori rosso e blu" +LockX="Blocca X Server durante la cattura" +IncludeXBorder="Includi i bordi della finestra X" +ExcludeAlpha="Utilizza il formato alfa-less per le texture (soluzione di Mesa)" diff --git a/plugins/linux-capture/data/locale/ro-RO.ini b/plugins/linux-capture/data/locale/ro-RO.ini index 0ae7367..57a4520 100644 --- a/plugins/linux-capture/data/locale/ro-RO.ini +++ b/plugins/linux-capture/data/locale/ro-RO.ini @@ -12,5 +12,5 @@ CropBottom="Trunchiază partea inferioară (pixeli)" SwapRedBlue="Schimbă roșu cu albastru" LockX="Blochează serverul X atunci când se capturează" IncludeXBorder="Include marginea cu X" -ExcludeAlpha="Folosește formatul de texturi fără alpha (soluție de evitare Mesa)" +ExcludeAlpha="Folosește formatul de texturi fără alpha (soluție de evitare pentru Mesa)" diff --git a/plugins/linux-capture/data/locale/sr-CS.ini b/plugins/linux-capture/data/locale/sr-CS.ini index 912e937..362aa7f 100644 --- a/plugins/linux-capture/data/locale/sr-CS.ini +++ b/plugins/linux-capture/data/locale/sr-CS.ini @@ -1,6 +1,6 @@ -X11SharedMemoryScreenInput="Ekranski ulaz (XSHM)" -Screen="Ekran" -CaptureCursor="Snimaj kursor" +X11SharedMemoryScreenInput="Snimak ekrana (XSHM)" +Screen="Екран" +CaptureCursor="Снимај курсор" AdvancedSettings="Napredna podešavanja" XServer="X server" XCCapture="Улаз са прозора (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/sr-SP.ini b/plugins/linux-capture/data/locale/sr-SP.ini index 1823224..9920ed1 100644 --- a/plugins/linux-capture/data/locale/sr-SP.ini +++ b/plugins/linux-capture/data/locale/sr-SP.ini @@ -1,6 +1,6 @@ -X11SharedMemoryScreenInput="Екрански улаз (XSHM)" -Screen="Екран" -CaptureCursor="Снимај курсор" +X11SharedMemoryScreenInput="Снимак екрана (XSHM)" +Screen="Ekran" +CaptureCursor="Snimaj kursor" AdvancedSettings="Напредна подешавања" XServer="X сервер" XCCapture="Ulaz sa prozora (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/sv-SE.ini b/plugins/linux-capture/data/locale/sv-SE.ini index 688168a..5fe29d2 100644 --- a/plugins/linux-capture/data/locale/sv-SE.ini +++ b/plugins/linux-capture/data/locale/sv-SE.ini @@ -1,7 +1,7 @@ X11SharedMemoryScreenInput="Bildskärmskälla (XSHM)" Screen="Skärm" CaptureCursor="Visa muspekare" -AdvancedSettings="Avancerade Inställningar" +AdvancedSettings="Avancerade inställningar" XServer="X-servern" XCCapture="Fönsterkälla (Xcomposite)" Window="Fönster" diff --git a/plugins/linux-capture/linux-capture.c b/plugins/linux-capture/linux-capture.c index ad95463..ce49ee7 100644 --- a/plugins/linux-capture/linux-capture.c +++ b/plugins/linux-capture/linux-capture.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-xshm", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "xcomposite/xshm based window/screen capture for X11"; +} extern struct obs_source_info xshm_input; diff --git a/plugins/linux-capture/xcompcap-main.cpp b/plugins/linux-capture/xcompcap-main.cpp index ba7986f..bbfe2eb 100644 --- a/plugins/linux-capture/xcompcap-main.cpp +++ b/plugins/linux-capture/xcompcap-main.cpp @@ -148,6 +148,7 @@ struct XCompcapMain_private bool lockX; bool include_border; bool exclude_alpha; + bool draw_opaque; double window_check_time = 0.0; @@ -251,6 +252,9 @@ static void xcc_cleanup(XCompcapMain_private *p) XDisplayLock xlock; if (p->gltex) { + GLuint gltex = *(GLuint*)gs_texture_get_obj(p->gltex); + glBindTexture(GL_TEXTURE_2D, gltex); + glXReleaseTexImageEXT(xdisp, p->glxpixmap, GLX_FRONT_LEFT_EXT); gs_texture_destroy(p->gltex); p->gltex = 0; } @@ -301,6 +305,7 @@ void XCompcapMain::updateSettings(obs_data_t *settings) p->show_cursor = obs_data_get_bool(settings, "show_cursor"); p->include_border = obs_data_get_bool(settings, "include_border"); p->exclude_alpha = obs_data_get_bool(settings, "exclude_alpha"); + p->draw_opaque = false; } else { p->win = prevWin; } @@ -347,6 +352,90 @@ void XCompcapMain::updateSettings(obs_data_t *settings) cf = GS_BGRX; } + bool has_alpha = true; + + const int attrs[] = + { + GLX_BIND_TO_TEXTURE_RGBA_EXT, GL_TRUE, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + GLX_ALPHA_SIZE, 8, + GLX_DOUBLEBUFFER, GL_FALSE, + None + }; + + int nelem = 0; + GLXFBConfig *configs = glXGetFBConfigs(xdisp, + XCompcap::getRootWindowScreen(attr.root), + &nelem); + + if (nelem <= 0) { + blog(LOG_ERROR, "no fb configs available"); + p->win = 0; + p->height = 0; + p->width = 0; + return; + } + + GLXFBConfig config; + for (int i = 0; i < nelem; i++) { + config = configs[i]; + XVisualInfo *visual = glXGetVisualFromFBConfig(xdisp, config); + if (!visual) + continue; + + if (attr.visual->visualid != visual->visualid) { + XFree(visual); + continue; + } + XFree(visual); + + int value; + glXGetFBConfigAttrib(xdisp, config, GLX_ALPHA_SIZE, &value); + if (value != 8) + has_alpha = false; + + break; + } + + XFree(configs); + configs = glXChooseFBConfig(xdisp, + XCompcap::getRootWindowScreen(attr.root), + attrs, &nelem); + + if (nelem <= 0) { + blog(LOG_ERROR, "no matching fb config found"); + p->win = 0; + p->height = 0; + p->width = 0; + return; + } + bool found = false; + for (int i = 0; i < nelem; i++) { + config = configs[i]; + XVisualInfo *visual = glXGetVisualFromFBConfig(xdisp, config); + if (!visual) + continue; + + if (attr.depth != visual->depth) { + XFree(visual); + continue; + } + XFree(visual); + found = true; + break; + } + if (!found) + config = configs[0]; + + if (cf == GS_BGRX || !has_alpha) { + p->draw_opaque = true; + } + + int inverted; + glXGetFBConfigAttrib(xdisp, config, GLX_Y_INVERTED_EXT, &inverted); + p->inverted = inverted != 0; + p->border = attr.border_width; if (p->include_border) { @@ -395,32 +484,6 @@ void XCompcapMain::updateSettings(obs_data_t *settings) glBindTexture(GL_TEXTURE_2D, 0); } - const int attrs[] = - { - GLX_BIND_TO_TEXTURE_RGBA_EXT, GL_TRUE, - GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, - GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, - GLX_ALPHA_SIZE, 8, - GLX_DOUBLEBUFFER, GL_FALSE, - None - }; - - int nelem = 0; - GLXFBConfig* configs = glXChooseFBConfig(xdisp, - XCompcap::getRootWindowScreen(attr.root), - attrs, &nelem); - - if (nelem <= 0) { - blog(LOG_ERROR, "no matching fb config found"); - p->win = 0; - p->height = 0; - p->width = 0; - return; - } - - glXGetFBConfigAttrib(xdisp, configs[0], GLX_Y_INVERTED_EXT, &nelem); - p->inverted = nelem != 0; - xlock.resetError(); p->pixmap = XCompositeNameWindowPixmap(xdisp, p->win); @@ -433,14 +496,23 @@ void XCompcapMain::updateSettings(obs_data_t *settings) return; } - const int attribs[] = + const int attribs_alpha[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, None }; - p->glxpixmap = glXCreatePixmap(xdisp, configs[0], p->pixmap, attribs); + const int attribs_no_alpha[] = + { + GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT, + None + }; + + const int *attribs = cf == GS_RGBA ? attribs_alpha : attribs_no_alpha; + + p->glxpixmap = glXCreatePixmap(xdisp, config, p->pixmap, attribs); if (xlock.gotError()) { blog(LOG_ERROR, "glXCreatePixmap failed: %s", @@ -466,10 +538,14 @@ void XCompcapMain::updateSettings(obs_data_t *settings) if (!p->windowName.empty()) { blog(LOG_INFO, "[window-capture: '%s'] update settings:\n" "\ttitle: %s\n" - "\tclass: %s", + "\tclass: %s\n" + "\tHas alpha: %s\n" + "\tFound proper GLXFBConfig: %s\n", obs_source_get_name(p->source), XCompcap::getWindowName(p->win).c_str(), - XCompcap::getWindowClass(p->win).c_str()); + XCompcap::getWindowClass(p->win).c_str(), + has_alpha ? "yes" : "no", + found ? "yes" : "no"); blog(LOG_DEBUG, "\n" "\tid: %s", std::to_string((long long)p->win).c_str()); @@ -563,7 +639,10 @@ void XCompcapMain::render(gs_effect_t *effect) PLock lock(&p->lock, true); - effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); + if (p->draw_opaque) + effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); + else + effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); if (!lock.isLocked() || !p->tex) return; diff --git a/plugins/linux-capture/xhelpers.c b/plugins/linux-capture/xhelpers.c index 1eeda7c..724f4f3 100644 --- a/plugins/linux-capture/xhelpers.c +++ b/plugins/linux-capture/xhelpers.c @@ -17,6 +17,7 @@ along with this program. If not, see . #include #include +#include #include #include @@ -95,6 +96,78 @@ fail: return -1; } +bool randr_is_active(xcb_connection_t *xcb) +{ + if (!xcb || !xcb_get_extension_data(xcb, &xcb_randr_id)->present) + return false; + + return true; +} + +int randr_screen_count(xcb_connection_t *xcb) +{ + if (!xcb) + return 0; + + xcb_screen_t *screen; + screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data; + + xcb_randr_get_screen_resources_cookie_t res_c; + xcb_randr_get_screen_resources_reply_t* res_r; + + res_c = xcb_randr_get_screen_resources(xcb, screen->root); + res_r = xcb_randr_get_screen_resources_reply(xcb, res_c, 0); + if (!res_r) + return 0; + + return xcb_randr_get_screen_resources_crtcs_length(res_r); +} + +int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, + int_fast32_t *x, int_fast32_t *y, + int_fast32_t *w, int_fast32_t *h, + xcb_screen_t **rscreen) +{ + xcb_screen_t *xscreen; + xscreen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data; + + xcb_randr_get_screen_resources_cookie_t res_c; + xcb_randr_get_screen_resources_reply_t* res_r; + + res_c = xcb_randr_get_screen_resources(xcb, xscreen->root); + res_r = xcb_randr_get_screen_resources_reply(xcb, res_c, 0); + if (!res_r) + goto fail; + + int screens = xcb_randr_get_screen_resources_crtcs_length(res_r); + if (screen < 0 || screen >= screens) + goto fail; + + xcb_randr_crtc_t *crtc = xcb_randr_get_screen_resources_crtcs(res_r); + + xcb_randr_get_crtc_info_cookie_t crtc_c; + xcb_randr_get_crtc_info_reply_t *crtc_r; + + crtc_c = xcb_randr_get_crtc_info(xcb, *(crtc + screen), 0); + crtc_r = xcb_randr_get_crtc_info_reply(xcb, crtc_c, 0); + if (!crtc_r) + goto fail; + + *x = crtc_r->x; + *y = crtc_r->y; + *w = crtc_r->width; + *h = crtc_r->height; + + if (rscreen) + *rscreen = xscreen; + + return 0; + +fail: + *x = *y = *w = *h = 0; + return -1; +} + int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *w, int_fast32_t *h) { diff --git a/plugins/linux-capture/xhelpers.h b/plugins/linux-capture/xhelpers.h index 066684f..95aff5c 100644 --- a/plugins/linux-capture/xhelpers.h +++ b/plugins/linux-capture/xhelpers.h @@ -64,6 +64,39 @@ int xinerama_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, int_fast32_t *x, int_fast32_t *y, int_fast32_t *w, int_fast32_t *h); +/** + * Check for Randr extension + * + * @return true if randr is available which means it's active. + */ +bool randr_is_active(xcb_connection_t *xcb); + +/** + * Get the number of Randr screens + * + * @return number of screens + */ +int randr_screen_count(xcb_connection_t *xcb); + +/** + * Get screen geometry for a Rand crtc (screen) + * + * @note On error the passed coordinates/sizes will be set to 0. + * + * @param xcb xcb connection + * @param screen screen number to get geometry for + * @param x x-coordinate of the screen + * @param y y-coordinate of the screen + * @param w width of the screen + * @param h height of the screen + * + * @return < 0 on error + */ +int randr_screen_geo(xcb_connection_t *xcb, int_fast32_t screen, + int_fast32_t *x, int_fast32_t *y, + int_fast32_t *w, int_fast32_t *h, + xcb_screen_t **rscreen); + /** * Get screen geometry for a X11 screen * diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index b5fa3a3..f00c0eb 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -18,6 +18,7 @@ along with this program. If not, see . #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ struct xshm_data { bool show_cursor; bool use_xinerama; + bool use_randr; bool advanced; }; @@ -83,6 +85,9 @@ static bool xshm_check_extensions(xcb_connection_t *xcb) if (!xcb_get_extension_data(xcb, &xcb_xinerama_id)->present) blog(LOG_INFO, "Missing Xinerama extension !"); + if (!xcb_get_extension_data(xcb, &xcb_randr_id)->present) + blog(LOG_INFO, "Missing Randr extension !"); + return ok; } @@ -96,7 +101,15 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data) int_fast32_t old_width = data->width; int_fast32_t old_height = data->height; - if (data->use_xinerama) { + if (data->use_randr) { + if (randr_screen_geo(data->xcb, data->screen_id, + &data->x_org, &data->y_org, + &data->width, &data->height, + &data->xcb_screen) < 0) { + return -1; + } + } + else if (data->use_xinerama) { if (xinerama_screen_geo(data->xcb, data->screen_id, &data->x_org, &data->y_org, &data->width, &data->height) < 0) { @@ -189,6 +202,7 @@ static void xshm_capture_start(struct xshm_data *data) if (!xshm_check_extensions(data->xcb)) goto fail; + data->use_randr = randr_is_active(data->xcb) ? true : false; data->use_xinerama = xinerama_is_active(data->xcb) ? true : false; if (xshm_update_geometry(data) < 0) { @@ -287,16 +301,21 @@ static bool xshm_server_changed(obs_properties_t *props, struct dstr screen_info; dstr_init(&screen_info); + bool randr = randr_is_active(xcb); bool xinerama = xinerama_is_active(xcb); - int_fast32_t count = (xinerama) ? - xinerama_screen_count(xcb) : - xcb_setup_roots_length(xcb_get_setup(xcb)); + int_fast32_t count = (randr) ? + randr_screen_count(xcb) : + (xinerama) ? + xinerama_screen_count(xcb) : + xcb_setup_roots_length(xcb_get_setup(xcb)); for (int_fast32_t i = 0; i < count; ++i) { int_fast32_t x, y, w, h; x = y = w = h = 0; - if (xinerama) + if (randr) + randr_screen_geo(xcb, i, &x, &y, &w, &h, NULL); + else if (xinerama) xinerama_screen_geo(xcb, i, &x, &y, &w, &h); else x11_screen_geo(xcb, i, &w, &h); diff --git a/plugins/linux-jack/data/locale/da-DK.ini b/plugins/linux-jack/data/locale/da-DK.ini index 0ac0563..ca3411b 100644 --- a/plugins/linux-jack/data/locale/da-DK.ini +++ b/plugins/linux-jack/data/locale/da-DK.ini @@ -1,4 +1,4 @@ -StartJACKServer="Start JACK server" -Channels="Antal af kanaler" -JACKInput="JACK input klient" +StartJACKServer="Start JACK Server" +Channels="Antal kanaler" +JACKInput="JACK Input Client" diff --git a/plugins/linux-jack/data/locale/de-DE.ini b/plugins/linux-jack/data/locale/de-DE.ini index 38c656d..2c222bb 100644 --- a/plugins/linux-jack/data/locale/de-DE.ini +++ b/plugins/linux-jack/data/locale/de-DE.ini @@ -1,4 +1,4 @@ -StartJACKServer="JACK Server starten" +StartJACKServer="JACK-Server starten" Channels="Anzahl der Kanäle" -JACKInput="JACK-Eingang-Client" +JACKInput="JACK-Eingabe-Client" diff --git a/plugins/linux-jack/data/locale/fa-IR.ini b/plugins/linux-jack/data/locale/fa-IR.ini new file mode 100644 index 0000000..88f791d --- /dev/null +++ b/plugins/linux-jack/data/locale/fa-IR.ini @@ -0,0 +1,3 @@ +StartJACKServer="شروع JACK سرور" +Channels="تعداد کانال" + diff --git a/plugins/linux-jack/data/locale/it-IT.ini b/plugins/linux-jack/data/locale/it-IT.ini index 6f73b82..3edd86a 100644 --- a/plugins/linux-jack/data/locale/it-IT.ini +++ b/plugins/linux-jack/data/locale/it-IT.ini @@ -1,4 +1,4 @@ -StartJACKServer="Avvia il Server JACK" +StartJACKServer="Avvia il server JACK" Channels="Numero dei canali" -JACKInput="Ingresso JACK Client" +JACKInput="Ingresso del client JACK" diff --git a/plugins/linux-jack/data/locale/ur-PK.ini b/plugins/linux-jack/data/locale/ur-PK.ini new file mode 100644 index 0000000..3f12b3c --- /dev/null +++ b/plugins/linux-jack/data/locale/ur-PK.ini @@ -0,0 +1,4 @@ +StartJACKServer="جیک کا آغاز" +Channels="چینلز کی تعداد" +JACKInput="وصول کار جیک ان پٹ" + diff --git a/plugins/linux-jack/jack-wrapper.c b/plugins/linux-jack/jack-wrapper.c index 63309cc..98fa132 100644 --- a/plugins/linux-jack/jack-wrapper.c +++ b/plugins/linux-jack/jack-wrapper.c @@ -103,7 +103,7 @@ int_fast32_t jack_init(struct jack_data* data) sizeof(jack_port_t*) * data->channels); for (unsigned int i = 0; i < data->channels; ++i) { char port_name[10] = {'\0'}; - snprintf(port_name, sizeof(port_name), "in_%d", i+1); + snprintf(port_name, sizeof(port_name), "in_%u", i+1); data->jack_ports[i] = jack_port_register(data->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); diff --git a/plugins/linux-jack/linux-jack.c b/plugins/linux-jack/linux-jack.c index 349a205..be6721d 100644 --- a/plugins/linux-jack/linux-jack.c +++ b/plugins/linux-jack/linux-jack.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-jack", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "JACK Audio Connection Kit output capture"; +} extern struct obs_source_info jack_output_capture; diff --git a/plugins/linux-pulseaudio/data/locale/da-DK.ini b/plugins/linux-pulseaudio/data/locale/da-DK.ini index ba09e5a..ac28123 100644 --- a/plugins/linux-pulseaudio/data/locale/da-DK.ini +++ b/plugins/linux-pulseaudio/data/locale/da-DK.ini @@ -1,4 +1,4 @@ -PulseInput="Indfang lyd ind (PulseAudio)" -PulseOutput="Indfang lyd Ud (PulseAudio)" +PulseInput="Lydinputoptagelse (PulseAudio)" +PulseOutput="Lydoutputoptagelse (PulseAudio)" Device="Enhed" diff --git a/plugins/linux-pulseaudio/data/locale/de-DE.ini b/plugins/linux-pulseaudio/data/locale/de-DE.ini index 8f28f83..e05d55d 100644 --- a/plugins/linux-pulseaudio/data/locale/de-DE.ini +++ b/plugins/linux-pulseaudio/data/locale/de-DE.ini @@ -1,4 +1,4 @@ -PulseInput="Audio Eingabe Aufnahme (PulseAudio)" -PulseOutput="Audio Ausgabe Aufnahme (PulseAudio)" +PulseInput="Audioeingabeaufnahme (PulseAudio)" +PulseOutput="Audioausgabeaufnahme (PulseAudio)" Device="Gerät" diff --git a/plugins/linux-pulseaudio/data/locale/fa-IR.ini b/plugins/linux-pulseaudio/data/locale/fa-IR.ini new file mode 100644 index 0000000..72535e1 --- /dev/null +++ b/plugins/linux-pulseaudio/data/locale/fa-IR.ini @@ -0,0 +1,4 @@ +PulseInput="ضبط صوت داخلی (PulseAudio)" +PulseOutput="ضبط صوت خروجی (PulseAudio)" +Device="دستگاه" + diff --git a/plugins/linux-pulseaudio/data/locale/fr-FR.ini b/plugins/linux-pulseaudio/data/locale/fr-FR.ini index 152b52a..125f38b 100644 --- a/plugins/linux-pulseaudio/data/locale/fr-FR.ini +++ b/plugins/linux-pulseaudio/data/locale/fr-FR.ini @@ -1,4 +1,4 @@ -PulseInput="Capture de l'audio entrant (PulseAudio)" -PulseOutput="Capture de l'audio sortant (PulseAudio)" +PulseInput="Capture Audio (Entrée PulseAudio)" +PulseOutput="Capture Audio (Sorties PulseAudio)" Device="Périphérique" diff --git a/plugins/linux-pulseaudio/data/locale/it-IT.ini b/plugins/linux-pulseaudio/data/locale/it-IT.ini index 672ed38..e10bac9 100644 --- a/plugins/linux-pulseaudio/data/locale/it-IT.ini +++ b/plugins/linux-pulseaudio/data/locale/it-IT.ini @@ -1,4 +1,4 @@ -PulseInput="Input di acquisizione audio (PulseAudio)" -PulseOutput="Output acquisizione audio (PulseAudio)" +PulseInput="Ingresso di acquisizione audio (PulseAudio)" +PulseOutput="Uscita di acquisizione audio (PulseAudio)" Device="Dispositivo" diff --git a/plugins/linux-pulseaudio/data/locale/ka-GE.ini b/plugins/linux-pulseaudio/data/locale/ka-GE.ini index 78a0453..e36f439 100644 --- a/plugins/linux-pulseaudio/data/locale/ka-GE.ini +++ b/plugins/linux-pulseaudio/data/locale/ka-GE.ini @@ -1,4 +1,4 @@ PulseInput="შემავალი ხმოვანი სიგნალის ჩაწერა (PulseAudio)" -PulseOutput="გამომავალი ხმოვანი სიგნალის ჩაწერა (PulseAudio)" +PulseOutput="გამოტანილი ხმის ჩაწერა (PulseAudio)" Device="მოწყობილობა" diff --git a/plugins/linux-pulseaudio/linux-pulseaudio.c b/plugins/linux-pulseaudio/linux-pulseaudio.c index acfd10e..78f066e 100644 --- a/plugins/linux-pulseaudio/linux-pulseaudio.c +++ b/plugins/linux-pulseaudio/linux-pulseaudio.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-pulseaudio", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Linux PulseAudio input/output capture"; +} extern struct obs_source_info pulse_input_capture; extern struct obs_source_info pulse_output_capture; diff --git a/plugins/linux-v4l2/data/locale/ar-SA.ini b/plugins/linux-v4l2/data/locale/ar-SA.ini index 988037c..034bfe3 100644 --- a/plugins/linux-v4l2/data/locale/ar-SA.ini +++ b/plugins/linux-v4l2/data/locale/ar-SA.ini @@ -5,4 +5,7 @@ VideoStandard="معيار الفيديو" DVTiming="توقيت DV" Resolution="الأبعاد" FrameRate="الإطار" +ColorRange="نطاق اللون" +ColorRange.Partial="جزئي" +ColorRange.Full="كامل" diff --git a/plugins/linux-v4l2/data/locale/ca-ES.ini b/plugins/linux-v4l2/data/locale/ca-ES.ini index ed9f9d2..4a01274 100644 --- a/plugins/linux-v4l2/data/locale/ca-ES.ini +++ b/plugins/linux-v4l2/data/locale/ca-ES.ini @@ -8,4 +8,7 @@ Resolution="Resolució" FrameRate="Fotogrames per segon" LeaveUnchanged="No ho canviïs" UseBuffering="Usa memòria intermèdia" +ColorRange="Gamma de colors" +ColorRange.Partial="Parcial" +ColorRange.Full="Complet" diff --git a/plugins/linux-v4l2/data/locale/cs-CZ.ini b/plugins/linux-v4l2/data/locale/cs-CZ.ini index ad33b44..56488ab 100644 --- a/plugins/linux-v4l2/data/locale/cs-CZ.ini +++ b/plugins/linux-v4l2/data/locale/cs-CZ.ini @@ -8,4 +8,7 @@ Resolution="Rozlišení" FrameRate="Snímkovací frekvence" LeaveUnchanged="Ponechat nezměněné" UseBuffering="Použít vyrovnávací paměť" +ColorRange="Rozsah barev" +ColorRange.Partial="Částečný" +ColorRange.Full="Plný" diff --git a/plugins/linux-v4l2/data/locale/da-DK.ini b/plugins/linux-v4l2/data/locale/da-DK.ini index d3c94e4..75755b6 100644 --- a/plugins/linux-v4l2/data/locale/da-DK.ini +++ b/plugins/linux-v4l2/data/locale/da-DK.ini @@ -1,11 +1,14 @@ -V4L2Input="Video optagelses enhed (V4L2)" +V4L2Input="Videooptageenhed (V4L2)" Device="Enhed" Input="Input" -VideoFormat="Video format" -VideoStandard="Video standard" -DVTiming="DV timing" +VideoFormat="Videoformat" +VideoStandard="Videostandard" +DVTiming="DV-timing" Resolution="Opløsning" -FrameRate="Framerate" -LeaveUnchanged="Efterlad uændret" -UseBuffering="Brug buffering" +FrameRate="Billedhastighed" +LeaveUnchanged="Behold uændret" +UseBuffering="Benyt buffering" +ColorRange="Farveområde" +ColorRange.Partial="Delvist" +ColorRange.Full="Fuldt" diff --git a/plugins/linux-v4l2/data/locale/de-DE.ini b/plugins/linux-v4l2/data/locale/de-DE.ini index 03d3d56..f2f6fc2 100644 --- a/plugins/linux-v4l2/data/locale/de-DE.ini +++ b/plugins/linux-v4l2/data/locale/de-DE.ini @@ -7,5 +7,8 @@ DVTiming="DV-Timing" Resolution="Auflösung" FrameRate="Bildrate" LeaveUnchanged="Unverändert lassen" -UseBuffering="Buffering benutzen" +UseBuffering="Puffern benutzen" +ColorRange="Farbbereich" +ColorRange.Partial="Begrenzt" +ColorRange.Full="Voll" diff --git a/plugins/linux-v4l2/data/locale/en-US.ini b/plugins/linux-v4l2/data/locale/en-US.ini index bdb93b0..7a001c2 100644 --- a/plugins/linux-v4l2/data/locale/en-US.ini +++ b/plugins/linux-v4l2/data/locale/en-US.ini @@ -8,3 +8,6 @@ Resolution="Resolution" FrameRate="Frame Rate" LeaveUnchanged="Leave Unchanged" UseBuffering="Use Buffering" +ColorRange="Color Range" +ColorRange.Partial="Partial" +ColorRange.Full="Full" diff --git a/plugins/linux-v4l2/data/locale/es-ES.ini b/plugins/linux-v4l2/data/locale/es-ES.ini index e7e0e7e..38dbadc 100644 --- a/plugins/linux-v4l2/data/locale/es-ES.ini +++ b/plugins/linux-v4l2/data/locale/es-ES.ini @@ -8,4 +8,7 @@ Resolution="Resolución" FrameRate="Frecuencia de imágenes" LeaveUnchanged="Dejar sin cambios" UseBuffering="Utilizar el almacenamiento en búfer" +ColorRange="Gama de Colores" +ColorRange.Partial="Parcial" +ColorRange.Full="Completo" diff --git a/plugins/linux-v4l2/data/locale/eu-ES.ini b/plugins/linux-v4l2/data/locale/eu-ES.ini index 3769275..e3f7bd0 100644 --- a/plugins/linux-v4l2/data/locale/eu-ES.ini +++ b/plugins/linux-v4l2/data/locale/eu-ES.ini @@ -8,4 +8,7 @@ Resolution="Bereizmena" FrameRate="Fotograma emaria" LeaveUnchanged="Utzi aldatu gabe" UseBuffering="Erabili bufferreratzea" +ColorRange="Kolore tartea" +ColorRange.Partial="Partziala" +ColorRange.Full="Osoa" diff --git a/plugins/linux-v4l2/data/locale/fa-IR.ini b/plugins/linux-v4l2/data/locale/fa-IR.ini new file mode 100644 index 0000000..639ebc8 --- /dev/null +++ b/plugins/linux-v4l2/data/locale/fa-IR.ini @@ -0,0 +1,14 @@ +V4L2Input="دستگاه ضبط ویدئویی (V4L2)" +Device="دستگاه" +Input="ورودی" +VideoFormat="فرمت های تصویری" +VideoStandard="استاندارد های تصویری" +DVTiming="زمان بندی دی وی" +Resolution="اندازه تصویر" +FrameRate="نرخ فریم" +LeaveUnchanged="ترک بدون تغییر" +UseBuffering="استفاده از بافرینگ" +ColorRange="طیف رنگ" +ColorRange.Partial="جزئی" +ColorRange.Full="کامل" + diff --git a/plugins/linux-v4l2/data/locale/fi-FI.ini b/plugins/linux-v4l2/data/locale/fi-FI.ini index 28279a9..14795ed 100644 --- a/plugins/linux-v4l2/data/locale/fi-FI.ini +++ b/plugins/linux-v4l2/data/locale/fi-FI.ini @@ -8,4 +8,7 @@ Resolution="Tarkkuus" FrameRate="Kuvanopeus" LeaveUnchanged="Jätä ennalleen" UseBuffering="Käytä puskurointia" +ColorRange="Värialue" +ColorRange.Partial="Osittainen" +ColorRange.Full="Täysi" diff --git a/plugins/linux-v4l2/data/locale/fr-FR.ini b/plugins/linux-v4l2/data/locale/fr-FR.ini index 52d4e6f..367e885 100644 --- a/plugins/linux-v4l2/data/locale/fr-FR.ini +++ b/plugins/linux-v4l2/data/locale/fr-FR.ini @@ -5,7 +5,10 @@ VideoFormat="Format vidéo" VideoStandard="Standard vidéo" DVTiming="Timing DV" Resolution="Résolution" -FrameRate="Fréquence d'image" +FrameRate="Images par seconde" LeaveUnchanged="Annuler les modifications" -UseBuffering="Utiliser la mise en mémoire tampon" +UseBuffering="Utiliser le tampon mémoire" +ColorRange="Gamme de couleurs" +ColorRange.Partial="Partielle" +ColorRange.Full="Complète" diff --git a/plugins/linux-v4l2/data/locale/hu-HU.ini b/plugins/linux-v4l2/data/locale/hu-HU.ini index 1f2db15..85b6e79 100644 --- a/plugins/linux-v4l2/data/locale/hu-HU.ini +++ b/plugins/linux-v4l2/data/locale/hu-HU.ini @@ -8,4 +8,7 @@ Resolution="Felbontás" FrameRate="Képkockasebesség" LeaveUnchanged="Változatlanul hagyni" UseBuffering="Pufferelés használata" +ColorRange="Színtartomány" +ColorRange.Partial="Részleges" +ColorRange.Full="Teljes" diff --git a/plugins/linux-v4l2/data/locale/it-IT.ini b/plugins/linux-v4l2/data/locale/it-IT.ini index a8723b7..eac5c7a 100644 --- a/plugins/linux-v4l2/data/locale/it-IT.ini +++ b/plugins/linux-v4l2/data/locale/it-IT.ini @@ -1,11 +1,14 @@ V4L2Input="Dispositivo di acquisizione video (V4L2)" Device="Dispositivo" -Input="Input" -VideoFormat="Formato Video" -VideoStandard="Video Standard" -DVTiming="DV Timing" +Input="Ingresso" +VideoFormat="Formato video" +VideoStandard="Standard video" +DVTiming="Timing DV" Resolution="Risoluzione" -FrameRate="Frame Rate" +FrameRate="Velocità dei fotogrammi" LeaveUnchanged="Lascia invariato" -UseBuffering="Usa Buffer" +UseBuffering="Utilizza il buffering" +ColorRange="Gamma di colori" +ColorRange.Partial="Parziale" +ColorRange.Full="Intero" diff --git a/plugins/linux-v4l2/data/locale/ja-JP.ini b/plugins/linux-v4l2/data/locale/ja-JP.ini index 847fd2c..215675f 100644 --- a/plugins/linux-v4l2/data/locale/ja-JP.ini +++ b/plugins/linux-v4l2/data/locale/ja-JP.ini @@ -8,4 +8,7 @@ Resolution="解像度" FrameRate="フレームレート" LeaveUnchanged="変更せずに戻る" UseBuffering="バッファリングを使用する" +ColorRange="色範囲" +ColorRange.Partial="一部" +ColorRange.Full="全部" diff --git a/plugins/linux-v4l2/data/locale/ka-GE.ini b/plugins/linux-v4l2/data/locale/ka-GE.ini index 4ede749..1b2e750 100644 --- a/plugins/linux-v4l2/data/locale/ka-GE.ini +++ b/plugins/linux-v4l2/data/locale/ka-GE.ini @@ -1,4 +1,4 @@ -V4L2Input="ვიდეოს ჩამწერი მოწყობილობა (V4L2)" +V4L2Input="ვიდეოს გადამღები მოწყობილობა (V4L2)" Device="მოწყობილობა" Input="შემავალი" VideoFormat="ვიდეოს ფორმატი" @@ -8,4 +8,7 @@ Resolution="გარჩევადობა" FrameRate="კადრის სიხშირე" LeaveUnchanged="უცვლელად დატოვება" UseBuffering="ბუფერიზაციის გამოყენება" +ColorRange="ფერთა გამა" +ColorRange.Partial="ნაწილობრივი" +ColorRange.Full="სრული" diff --git a/plugins/linux-v4l2/data/locale/ko-KR.ini b/plugins/linux-v4l2/data/locale/ko-KR.ini index 7554923..0e55a4d 100644 --- a/plugins/linux-v4l2/data/locale/ko-KR.ini +++ b/plugins/linux-v4l2/data/locale/ko-KR.ini @@ -8,4 +8,7 @@ Resolution="해상도" FrameRate="프레임 레이트" LeaveUnchanged="저장하지 않고 두기" UseBuffering="버퍼링 사용" +ColorRange="색상 범위" +ColorRange.Partial="부분" +ColorRange.Full="전체" diff --git a/plugins/linux-v4l2/data/locale/nb-NO.ini b/plugins/linux-v4l2/data/locale/nb-NO.ini index a0c2d89..2945c8c 100644 --- a/plugins/linux-v4l2/data/locale/nb-NO.ini +++ b/plugins/linux-v4l2/data/locale/nb-NO.ini @@ -8,4 +8,7 @@ Resolution="Oppløsning" FrameRate="Bildefrekvens" LeaveUnchanged="La stå uendret" UseBuffering="Bruk bufring" +ColorRange="Fargespekter" +ColorRange.Partial="Delvis" +ColorRange.Full="Full" diff --git a/plugins/linux-v4l2/data/locale/nl-NL.ini b/plugins/linux-v4l2/data/locale/nl-NL.ini index 4a5ab42..356e52c 100644 --- a/plugins/linux-v4l2/data/locale/nl-NL.ini +++ b/plugins/linux-v4l2/data/locale/nl-NL.ini @@ -8,4 +8,7 @@ Resolution="Resolutie" FrameRate="Frame Rate" LeaveUnchanged="Ongewijzigd Laten" UseBuffering="Buffering Gebruiken" +ColorRange="Kleurbereik" +ColorRange.Partial="Gedeeltelijk" +ColorRange.Full="Volledig" diff --git a/plugins/linux-v4l2/data/locale/pl-PL.ini b/plugins/linux-v4l2/data/locale/pl-PL.ini index ba1200b..34986ff 100644 --- a/plugins/linux-v4l2/data/locale/pl-PL.ini +++ b/plugins/linux-v4l2/data/locale/pl-PL.ini @@ -8,4 +8,7 @@ Resolution="Rozdzielczość" FrameRate="Klatki na sekundę" LeaveUnchanged="Pozostaw bez zmian" UseBuffering="Użyj buforowania" +ColorRange="Zakres kolorów" +ColorRange.Partial="Częściowy" +ColorRange.Full="Pełny" diff --git a/plugins/linux-v4l2/data/locale/pt-BR.ini b/plugins/linux-v4l2/data/locale/pt-BR.ini index 9909414..34fe477 100644 --- a/plugins/linux-v4l2/data/locale/pt-BR.ini +++ b/plugins/linux-v4l2/data/locale/pt-BR.ini @@ -8,4 +8,7 @@ Resolution="Resolução" FrameRate="Taxa de quadros" LeaveUnchanged="Deixar inalterado" UseBuffering="Utilizar Buffering" +ColorRange="Intervalo de Cor" +ColorRange.Partial="Parcial" +ColorRange.Full="Completo" diff --git a/plugins/linux-v4l2/data/locale/ro-RO.ini b/plugins/linux-v4l2/data/locale/ro-RO.ini index 77481d0..78fba1f 100644 --- a/plugins/linux-v4l2/data/locale/ro-RO.ini +++ b/plugins/linux-v4l2/data/locale/ro-RO.ini @@ -7,5 +7,5 @@ DVTiming="Sincronizare DV" Resolution="Rezoluție" FrameRate="Frecvență de cadre" LeaveUnchanged="Lasă neschimbat" -UseBuffering="Folosește buffering" +UseBuffering="Folosește zona tampon" diff --git a/plugins/linux-v4l2/data/locale/ru-RU.ini b/plugins/linux-v4l2/data/locale/ru-RU.ini index f63553c..3daf760 100644 --- a/plugins/linux-v4l2/data/locale/ru-RU.ini +++ b/plugins/linux-v4l2/data/locale/ru-RU.ini @@ -8,4 +8,7 @@ Resolution="Разрешение" FrameRate="Частота кадров" LeaveUnchanged="Оставить без изменений" UseBuffering="Использовать буферизацию" +ColorRange="Цветовой диапазон" +ColorRange.Partial="Частичный" +ColorRange.Full="Полный" diff --git a/plugins/linux-v4l2/data/locale/sk-SK.ini b/plugins/linux-v4l2/data/locale/sk-SK.ini index 02f9011..efd1878 100644 --- a/plugins/linux-v4l2/data/locale/sk-SK.ini +++ b/plugins/linux-v4l2/data/locale/sk-SK.ini @@ -8,4 +8,7 @@ Resolution="Rozlíšenie" FrameRate="Frekvencia snímok" LeaveUnchanged="Ponechať bez zmeny" UseBuffering="Použiť vyrovnávaciu pamäť" +ColorRange="Farebný rozsah" +ColorRange.Partial="Čiastočný" +ColorRange.Full="Plný" diff --git a/plugins/linux-v4l2/data/locale/sr-CS.ini b/plugins/linux-v4l2/data/locale/sr-CS.ini index 917d57f..c8cdf5e 100644 --- a/plugins/linux-v4l2/data/locale/sr-CS.ini +++ b/plugins/linux-v4l2/data/locale/sr-CS.ini @@ -1,4 +1,4 @@ -V4L2Input="Ulazni video uređaj (V4L2)" +V4L2Input="Uređaj za snimanje videa (V4L2)" Device="Uređaj" Input="Ulaz" VideoFormat="Video format" diff --git a/plugins/linux-v4l2/data/locale/sr-SP.ini b/plugins/linux-v4l2/data/locale/sr-SP.ini index c631713..de0a8be 100644 --- a/plugins/linux-v4l2/data/locale/sr-SP.ini +++ b/plugins/linux-v4l2/data/locale/sr-SP.ini @@ -1,4 +1,4 @@ -V4L2Input="Улазни видео уређај (V4L2)" +V4L2Input="Уређај за снимање видеа (V4L2)" Device="Уређај" Input="Улаз" VideoFormat="Видео формат" diff --git a/plugins/linux-v4l2/data/locale/sv-SE.ini b/plugins/linux-v4l2/data/locale/sv-SE.ini index 25b0486..4d96e71 100644 --- a/plugins/linux-v4l2/data/locale/sv-SE.ini +++ b/plugins/linux-v4l2/data/locale/sv-SE.ini @@ -8,4 +8,7 @@ Resolution="Upplösning" FrameRate="Bildhastighet" LeaveUnchanged="Lämna oförändrat" UseBuffering="Använd buffer" +ColorRange="Färgintervall" +ColorRange.Partial="Delvis" +ColorRange.Full="Full" diff --git a/plugins/linux-v4l2/data/locale/tr-TR.ini b/plugins/linux-v4l2/data/locale/tr-TR.ini index 544d6a1..ad36b52 100644 --- a/plugins/linux-v4l2/data/locale/tr-TR.ini +++ b/plugins/linux-v4l2/data/locale/tr-TR.ini @@ -8,4 +8,7 @@ Resolution="Çözünürlük" FrameRate="Kare Hızı" LeaveUnchanged="Değişmeden Bırak" UseBuffering="Arabelleğe Almayı Kullan" +ColorRange="Renk Aralığı" +ColorRange.Partial="Kısmi" +ColorRange.Full="Tam" diff --git a/plugins/linux-v4l2/data/locale/uk-UA.ini b/plugins/linux-v4l2/data/locale/uk-UA.ini index 3305e16..2e4614e 100644 --- a/plugins/linux-v4l2/data/locale/uk-UA.ini +++ b/plugins/linux-v4l2/data/locale/uk-UA.ini @@ -8,4 +8,7 @@ Resolution="Роздільна здатність" FrameRate="Частота кадрів" LeaveUnchanged="Залишити без змін" UseBuffering="Увімкнути буферизацію" +ColorRange="Колірний діапазон" +ColorRange.Partial="Частковий" +ColorRange.Full="Повний" diff --git a/plugins/linux-v4l2/data/locale/zh-CN.ini b/plugins/linux-v4l2/data/locale/zh-CN.ini index 6955936..68c0c1f 100644 --- a/plugins/linux-v4l2/data/locale/zh-CN.ini +++ b/plugins/linux-v4l2/data/locale/zh-CN.ini @@ -8,4 +8,7 @@ Resolution="分辨率" FrameRate="帧率" LeaveUnchanged="保持不变" UseBuffering="使用缓冲" +ColorRange="颜色范围" +ColorRange.Partial="部分" +ColorRange.Full="全部" diff --git a/plugins/linux-v4l2/data/locale/zh-TW.ini b/plugins/linux-v4l2/data/locale/zh-TW.ini index 66a8387..5a00fd5 100644 --- a/plugins/linux-v4l2/data/locale/zh-TW.ini +++ b/plugins/linux-v4l2/data/locale/zh-TW.ini @@ -8,4 +8,7 @@ Resolution="解析度" FrameRate="影格率" LeaveUnchanged="不改變並離開" UseBuffering="使用緩衝" +ColorRange="顏色範圍" +ColorRange.Partial="部分" +ColorRange.Full="完整" diff --git a/plugins/linux-v4l2/linux-v4l2.c b/plugins/linux-v4l2/linux-v4l2.c index 287e10c..b57deaa 100644 --- a/plugins/linux-v4l2/linux-v4l2.c +++ b/plugins/linux-v4l2/linux-v4l2.c @@ -18,6 +18,10 @@ along with this program. If not, see . OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("linux-v4l2", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Video4Linux2(V4L2) sources"; +} extern struct obs_source_info v4l2_input; diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 46726ee..d3803c1 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -75,6 +75,7 @@ struct v4l2_data { int dv_timing; int resolution; int framerate; + int color_range; /* internal data */ obs_source_t *source; @@ -112,7 +113,7 @@ static void v4l2_prep_obs_frame(struct v4l2_data *data, frame->width = data->width; frame->height = data->height; frame->format = v4l2_to_obs_video_format(data->pixfmt); - video_format_get_parameters(VIDEO_CS_DEFAULT, VIDEO_RANGE_PARTIAL, + video_format_get_parameters(VIDEO_CS_DEFAULT, data->color_range, frame->color_matrix, frame->color_range_min, frame->color_range_max); @@ -231,6 +232,7 @@ static void v4l2_defaults(obs_data_t *settings) obs_data_set_default_int(settings, "dv_timing", -1); obs_data_set_default_int(settings, "resolution", -1); obs_data_set_default_int(settings, "framerate", -1); + obs_data_set_default_int(settings, "color_range", VIDEO_RANGE_PARTIAL); obs_data_set_default_bool(settings, "buffering", true); } @@ -329,7 +331,11 @@ static void v4l2_device_list(obs_property_t *prop, obs_data_t *settings) continue; } - obs_property_list_add_string(prop, (char *) video_cap.card, + /* make sure device names are unique */ + char unique_device_name[68]; + sprintf(unique_device_name, "%s (%s)", video_cap.card, + video_cap.bus_info); + obs_property_list_add_string(prop, unique_device_name, device.array); blog(LOG_INFO, "Found device '%s' at %s", video_cap.card, device.array); @@ -759,6 +765,12 @@ static obs_properties_t *v4l2_properties(void *vptr) "framerate", obs_module_text("FrameRate"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_t *color_range_list = obs_properties_add_list(props, + "color_range", obs_module_text("ColorRange"), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(color_range_list, obs_module_text("ColorRange.Partial"), VIDEO_RANGE_PARTIAL); + obs_property_list_add_int(color_range_list, obs_module_text("ColorRange.Full"), VIDEO_RANGE_FULL); + obs_properties_add_bool(props, "buffering", obs_module_text("UseBuffering")); @@ -940,6 +952,7 @@ static void v4l2_update(void *vptr, obs_data_t *settings) data->dv_timing = obs_data_get_int(settings, "dv_timing"); data->resolution = obs_data_get_int(settings, "resolution"); data->framerate = obs_data_get_int(settings, "framerate"); + data->color_range = obs_data_get_int(settings, "color_range"); v4l2_update_source_flags(data, settings); diff --git a/plugins/mac-avcapture/av-capture.mm b/plugins/mac-avcapture/av-capture.mm index 2e2a591..446ba91 100644 --- a/plugins/mac-avcapture/av-capture.mm +++ b/plugins/mac-avcapture/av-capture.mm @@ -2175,6 +2175,10 @@ static void av_capture_update(void *data, obs_data_t *settings) OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("mac-avcapture", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "MacOS AVFoundation Capture source"; +} bool obs_module_load(void) { diff --git a/plugins/mac-avcapture/data/locale/ar-SA.ini b/plugins/mac-avcapture/data/locale/ar-SA.ini index 4fecc55..065a680 100644 --- a/plugins/mac-avcapture/data/locale/ar-SA.ini +++ b/plugins/mac-avcapture/data/locale/ar-SA.ini @@ -4,4 +4,11 @@ UsePreset="استعمال عارض ضوئي" Preset="العارض الضوئي" Buffering="استخدام التخزين المؤقت Buffering" FrameRate="معدل الإطار" +InputFormat="تنسيق الإدخال" +ColorSpace="مساحة الألوان" +VideoRange="مدى ألوان الفيديو" +VideoRange.Partial="جزئي" +VideoRange.Full="كامل" +Auto="تلقائي" +Unknown="غير معروف ($1)" diff --git a/plugins/mac-avcapture/data/locale/bg-BG.ini b/plugins/mac-avcapture/data/locale/bg-BG.ini index f9bc23a..9b17bcf 100644 --- a/plugins/mac-avcapture/data/locale/bg-BG.ini +++ b/plugins/mac-avcapture/data/locale/bg-BG.ini @@ -1,4 +1,14 @@ AVCapture="Устройство за записване на видео" Device="Устройство" +UsePreset="Използвай шаблона" +Preset="Шаблон" Buffering="Използвай буфериране" +FrameRate="Кадри в секунда" +InputFormat="Формат за входа" +ColorSpace="Цветово пространство" +VideoRange="Диапазон на видео" +VideoRange.Partial="Частичен" +VideoRange.Full="Пълен" +Auto="Автоматично" +Unknown="Неизвестно ($1)" diff --git a/plugins/mac-avcapture/data/locale/da-DK.ini b/plugins/mac-avcapture/data/locale/da-DK.ini index 921fb01..b43e4c2 100644 --- a/plugins/mac-avcapture/data/locale/da-DK.ini +++ b/plugins/mac-avcapture/data/locale/da-DK.ini @@ -1,14 +1,14 @@ -AVCapture="Video optagelsesenhed" +AVCapture="Videooptageenhed" Device="Enhed" -UsePreset="Brug forudindstilling" -Preset="Forudindstilling" +UsePreset="Benyt forvalg" +Preset="Forvalg" Buffering="Brug buffering" -FrameRate="Billedfrekvens" -InputFormat="Input format" +FrameRate="Billedhastighed" +InputFormat="Inputformat" ColorSpace="Farverum" -VideoRange="Videoudstrækning" +VideoRange="Videoområde" VideoRange.Partial="Delvis" VideoRange.Full="Fuld" Auto="Auto" -Unknown="Ukendt($1)" +Unknown="Ukendt ($1)" diff --git a/plugins/mac-avcapture/data/locale/de-DE.ini b/plugins/mac-avcapture/data/locale/de-DE.ini index e2a9570..c4bd593 100644 --- a/plugins/mac-avcapture/data/locale/de-DE.ini +++ b/plugins/mac-avcapture/data/locale/de-DE.ini @@ -2,11 +2,11 @@ AVCapture="Videoaufnahmegerät" Device="Gerät" UsePreset="Benutze Voreinstellung" Preset="Voreinstellung" -Buffering="Buffering benutzen" +Buffering="Puffern benutzen" FrameRate="Bildrate" InputFormat="Eingabeformat" ColorSpace="Farbraum" -VideoRange="Video-Bereich" +VideoRange="Videobereich" VideoRange.Partial="Teilweise" VideoRange.Full="Voll" Auto="Auto" diff --git a/plugins/mac-avcapture/data/locale/eu-ES.ini b/plugins/mac-avcapture/data/locale/eu-ES.ini index 6b9ff97..84144f3 100644 --- a/plugins/mac-avcapture/data/locale/eu-ES.ini +++ b/plugins/mac-avcapture/data/locale/eu-ES.ini @@ -1,6 +1,6 @@ AVCapture="Bideoa kapturatzeko gailua" Device="Gailua" -UsePreset="Erabili aurrezarpena" +UsePreset="Erabili aurre-ezarpena" Preset="Aurre-ezarpena" Buffering="Erabili bufferreratzea" FrameRate="Fotograma emaria" diff --git a/plugins/mac-avcapture/data/locale/fa-IR.ini b/plugins/mac-avcapture/data/locale/fa-IR.ini new file mode 100644 index 0000000..ab6b22b --- /dev/null +++ b/plugins/mac-avcapture/data/locale/fa-IR.ini @@ -0,0 +1,14 @@ +AVCapture="دستگاه ضبط ویدئویی" +Device="دستگاه" +UsePreset="استفاده از پیشفرض" +Preset="پیش فرض" +Buffering="استفاده از بافرینگ" +FrameRate="نرخ فریم" +InputFormat="فرمت های ورودی" +ColorSpace="فضای رنگی" +VideoRange="محدوده ویدئو" +VideoRange.Partial="جزئی" +VideoRange.Full="کامل" +Auto="خودکار" +Unknown="ناشناخته ($1)" + diff --git a/plugins/mac-avcapture/data/locale/fr-FR.ini b/plugins/mac-avcapture/data/locale/fr-FR.ini index 895e90b..c9d56bf 100644 --- a/plugins/mac-avcapture/data/locale/fr-FR.ini +++ b/plugins/mac-avcapture/data/locale/fr-FR.ini @@ -1,12 +1,12 @@ AVCapture="Périphérique de capture vidéo" Device="Périphérique" -UsePreset="Utiliser les réglages par défaut" -Preset="Réglages par défaut" -Buffering="Utiliser la mise en mémoire tampon" -FrameRate="Fréquence d'image" +UsePreset="Utiliser un pré-réglage" +Preset="Pré-réglage" +Buffering="Utiliser le tampon mémoire" +FrameRate="Débit d'images (nombre d'images par seconde)" InputFormat="Format d'entrée" ColorSpace="Espace colorimétrique" -VideoRange="Gamme vidéo" +VideoRange="Gamme de couleurs" VideoRange.Partial="Partielle" VideoRange.Full="Complète" Auto="Auto" diff --git a/plugins/mac-avcapture/data/locale/it-IT.ini b/plugins/mac-avcapture/data/locale/it-IT.ini index 42d5dc7..c318843 100644 --- a/plugins/mac-avcapture/data/locale/it-IT.ini +++ b/plugins/mac-avcapture/data/locale/it-IT.ini @@ -1,14 +1,14 @@ AVCapture="Dispositivo di cattura video" Device="Dispositivo" -UsePreset="Usa il pre impostato" -Preset="Preimpostato" -Buffering="Usa Buffer" -FrameRate="Frame rate" -InputFormat="Formato di input" +UsePreset="Utilizza il preset" +Preset="Preset" +Buffering="Utilizza il buffering" +FrameRate="Velocità dei fotogrammi" +InputFormat="Formato di ingresso" ColorSpace="Spazio colore" -VideoRange="Gamma video" +VideoRange="Gamma di colori" VideoRange.Partial="Parziale" -VideoRange.Full="Intero" -Auto="Autom." +VideoRange.Full="Intera" +Auto="Automatica" Unknown="Sconosciuto ($1)" diff --git a/plugins/mac-avcapture/data/locale/ka-GE.ini b/plugins/mac-avcapture/data/locale/ka-GE.ini index d00fdbd..8aa910f 100644 --- a/plugins/mac-avcapture/data/locale/ka-GE.ini +++ b/plugins/mac-avcapture/data/locale/ka-GE.ini @@ -1,4 +1,4 @@ -AVCapture="ვიდეოს ჩამწერი მოწყობილობა" +AVCapture="ვიდეოს გადამღები მოწყობილობა" Device="მოწყობილობა" UsePreset="მზა პარამეტრებით სარგებლობა" Preset="მზა პარამეტრები" diff --git a/plugins/mac-avcapture/data/locale/ro-RO.ini b/plugins/mac-avcapture/data/locale/ro-RO.ini index a997f67..b246f32 100644 --- a/plugins/mac-avcapture/data/locale/ro-RO.ini +++ b/plugins/mac-avcapture/data/locale/ro-RO.ini @@ -2,7 +2,7 @@ AVCapture="Dispozitiv de captură video" Device="Dispozitiv" UsePreset="Folosește presetare" Preset="Presetare" -Buffering="Folosește buffering" +Buffering="Folosește zona tampon" FrameRate="Frecvență de cadre" InputFormat="Format de intrare" ColorSpace="Spațiu de culori" diff --git a/plugins/mac-avcapture/data/locale/sr-CS.ini b/plugins/mac-avcapture/data/locale/sr-CS.ini index 0c3ae05..86ea6d5 100644 --- a/plugins/mac-avcapture/data/locale/sr-CS.ini +++ b/plugins/mac-avcapture/data/locale/sr-CS.ini @@ -1,7 +1,7 @@ -AVCapture="Ulazni video uređaj" +AVCapture="Uređaj za snimanje videa" Device="Uređaj" -UsePreset="Koristi šablon" -Preset="Šablon" +UsePreset="Koristi predefinisana podešavanja" +Preset="Predefinisana podešavanja" Buffering="Koristi baferovanje" FrameRate="Frame rate" InputFormat="Format ulaza" diff --git a/plugins/mac-avcapture/data/locale/sr-SP.ini b/plugins/mac-avcapture/data/locale/sr-SP.ini index 5c6cda8..f5dfbda 100644 --- a/plugins/mac-avcapture/data/locale/sr-SP.ini +++ b/plugins/mac-avcapture/data/locale/sr-SP.ini @@ -1,7 +1,7 @@ -AVCapture="Улазни видео уређај" +AVCapture="Уређај за снимање видеа" Device="Уређај" -UsePreset="Користи шаблон" -Preset="Шаблон" +UsePreset="Користи предефинисана подешавања" +Preset="Предефинисана подешавања" Buffering="Користи баферовање" FrameRate="Frame rate" InputFormat="Формат улаза" diff --git a/plugins/mac-avcapture/data/locale/uk-UA.ini b/plugins/mac-avcapture/data/locale/uk-UA.ini index cf84450..5177afd 100644 --- a/plugins/mac-avcapture/data/locale/uk-UA.ini +++ b/plugins/mac-avcapture/data/locale/uk-UA.ini @@ -1,7 +1,7 @@ AVCapture="Відео пристрій" Device="Пристрій" UsePreset="Використовувати шаблон" -Preset="Пресет" +Preset="Шаблон" Buffering="Увімкнути буферизацію" FrameRate="Частота кадрів" InputFormat="Вхідний формат" diff --git a/plugins/mac-avcapture/data/locale/vi-VN.ini b/plugins/mac-avcapture/data/locale/vi-VN.ini index 48089de..a52a172 100644 --- a/plugins/mac-avcapture/data/locale/vi-VN.ini +++ b/plugins/mac-avcapture/data/locale/vi-VN.ini @@ -1,4 +1,5 @@ Device="Thiết bị" +UsePreset="Dùng mẫu có sẵn" Auto="Tự động" Unknown="Không xác định ($1)" diff --git a/plugins/mac-capture/CMakeLists.txt b/plugins/mac-capture/CMakeLists.txt index 6e7b2eb..50e34f6 100644 --- a/plugins/mac-capture/CMakeLists.txt +++ b/plugins/mac-capture/CMakeLists.txt @@ -14,7 +14,6 @@ include_directories(${COREAUDIO} set(mac-capture_HEADERS audio-device-enum.h - mac-helpers.h window-utils.h) set(mac-capture_SOURCES diff --git a/plugins/mac-capture/audio-device-enum.c b/plugins/mac-capture/audio-device-enum.c index bf7de8d..5a1d7b7 100644 --- a/plugins/mac-capture/audio-device-enum.c +++ b/plugins/mac-capture/audio-device-enum.c @@ -1,7 +1,8 @@ #include #include -#include "mac-helpers.h" +#include + #include "audio-device-enum.h" /* ugh, because mac has no means of capturing output, we have to basically @@ -114,9 +115,9 @@ static bool coreaudio_enum_add_device(void *param, CFStringRef cf_name, memset(&item, 0, sizeof(item)); - if (!cf_to_dstr(cf_name, &item.name)) + if (!cfstr_copy_dstr(cf_name, kCFStringEncodingUTF8, &item.name)) goto fail; - if (!cf_to_dstr(cf_uid, &item.value)) + if (!cfstr_copy_dstr(cf_uid, kCFStringEncodingUTF8, &item.value)) goto fail; if (data->input || !device_is_input(item.value.array)) diff --git a/plugins/mac-capture/data/locale/da-DK.ini b/plugins/mac-capture/data/locale/da-DK.ini index b784969..6b3c602 100644 --- a/plugins/mac-capture/data/locale/da-DK.ini +++ b/plugins/mac-capture/data/locale/da-DK.ini @@ -1,11 +1,11 @@ -CoreAudio.InputCapture="Indfang lyd ind" -CoreAudio.OutputCapture="Indfang lyd ud" +CoreAudio.InputCapture="Lydinputoptagelse" +CoreAudio.OutputCapture="Lydoutputoptagelse" CoreAudio.Device="Enhed" CoreAudio.Device.Default="Standard" -DisplayCapture="Skærm optag" +DisplayCapture="Skærmoptagelse" DisplayCapture.Display="Skærm" -DisplayCapture.ShowCursor="Vis markøren" -WindowCapture="Vindue indfang" +DisplayCapture.ShowCursor="Vis markør" +WindowCapture="Vinduesoptagelse" WindowCapture.ShowShadow="Vis vinduesskygge" WindowUtils.Window="Vindue" WindowUtils.ShowEmptyNames="Vis vinduer med tomme navne" diff --git a/plugins/mac-capture/data/locale/de-DE.ini b/plugins/mac-capture/data/locale/de-DE.ini index 90bc1dd..43cbbf5 100644 --- a/plugins/mac-capture/data/locale/de-DE.ini +++ b/plugins/mac-capture/data/locale/de-DE.ini @@ -1,14 +1,14 @@ -CoreAudio.InputCapture="Audio Eingabe Aufnahme" -CoreAudio.OutputCapture="Audio Ausgabe Aufnahme" +CoreAudio.InputCapture="Audioeingabeaufnahme" +CoreAudio.OutputCapture="Audioausgabeaufnahme" CoreAudio.Device="Gerät" CoreAudio.Device.Default="Standard" -DisplayCapture="Monitoraufnahme" -DisplayCapture.Display="Monitor" +DisplayCapture="Bildschirmaufnahme" +DisplayCapture.Display="Bildschirm" DisplayCapture.ShowCursor="Mauszeiger anzeigen" WindowCapture="Fensteraufnahme" -WindowCapture.ShowShadow="Zeige Fensterschatten" +WindowCapture.ShowShadow="Fensterschatten anzeigen" WindowUtils.Window="Fenster" -WindowUtils.ShowEmptyNames="Zeige Fenster mit leeren Namen" +WindowUtils.ShowEmptyNames="Fenster mit leeren Namen anzeigen" CropMode="Zuschneiden" CropMode.None="Keine" CropMode.Manual="Manuell" diff --git a/plugins/mac-capture/data/locale/fa-IR.ini b/plugins/mac-capture/data/locale/fa-IR.ini new file mode 100644 index 0000000..429a9ef --- /dev/null +++ b/plugins/mac-capture/data/locale/fa-IR.ini @@ -0,0 +1,21 @@ +CoreAudio.InputCapture="گرفتن صدای ورودی" +CoreAudio.OutputCapture="گرفتن صدای خروجی" +CoreAudio.Device="دستگاه" +CoreAudio.Device.Default="پیش فرض" +DisplayCapture="کپچر نمایش" +DisplayCapture.Display="نمایش" +DisplayCapture.ShowCursor="نمایش مکان نما" +WindowCapture="ویندوز کپچر" +WindowCapture.ShowShadow="نمایش سایه ویندوز" +WindowUtils.Window="ویندوز" +WindowUtils.ShowEmptyNames="نمایش پنجره با نام های خالی" +CropMode="بریدن" +CropMode.None="هیچ‌کدام" +CropMode.Manual="دستی" +CropMode.ToWindow="به پنجره" +CropMode.ToWindowAndManual="به پنجره و دستي" +Crop.origin.x="برش چپ" +Crop.origin.y="برش بالا" +Crop.size.width="برش راست" +Crop.size.height="برش پایین" + diff --git a/plugins/mac-capture/data/locale/fr-FR.ini b/plugins/mac-capture/data/locale/fr-FR.ini index 659dbc5..54f94b9 100644 --- a/plugins/mac-capture/data/locale/fr-FR.ini +++ b/plugins/mac-capture/data/locale/fr-FR.ini @@ -1,14 +1,14 @@ -CoreAudio.InputCapture="Capture de l'audio entrant" -CoreAudio.OutputCapture="Capture de l'audio sortant" +CoreAudio.InputCapture="Capture d'une Entrée Audio" +CoreAudio.OutputCapture="Capture Audio (Sorties)" CoreAudio.Device="Périphérique" -CoreAudio.Device.Default="Par défaut" -DisplayCapture="Afficher la capture" -DisplayCapture.Display="Affichage" +CoreAudio.Device.Default="Défaut" +DisplayCapture="Capture d'écran" +DisplayCapture.Display="Écran" DisplayCapture.ShowCursor="Afficher le curseur" -WindowCapture="Capture de la fenêtre" +WindowCapture="Capture de Fenêtre" WindowCapture.ShowShadow="Afficher l'ombre de la fenêtre" WindowUtils.Window="Fenêtre" -WindowUtils.ShowEmptyNames="Afficher les fenêtres avec des noms vides" +WindowUtils.ShowEmptyNames="Afficher les fenêtres sans noms" CropMode="Rogner" CropMode.None="Aucune" CropMode.Manual="Manuel" diff --git a/plugins/mac-capture/data/locale/it-IT.ini b/plugins/mac-capture/data/locale/it-IT.ini index 4e43f23..acf4c71 100644 --- a/plugins/mac-capture/data/locale/it-IT.ini +++ b/plugins/mac-capture/data/locale/it-IT.ini @@ -2,18 +2,18 @@ CoreAudio.InputCapture="Cattura l'audio in ingresso" CoreAudio.OutputCapture="Cattura l'audio in uscita" CoreAudio.Device="Dispositivo" CoreAudio.Device.Default="Predefinito" -DisplayCapture="Cattura schermo" -DisplayCapture.Display="Display" +DisplayCapture="Cattura lo schermo" +DisplayCapture.Display="Schermo" DisplayCapture.ShowCursor="Mostra il cursore" -WindowCapture="Cattura finestra" -WindowCapture.ShowShadow="Visualizza ombra finestra" +WindowCapture="Cattura la finestra" +WindowCapture.ShowShadow="Mostra l'ombra della finestra" WindowUtils.Window="Finestra" -WindowUtils.ShowEmptyNames="Visualizza finestre con senza nomi" +WindowUtils.ShowEmptyNames="Mostra le finestre con nomi vuoti" CropMode="Ritaglia" -CropMode.None="Nessuno" -CropMode.Manual="Manuale" +CropMode.None="Niente" +CropMode.Manual="Manualmente" CropMode.ToWindow="Alla finestra" -CropMode.ToWindowAndManual="Alla finestra manualmente" +CropMode.ToWindowAndManual="Alla finestra e manualmente" Crop.origin.x="Ritaglia a sinistra" Crop.origin.y="Ritaglia dall'alto" Crop.size.width="Ritaglia a destra" diff --git a/plugins/mac-capture/data/locale/ka-GE.ini b/plugins/mac-capture/data/locale/ka-GE.ini index 0f8b3eb..69d81de 100644 --- a/plugins/mac-capture/data/locale/ka-GE.ini +++ b/plugins/mac-capture/data/locale/ka-GE.ini @@ -1,5 +1,5 @@ CoreAudio.InputCapture="შემავალი ხმოვანი სიგნალის ჩაწერა" -CoreAudio.OutputCapture="გამომავალი ხმოვანი სიგნალის ჩაწერა" +CoreAudio.OutputCapture="გამოტანილი ხმის ჩაწერა" CoreAudio.Device="მოწყობილობა" CoreAudio.Device.Default="ნაგულისხმევი" DisplayCapture="ეკრანის გადაღება" diff --git a/plugins/mac-capture/data/locale/nl-NL.ini b/plugins/mac-capture/data/locale/nl-NL.ini index 1be3b06..56210fd 100644 --- a/plugins/mac-capture/data/locale/nl-NL.ini +++ b/plugins/mac-capture/data/locale/nl-NL.ini @@ -2,7 +2,7 @@ CoreAudio.InputCapture="Audioinvoer Opname" CoreAudio.OutputCapture="Audiouitvoer Opname" CoreAudio.Device="Apparaat" CoreAudio.Device.Default="Standaardinstellingen" -DisplayCapture="Beeldschermcapture" +DisplayCapture="Beeldschermopname" DisplayCapture.Display="Beeldscherm" DisplayCapture.ShowCursor="Cursor Weergeven" WindowCapture="Venstercapture" diff --git a/plugins/mac-capture/data/locale/ro-RO.ini b/plugins/mac-capture/data/locale/ro-RO.ini index 3f07ce8..9215d51 100644 --- a/plugins/mac-capture/data/locale/ro-RO.ini +++ b/plugins/mac-capture/data/locale/ro-RO.ini @@ -6,9 +6,9 @@ DisplayCapture="Captură de display" DisplayCapture.Display="Display" DisplayCapture.ShowCursor="Arată cursorul" WindowCapture="Captură de fereastră" -WindowCapture.ShowShadow="Arată umbra ferestrei" +WindowCapture.ShowShadow="Afișează umbra ferestrei" WindowUtils.Window="Fereastră" -WindowUtils.ShowEmptyNames="Arată ferestrele cu numele goale" +WindowUtils.ShowEmptyNames="Afișează ferestrele cu nume goale" CropMode="Trunchiază" CropMode.None="Niciunul" CropMode.Manual="Manual" diff --git a/plugins/mac-capture/data/locale/sr-CS.ini b/plugins/mac-capture/data/locale/sr-CS.ini index a3504e5..8b235e3 100644 --- a/plugins/mac-capture/data/locale/sr-CS.ini +++ b/plugins/mac-capture/data/locale/sr-CS.ini @@ -1,11 +1,11 @@ -CoreAudio.InputCapture="Ulaz zvuka" -CoreAudio.OutputCapture="Izlaz zvuka" +CoreAudio.InputCapture="Snimanje zvuka na ulazu" +CoreAudio.OutputCapture="Snimanje zvuka na izlazu" CoreAudio.Device="Uređaj" CoreAudio.Device.Default="Podrazumevano" -DisplayCapture="Prikaži ulaz" -DisplayCapture.Display="Ekran" +DisplayCapture="Prikaži snimak" +DisplayCapture.Display="Prikaži" DisplayCapture.ShowCursor="Prikaži kursor" -WindowCapture="Snimanje sa prozora" +WindowCapture="Snimanje prozora" WindowCapture.ShowShadow="Prikaži senku na prozoru" WindowUtils.Window="Prozor" WindowUtils.ShowEmptyNames="Prikaži i prozore bez imena" diff --git a/plugins/mac-capture/data/locale/sr-SP.ini b/plugins/mac-capture/data/locale/sr-SP.ini index 123ac46..2234d8a 100644 --- a/plugins/mac-capture/data/locale/sr-SP.ini +++ b/plugins/mac-capture/data/locale/sr-SP.ini @@ -1,11 +1,11 @@ -CoreAudio.InputCapture="Улаз звука" -CoreAudio.OutputCapture="Излаз звука" +CoreAudio.InputCapture="Снимање звука на улазу" +CoreAudio.OutputCapture="Снимање звука на излазу" CoreAudio.Device="Уређај" CoreAudio.Device.Default="Подразумевано" -DisplayCapture="Прикажи улаз" -DisplayCapture.Display="Екран" +DisplayCapture="Прикажи снимак" +DisplayCapture.Display="Прикажи" DisplayCapture.ShowCursor="Прикажи курсор" -WindowCapture="Снимање са прозора" +WindowCapture="Снимање прозора" WindowCapture.ShowShadow="Прикажи сенку на прозору" WindowUtils.Window="Прозор" WindowUtils.ShowEmptyNames="Прикажи и прозоре без имена" diff --git a/plugins/mac-capture/mac-audio.c b/plugins/mac-capture/mac-audio.c index 47b480d..7d94c64 100644 --- a/plugins/mac-capture/mac-audio.c +++ b/plugins/mac-capture/mac-audio.c @@ -7,8 +7,8 @@ #include #include #include +#include -#include "mac-helpers.h" #include "audio-device-enum.h" #define PROPERTY_DEFAULT_DEVICE kAudioHardwarePropertyDefaultInputDevice @@ -496,7 +496,7 @@ static bool coreaudio_get_device_name(struct coreaudio_data *ca) { CFStringRef cf_name = NULL; UInt32 size = sizeof(CFStringRef); - char name[1024]; + char *name = NULL; const AudioObjectPropertyAddress addr = { kAudioDevicePropertyDeviceNameCFString, @@ -512,14 +512,15 @@ static bool coreaudio_get_device_name(struct coreaudio_data *ca) return false; } - if (!cf_to_cstr(cf_name, name, 1024)) { + name = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8); + if (!name) { blog(LOG_WARNING, "[coreaudio_get_device_name] failed to " "convert name to cstr for some reason"); return false; } bfree(ca->device_name); - ca->device_name = bstrdup(name); + ca->device_name = name; if (cf_name) CFRelease(cf_name); diff --git a/plugins/mac-capture/mac-helpers.h b/plugins/mac-capture/mac-helpers.h deleted file mode 100644 index e77b450..0000000 --- a/plugins/mac-capture/mac-helpers.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -static inline bool mac_success(OSStatus stat, const char *action) -{ - if (stat != noErr) { - blog(LOG_WARNING, "%s failed: %d", action, (int)stat); - return false; - } - - return true; -} - -static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size) -{ - if (!ref) return false; - return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8); -} - -static inline bool cf_to_dstr(CFStringRef ref, struct dstr *str) -{ - size_t size; - if (!ref) return false; - - size = (size_t)CFStringGetLength(ref); - if (!size) - return false; - - dstr_resize(str, size); - - return (bool)CFStringGetCString(ref, str->array, size+1, - kCFStringEncodingUTF8); -} diff --git a/plugins/mac-capture/plugin-main.c b/plugins/mac-capture/plugin-main.c index 4f6698d..2efa3a0 100644 --- a/plugins/mac-capture/plugin-main.c +++ b/plugins/mac-capture/plugin-main.c @@ -2,6 +2,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("mac-capture", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "macOS audio input/output and window/display capture"; +} extern struct obs_source_info coreaudio_input_capture_info; extern struct obs_source_info coreaudio_output_capture_info; diff --git a/plugins/mac-syphon/data/locale/ar-SA.ini b/plugins/mac-syphon/data/locale/ar-SA.ini index 55a9c1f..4c8f1ed 100644 --- a/plugins/mac-syphon/data/locale/ar-SA.ini +++ b/plugins/mac-syphon/data/locale/ar-SA.ini @@ -1,4 +1,3 @@ -Syphon="التقاط لعبة (Syphon)" Source="مصدر" LaunchSyphonInject="بدء تشغيل حقن SyphonInject" Inject="حقن" diff --git a/plugins/mac-syphon/data/locale/bn-BD.ini b/plugins/mac-syphon/data/locale/bn-BD.ini index 33bec3d..37765a2 100644 --- a/plugins/mac-syphon/data/locale/bn-BD.ini +++ b/plugins/mac-syphon/data/locale/bn-BD.ini @@ -1,4 +1,3 @@ -Syphon="খেলার অধিগ্রহণ (Syphon)" Source="উৎস" Application="অ্যাপ্লিকেশন" SyphonLicense="Syphon লাইসেন্স" diff --git a/plugins/mac-syphon/data/locale/ca-ES.ini b/plugins/mac-syphon/data/locale/ca-ES.ini index c54badb..5935542 100644 --- a/plugins/mac-syphon/data/locale/ca-ES.ini +++ b/plugins/mac-syphon/data/locale/ca-ES.ini @@ -1,4 +1,4 @@ -Syphon="Captura de videojoc (Syphon)" +Syphon="Client Syphon" Source="Origen" LaunchSyphonInject="Executa SyphonInject" Inject="Injecta" diff --git a/plugins/mac-syphon/data/locale/cs-CZ.ini b/plugins/mac-syphon/data/locale/cs-CZ.ini index 3e48974..ee4d195 100644 --- a/plugins/mac-syphon/data/locale/cs-CZ.ini +++ b/plugins/mac-syphon/data/locale/cs-CZ.ini @@ -1,4 +1,4 @@ -Syphon="Záznam hry (Syphon)" +Syphon="Klient Syphon" Source="Zdroj" LaunchSyphonInject="Spustit SyphonInject" Inject="Injektovat" diff --git a/plugins/mac-syphon/data/locale/da-DK.ini b/plugins/mac-syphon/data/locale/da-DK.ini index 5ed2c5f..681e6f8 100644 --- a/plugins/mac-syphon/data/locale/da-DK.ini +++ b/plugins/mac-syphon/data/locale/da-DK.ini @@ -1,9 +1,9 @@ -Syphon="Indfang spil (Syphon)" +Syphon="Syphon-klient" Source="Kilde" LaunchSyphonInject="Kør SyphonInject" -Inject="Indsæt" +Inject="Injicér" Application="Program" -SyphonLicense="Syphon licens" +SyphonLicense="Syphon-licens" Crop="Beskær" Crop.origin.x="Beskær venstre" Crop.origin.y="Beskær top" diff --git a/plugins/mac-syphon/data/locale/de-DE.ini b/plugins/mac-syphon/data/locale/de-DE.ini index 60201ff..eb3e43c 100644 --- a/plugins/mac-syphon/data/locale/de-DE.ini +++ b/plugins/mac-syphon/data/locale/de-DE.ini @@ -1,4 +1,4 @@ -Syphon="Spielaufnahme (Syphon)" +Syphon="Syphon-Client" Source="Quelle" LaunchSyphonInject="SyphonInject starten" Inject="Injizieren" @@ -9,5 +9,5 @@ Crop.origin.x="Links abschneiden" Crop.origin.y="Oben abschneiden" Crop.size.width="Rechts abschneiden" Crop.size.height="Unten abschneiden" -AllowTransparency="Erlaube Transparenz" +AllowTransparency="Transparenz erlauben" diff --git a/plugins/mac-syphon/data/locale/el-GR.ini b/plugins/mac-syphon/data/locale/el-GR.ini index 60a5bd7..799625a 100644 --- a/plugins/mac-syphon/data/locale/el-GR.ini +++ b/plugins/mac-syphon/data/locale/el-GR.ini @@ -1,4 +1,3 @@ -Syphon="Σύλληψη Παιχνιδιού (Syphon)" Source="Πηγή" LaunchSyphonInject="Έναρξη SyphonInject" Inject="Εισήγαγε" diff --git a/plugins/mac-syphon/data/locale/en-US.ini b/plugins/mac-syphon/data/locale/en-US.ini index 607eb5d..86550be 100644 --- a/plugins/mac-syphon/data/locale/en-US.ini +++ b/plugins/mac-syphon/data/locale/en-US.ini @@ -1,4 +1,4 @@ -Syphon="Game Capture (Syphon)" +Syphon="Syphon Client" Source="Source" LaunchSyphonInject="Launch SyphonInject" Inject="Inject" diff --git a/plugins/mac-syphon/data/locale/es-ES.ini b/plugins/mac-syphon/data/locale/es-ES.ini index 1402989..7e9aebb 100644 --- a/plugins/mac-syphon/data/locale/es-ES.ini +++ b/plugins/mac-syphon/data/locale/es-ES.ini @@ -1,4 +1,4 @@ -Syphon="Captura de juego (Syphon)" +Syphon="Syphon Client" Source="Fuente" LaunchSyphonInject="Lanzar SyphonInject" Inject="Inyectar" diff --git a/plugins/mac-syphon/data/locale/et-EE.ini b/plugins/mac-syphon/data/locale/et-EE.ini index 456813f..2718251 100644 --- a/plugins/mac-syphon/data/locale/et-EE.ini +++ b/plugins/mac-syphon/data/locale/et-EE.ini @@ -1,4 +1,3 @@ -Syphon="Mängu hõive (Syphon)" Source="Allikas" LaunchSyphonInject="Käivita SyphonInject" Inject="Sisesta" diff --git a/plugins/mac-syphon/data/locale/eu-ES.ini b/plugins/mac-syphon/data/locale/eu-ES.ini index 1a4c5f5..ce7fe8d 100644 --- a/plugins/mac-syphon/data/locale/eu-ES.ini +++ b/plugins/mac-syphon/data/locale/eu-ES.ini @@ -1,4 +1,4 @@ -Syphon="Jokoen kaptura (Syphon)" +Syphon="Syphon bezeroa" Source="Iturburua" LaunchSyphonInject="Abiarazi SyphonInject" Inject="Injektatu" diff --git a/plugins/mac-syphon/data/locale/fa-IR.ini b/plugins/mac-syphon/data/locale/fa-IR.ini new file mode 100644 index 0000000..fe38fac --- /dev/null +++ b/plugins/mac-syphon/data/locale/fa-IR.ini @@ -0,0 +1,12 @@ +Source="منبع" +LaunchSyphonInject="راه اندازی SyphonInject" +Inject="تزریق" +Application="برنامه" +SyphonLicense="مجوز syphon" +Crop="بریدن" +Crop.origin.x="برش چپ" +Crop.origin.y="برش بالا" +Crop.size.width="برش راست" +Crop.size.height="برش پایین" +AllowTransparency="اجازه شفافیت" + diff --git a/plugins/mac-syphon/data/locale/fi-FI.ini b/plugins/mac-syphon/data/locale/fi-FI.ini index 586fafc..9f43f87 100644 --- a/plugins/mac-syphon/data/locale/fi-FI.ini +++ b/plugins/mac-syphon/data/locale/fi-FI.ini @@ -1,4 +1,4 @@ -Syphon="Kaappaa peli (Syphon)" +Syphon="Syphon Client" Source="Lähde" LaunchSyphonInject="Käynnistä SyphonInject" Inject="Inject" diff --git a/plugins/mac-syphon/data/locale/fil-PH.ini b/plugins/mac-syphon/data/locale/fil-PH.ini index 18c8632..b416c8b 100644 --- a/plugins/mac-syphon/data/locale/fil-PH.ini +++ b/plugins/mac-syphon/data/locale/fil-PH.ini @@ -1,4 +1,3 @@ -Syphon="Pagkuha ng Laro (Siphon)" Source="Pinagkukunan" LaunchSyphonInject="Simulan ang SayponIngekt" Inject="Turukan" diff --git a/plugins/mac-syphon/data/locale/fr-FR.ini b/plugins/mac-syphon/data/locale/fr-FR.ini index 9e482a4..de78996 100644 --- a/plugins/mac-syphon/data/locale/fr-FR.ini +++ b/plugins/mac-syphon/data/locale/fr-FR.ini @@ -1,6 +1,6 @@ -Syphon="Capture de jeu (Syphon)" +Syphon="Client Syphon" Source="Source" -LaunchSyphonInject="Lancer SyphonInject" +LaunchSyphonInject="Démarrer SyphonInject" Inject="Injecter" Application="Application" SyphonLicense="Licence de Syphon" diff --git a/plugins/mac-syphon/data/locale/gl-ES.ini b/plugins/mac-syphon/data/locale/gl-ES.ini index a40f703..27b87bb 100644 --- a/plugins/mac-syphon/data/locale/gl-ES.ini +++ b/plugins/mac-syphon/data/locale/gl-ES.ini @@ -1,4 +1,3 @@ -Syphon="Captura de xogo (Syphon)" Source="Fonte" LaunchSyphonInject="Lanzar SyphonInject" Inject="Inxectar" diff --git a/plugins/mac-syphon/data/locale/hr-HR.ini b/plugins/mac-syphon/data/locale/hr-HR.ini index 34f6f38..1c84dab 100644 --- a/plugins/mac-syphon/data/locale/hr-HR.ini +++ b/plugins/mac-syphon/data/locale/hr-HR.ini @@ -1,4 +1,3 @@ -Syphon="Snimanje igre (Syphon)" Source="Izvor" LaunchSyphonInject="Pokreni SyphonInject" Inject="Ubrizgaj" diff --git a/plugins/mac-syphon/data/locale/hu-HU.ini b/plugins/mac-syphon/data/locale/hu-HU.ini index b920289..bb2250e 100644 --- a/plugins/mac-syphon/data/locale/hu-HU.ini +++ b/plugins/mac-syphon/data/locale/hu-HU.ini @@ -1,4 +1,4 @@ -Syphon="Játék felvétel (Syphon)" +Syphon="Syphon Kliens" Source="Forrás" LaunchSyphonInject="SyphonInject indítása" Inject="Fecskendezés" diff --git a/plugins/mac-syphon/data/locale/it-IT.ini b/plugins/mac-syphon/data/locale/it-IT.ini index 2204ca5..3c94b02 100644 --- a/plugins/mac-syphon/data/locale/it-IT.ini +++ b/plugins/mac-syphon/data/locale/it-IT.ini @@ -1,6 +1,6 @@ -Syphon="Cattura Gioco (Syphon)" -Source="Sorgente" -LaunchSyphonInject="Lancia SyphonInject" +Syphon="Client Syphon" +Source="Fonte" +LaunchSyphonInject="Avvia SyphonInject" Inject="Inserisci" Application="Applicazione" SyphonLicense="Licenza Syphon" @@ -9,5 +9,5 @@ Crop.origin.x="Ritaglia a sinistra" Crop.origin.y="Ritaglia dall'alto" Crop.size.width="Ritaglia a destra" Crop.size.height="Ritaglia dal basso" -AllowTransparency="Permetti trasparenza" +AllowTransparency="Permetti la trasparenza" diff --git a/plugins/mac-syphon/data/locale/ja-JP.ini b/plugins/mac-syphon/data/locale/ja-JP.ini index 47013a2..a62e8a3 100644 --- a/plugins/mac-syphon/data/locale/ja-JP.ini +++ b/plugins/mac-syphon/data/locale/ja-JP.ini @@ -1,4 +1,4 @@ -Syphon="ゲームキャプチャ (サイフォン)" +Syphon="サイフォンクライアント" Source="ソース" LaunchSyphonInject="サイフォンインジェクトを起動する" Inject="インジェクト" diff --git a/plugins/mac-syphon/data/locale/ka-GE.ini b/plugins/mac-syphon/data/locale/ka-GE.ini index f359593..1733ede 100644 --- a/plugins/mac-syphon/data/locale/ka-GE.ini +++ b/plugins/mac-syphon/data/locale/ka-GE.ini @@ -1,4 +1,4 @@ -Syphon="თამაშის გადაღება (Syphon)" +Syphon="Syphon-პროგრამა" Source="წყარო" LaunchSyphonInject="SyphonInject გაშვება" Inject="ჩადგმა" diff --git a/plugins/mac-syphon/data/locale/ko-KR.ini b/plugins/mac-syphon/data/locale/ko-KR.ini index ce4cce2..fb9fad8 100644 --- a/plugins/mac-syphon/data/locale/ko-KR.ini +++ b/plugins/mac-syphon/data/locale/ko-KR.ini @@ -1,4 +1,4 @@ -Syphon="게임 캡쳐 (Syphon)" +Syphon="Syphon 클라이언트" Source="소스" LaunchSyphonInject="SyphonInject 실행" Inject="삽입" diff --git a/plugins/mac-syphon/data/locale/nb-NO.ini b/plugins/mac-syphon/data/locale/nb-NO.ini index ef04687..2e530bf 100644 --- a/plugins/mac-syphon/data/locale/nb-NO.ini +++ b/plugins/mac-syphon/data/locale/nb-NO.ini @@ -1,4 +1,4 @@ -Syphon="Spillopptak (Syphon)" +Syphon="Syphon-klient" Source="Kilde" LaunchSyphonInject="Start SyphonInject" Inject="Injiser" diff --git a/plugins/mac-syphon/data/locale/nl-NL.ini b/plugins/mac-syphon/data/locale/nl-NL.ini index 8a89106..f9ac3b4 100644 --- a/plugins/mac-syphon/data/locale/nl-NL.ini +++ b/plugins/mac-syphon/data/locale/nl-NL.ini @@ -1,4 +1,4 @@ -Syphon="Gamecapture (Syphon)" +Syphon="Syphon cliënt" Source="Bron" LaunchSyphonInject="Start SyphonInject" Inject="Injecteren" diff --git a/plugins/mac-syphon/data/locale/pl-PL.ini b/plugins/mac-syphon/data/locale/pl-PL.ini index 988a710..58a8679 100644 --- a/plugins/mac-syphon/data/locale/pl-PL.ini +++ b/plugins/mac-syphon/data/locale/pl-PL.ini @@ -1,4 +1,4 @@ -Syphon="Przechwytywanie gry (Syphon)" +Syphon="Klient Syphon" Source="Źródło" LaunchSyphonInject="Uruchom SyphonInject" Inject="Załaduj" diff --git a/plugins/mac-syphon/data/locale/pt-BR.ini b/plugins/mac-syphon/data/locale/pt-BR.ini index def7cfa..54b7bbd 100644 --- a/plugins/mac-syphon/data/locale/pt-BR.ini +++ b/plugins/mac-syphon/data/locale/pt-BR.ini @@ -1,4 +1,4 @@ -Syphon="Captura de jogo (Syphon)" +Syphon="Cliente do Syphon" Source="Fonte" LaunchSyphonInject="Iniciar SyphonInject" Inject="Injetar" diff --git a/plugins/mac-syphon/data/locale/pt-PT.ini b/plugins/mac-syphon/data/locale/pt-PT.ini index 059c56c..bc77e3a 100644 --- a/plugins/mac-syphon/data/locale/pt-PT.ini +++ b/plugins/mac-syphon/data/locale/pt-PT.ini @@ -1,4 +1,3 @@ -Syphon="Captura de jogo (Syphon)" Source="Fonte" LaunchSyphonInject="Iniciar SyphonInject" Inject="Injetar" diff --git a/plugins/mac-syphon/data/locale/ro-RO.ini b/plugins/mac-syphon/data/locale/ro-RO.ini index 9cc2423..496b786 100644 --- a/plugins/mac-syphon/data/locale/ro-RO.ini +++ b/plugins/mac-syphon/data/locale/ro-RO.ini @@ -1,4 +1,3 @@ -Syphon="Captură de joc (Syphon)" Source="Sursă" LaunchSyphonInject="Lansează SyphonInject" Inject="Injectează" diff --git a/plugins/mac-syphon/data/locale/ru-RU.ini b/plugins/mac-syphon/data/locale/ru-RU.ini index 4ca0df3..0357836 100644 --- a/plugins/mac-syphon/data/locale/ru-RU.ini +++ b/plugins/mac-syphon/data/locale/ru-RU.ini @@ -1,4 +1,4 @@ -Syphon="Захват игры (Syphon)" +Syphon="Клиент Syphon" Source="Источник" LaunchSyphonInject="Запуск SyphonInject" Inject="Ввести" diff --git a/plugins/mac-syphon/data/locale/sk-SK.ini b/plugins/mac-syphon/data/locale/sk-SK.ini index c57f1b4..94fc06b 100644 --- a/plugins/mac-syphon/data/locale/sk-SK.ini +++ b/plugins/mac-syphon/data/locale/sk-SK.ini @@ -1,4 +1,4 @@ -Syphon="Zachytávanie hry (Syphon)" +Syphon="Syphon klient" Source="Zdroj" LaunchSyphonInject="Zapnúť SyphonInject" Inject="Zaviesť" @@ -9,5 +9,5 @@ Crop.origin.x="Orezanie vľavo" Crop.origin.y="Orezanie hore" Crop.size.width="Orezanie vpravo" Crop.size.height="Orezanie dole" -AllowTransparency="Povoliť transparentnosť" +AllowTransparency="Povoliť priehľadnosť" diff --git a/plugins/mac-syphon/data/locale/sl-SI.ini b/plugins/mac-syphon/data/locale/sl-SI.ini index bbab943..c56cc7c 100644 --- a/plugins/mac-syphon/data/locale/sl-SI.ini +++ b/plugins/mac-syphon/data/locale/sl-SI.ini @@ -1,4 +1,3 @@ -Syphon="Zajemanje igre (Syphon)" Source="Vir" LaunchSyphonInject="Zaženi SyphonInject" Inject="Vstavi" diff --git a/plugins/mac-syphon/data/locale/sr-CS.ini b/plugins/mac-syphon/data/locale/sr-CS.ini index 34f6f38..c59480f 100644 --- a/plugins/mac-syphon/data/locale/sr-CS.ini +++ b/plugins/mac-syphon/data/locale/sr-CS.ini @@ -1,4 +1,4 @@ -Syphon="Snimanje igre (Syphon)" +Syphon="Syphon klijent" Source="Izvor" LaunchSyphonInject="Pokreni SyphonInject" Inject="Ubrizgaj" diff --git a/plugins/mac-syphon/data/locale/sr-SP.ini b/plugins/mac-syphon/data/locale/sr-SP.ini index 90e5c1b..66454ae 100644 --- a/plugins/mac-syphon/data/locale/sr-SP.ini +++ b/plugins/mac-syphon/data/locale/sr-SP.ini @@ -1,4 +1,4 @@ -Syphon="Снимање игре (Syphon)" +Syphon="Syphon клијент" Source="Извор" LaunchSyphonInject="Покрени SyphonInject" Inject="Убризгај" diff --git a/plugins/mac-syphon/data/locale/sv-SE.ini b/plugins/mac-syphon/data/locale/sv-SE.ini index 7f37426..1145c82 100644 --- a/plugins/mac-syphon/data/locale/sv-SE.ini +++ b/plugins/mac-syphon/data/locale/sv-SE.ini @@ -1,4 +1,4 @@ -Syphon="Spelinspelning (Syphon)" +Syphon="Syphon-klient" Source="Källa" LaunchSyphonInject="Starta SyphonInject" Inject="Injicera" diff --git a/plugins/mac-syphon/data/locale/tr-TR.ini b/plugins/mac-syphon/data/locale/tr-TR.ini index c68d2f3..6800b97 100644 --- a/plugins/mac-syphon/data/locale/tr-TR.ini +++ b/plugins/mac-syphon/data/locale/tr-TR.ini @@ -1,4 +1,4 @@ -Syphon="Oyun Yakalama (Syphon)" +Syphon="Syphon İstemcisi" Source="Kaynak" LaunchSyphonInject="SyphonInject'i Başlat" Inject="Araya gir" diff --git a/plugins/mac-syphon/data/locale/uk-UA.ini b/plugins/mac-syphon/data/locale/uk-UA.ini index bcf0248..2517736 100644 --- a/plugins/mac-syphon/data/locale/uk-UA.ini +++ b/plugins/mac-syphon/data/locale/uk-UA.ini @@ -1,4 +1,4 @@ -Syphon="Захват гри (Syphon)" +Syphon="Syphon клієнт" Source="Джерело" LaunchSyphonInject="Запустити SyphonInject" Inject="Ввести" diff --git a/plugins/mac-syphon/data/locale/vi-VN.ini b/plugins/mac-syphon/data/locale/vi-VN.ini index d272325..e1e1b09 100644 --- a/plugins/mac-syphon/data/locale/vi-VN.ini +++ b/plugins/mac-syphon/data/locale/vi-VN.ini @@ -1,3 +1,9 @@ Source="Nguồn" Application="Ứng dụng" +Crop="Cắt" +Crop.origin.x="Cắt trái" +Crop.origin.y="Cắt trên" +Crop.size.width="Cắt phải" +Crop.size.height="Cắt dưới" +AllowTransparency="Cho phép trong suốt" diff --git a/plugins/mac-syphon/data/locale/zh-CN.ini b/plugins/mac-syphon/data/locale/zh-CN.ini index eb1e104..87cec6f 100644 --- a/plugins/mac-syphon/data/locale/zh-CN.ini +++ b/plugins/mac-syphon/data/locale/zh-CN.ini @@ -1,4 +1,4 @@ -Syphon="游戏捕获(Syphon)" +Syphon="Syphon客户端" Source="来源" LaunchSyphonInject="启动 SyphonInject" Inject="注入" diff --git a/plugins/mac-syphon/data/locale/zh-TW.ini b/plugins/mac-syphon/data/locale/zh-TW.ini index 34028b2..7ece6e0 100644 --- a/plugins/mac-syphon/data/locale/zh-TW.ini +++ b/plugins/mac-syphon/data/locale/zh-TW.ini @@ -1,4 +1,4 @@ -Syphon="遊戲擷取 (Syphon)" +Syphon="Syphon用戶端" Source="來源" LaunchSyphonInject="啟動 SyphonInject" Inject="注入" diff --git a/plugins/mac-syphon/plugin-main.c b/plugins/mac-syphon/plugin-main.c index 011e711..e319d56 100644 --- a/plugins/mac-syphon/plugin-main.c +++ b/plugins/mac-syphon/plugin-main.c @@ -2,6 +2,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("syphon", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Syphon frame sharing"; +} extern struct obs_source_info syphon_info; diff --git a/plugins/mac-vth264/data/locale/da-DK.ini b/plugins/mac-vth264/data/locale/da-DK.ini index fbedef8..f30c85b 100644 --- a/plugins/mac-vth264/data/locale/da-DK.ini +++ b/plugins/mac-vth264/data/locale/da-DK.ini @@ -5,7 +5,7 @@ Bitrate="Bit-hastighed" UseMaxBitrate="Begræns bit-hastighed" MaxBitrate="Maks. bit-hastighed" MaxBitrateWindow="Maks. bit-hastighedsvindue (sekunder)" -KeyframeIntervalSec="Keyframe interval (sekunder, 0= auto)" +KeyframeIntervalSec="Keyframe-interval (sekunder, 0= auto)" Profile="Profil" None="(ingen)" DefaultEncoder="(Standard Encoder)" diff --git a/plugins/mac-vth264/data/locale/de-DE.ini b/plugins/mac-vth264/data/locale/de-DE.ini index b095bd3..fc0eda5 100644 --- a/plugins/mac-vth264/data/locale/de-DE.ini +++ b/plugins/mac-vth264/data/locale/de-DE.ini @@ -1,14 +1,14 @@ -VTH264EncHW="Apple VT H264 Hardware Codierer" -VTH264EncSW="Apple VT H264 Software Codierer" -VTEncoder="VideoToolbox Codierer" +VTH264EncHW="Apple-VT-H264-Hardware-Kodierer" +VTH264EncSW="Apple-VT-H264-Software-Kodierer" +VTEncoder="VideoToolbox-Kodierer" Bitrate="Bitrate" UseMaxBitrate="Limitiere Bitrate" -MaxBitrate="Maximale Bitrate" -MaxBitrateWindow="Maximales Bitrate Fenster (Sekunden)" -KeyframeIntervalSec="Keyframeintervall (Sekunden, 0=auto)" +MaxBitrate="Max. Bitrate" +MaxBitrateWindow="Maximales Bitratenfenster (Sekunden)" +KeyframeIntervalSec="Keyframeintervall (Sekunden, 0 = auto)" Profile="Profil" None="(Nichts)" -DefaultEncoder="(Standard-Codierer)" +DefaultEncoder="(Standardkodierer)" UseBFrames="B-Frames verwenden" diff --git a/plugins/mac-vth264/data/locale/fa-IR.ini b/plugins/mac-vth264/data/locale/fa-IR.ini new file mode 100644 index 0000000..150397c --- /dev/null +++ b/plugins/mac-vth264/data/locale/fa-IR.ini @@ -0,0 +1,14 @@ +VTH264EncHW="تخمین سخت افزار اپل VT H264" +VTH264EncSW="رمزگذار نرم افزار اپل VT H264" +VTEncoder="تخمین VideoToolbox" +Bitrate="نرخ بیت" +UseMaxBitrate="بیت محدود" +MaxBitrate="حداکثر میزان نرخ بیت" +MaxBitrateWindow="پنجره حداکثر میزان نرخ بیت (ثانیه)" +KeyframeIntervalSec="فاصله Keyframe (ثانیه 0 = خودکار)" +Profile="پروفایل" +None="(هیچ‌کدام)" +DefaultEncoder="(رمزگذاری پیشفرض)" +UseBFrames="استفاده از فریم های B" + + diff --git a/plugins/mac-vth264/data/locale/fr-FR.ini b/plugins/mac-vth264/data/locale/fr-FR.ini index 21bc564..d0ce648 100644 --- a/plugins/mac-vth264/data/locale/fr-FR.ini +++ b/plugins/mac-vth264/data/locale/fr-FR.ini @@ -1,10 +1,10 @@ VTH264EncHW="Encodeur matériel Apple VT H264" VTH264EncSW="Encodeur logiciel Apple VT H264" VTEncoder="Encodeur VideoToolbox" -Bitrate="Bitrate" -UseMaxBitrate="Limiter le bitrate" -MaxBitrate="Bitrate maximal" -MaxBitrateWindow="Fenêtre de bitrate maximal (en secondes)" +Bitrate="Débit" +UseMaxBitrate="Limiter le débit" +MaxBitrate="Débit maximal" +MaxBitrateWindow="Fenêtre de débit maximal (en secondes)" KeyframeIntervalSec="Intervalle d'image-clé (en secondes, 0 = auto)" Profile="Profil" None="(Aucun)" diff --git a/plugins/mac-vth264/data/locale/it-IT.ini b/plugins/mac-vth264/data/locale/it-IT.ini index 6e9331f..25bbb60 100644 --- a/plugins/mac-vth264/data/locale/it-IT.ini +++ b/plugins/mac-vth264/data/locale/it-IT.ini @@ -1,14 +1,14 @@ -VTH264EncHW="Apple VT H264 Hardware Encoder" -VTH264EncSW="Apple VT H264 Software Encoder" -VTEncoder="VideoToolbox Encoder" -Bitrate="Bitrate" -UseMaxBitrate="Limite bitrate" -MaxBitrate="Bitrate massimo" -MaxBitrateWindow="Bitrate massimo finestra (secondi)" -KeyframeIntervalSec="Intervallo Keyframe (secondi, 0=automatico)" +VTH264EncHW="Codifica hardware Apple VT H264" +VTH264EncSW="Codifica software Apple VT H264" +VTEncoder="Codifica VideoToolbox" +Bitrate="Velocità in bit" +UseMaxBitrate="Limite della velocità in bit" +MaxBitrate="Velocità in bit massima" +MaxBitrateWindow="Velocità in bit massima per la finestra (in secondi)" +KeyframeIntervalSec="Intervallo dei fotogrammi chiave (in secondi, 0=automatico)" Profile="Profilo" -None="(Nessuno)" -DefaultEncoder="(Encoder Predefinito)" -UseBFrames="Usa B-Frames" +None="(nessuno)" +DefaultEncoder="(codifica predefinita)" +UseBFrames="Utilizza i B-Frame" diff --git a/plugins/mac-vth264/data/locale/zh-CN.ini b/plugins/mac-vth264/data/locale/zh-CN.ini index 0811aca..389dcaf 100644 --- a/plugins/mac-vth264/data/locale/zh-CN.ini +++ b/plugins/mac-vth264/data/locale/zh-CN.ini @@ -6,7 +6,7 @@ UseMaxBitrate="限制比特率" MaxBitrate="最大比特率" MaxBitrateWindow="最大比特率窗口 (秒)" KeyframeIntervalSec="关键帧间隔(秒, 0=自动)" -Profile="档案" +Profile="配置文件" None="(无)" DefaultEncoder="(默认编码器)" UseBFrames="使用 B 帧" diff --git a/plugins/mac-vth264/encoder.c b/plugins/mac-vth264/encoder.c index 71e5e89..83137dd 100644 --- a/plugins/mac-vth264/encoder.c +++ b/plugins/mac-vth264/encoder.c @@ -6,6 +6,8 @@ #include #include +#include + #include #define VT_LOG(level, format, ...) \ @@ -74,23 +76,20 @@ struct vt_h264_encoder static void log_osstatus(int log_level, struct vt_h264_encoder *enc, const char *context, OSStatus code) { + char *c_str = NULL; CFErrorRef err = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, code, NULL); CFStringRef str = CFErrorCopyDescription(err); - CFIndex length = CFStringGetLength(str); - CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, - kCFStringEncodingUTF8); - - char *c_str = malloc(max_size); - if (CFStringGetCString(str, c_str, max_size, kCFStringEncodingUTF8)) { + c_str = cfstr_copy_cstr(str, kCFStringEncodingUTF8); + if (c_str) { if (enc) VT_BLOG(log_level, "Error in %s: %s", context, c_str); else VT_LOG(log_level, "Error in %s: %s", context, c_str); } - free(c_str); + bfree(c_str); CFRelease(str); CFRelease(err); } @@ -875,14 +874,18 @@ static obs_properties_t *vt_h264_properties(void *unused) obs_properties_t *props = obs_properties_create(); obs_property_t *p; - obs_properties_add_int(props, "bitrate", TEXT_BITRATE, 50, 10000000, 1); + p = obs_properties_add_int(props, "bitrate", + TEXT_BITRATE, 50, 10000000, 50); + obs_property_int_set_suffix(p, " Kbps"); p = obs_properties_add_bool(props, "limit_bitrate", TEXT_USE_MAX_BITRATE); obs_property_set_modified_callback(p, limit_bitrate_modified); - obs_properties_add_int(props, "max_bitrate", TEXT_MAX_BITRATE, 50, - 10000000, 1); + p = obs_properties_add_int(props, "max_bitrate", TEXT_MAX_BITRATE, 50, + 10000000, 50); + obs_property_int_set_suffix(p, " Kbps"); + obs_properties_add_float(props, "max_bitrate_window", TEXT_MAX_BITRATE_WINDOW, 0.10f, 10.0f, 0.25f); diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 4c04ab1..34d08b0 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -13,6 +13,7 @@ set(obs-ffmpeg_HEADERS obs-ffmpeg-formats.h obs-ffmpeg-compat.h closest-pixel-format.h) + set(obs-ffmpeg_SOURCES obs-ffmpeg.c obs-ffmpeg-audio-encoders.c @@ -21,6 +22,21 @@ set(obs-ffmpeg_SOURCES obs-ffmpeg-mux.c obs-ffmpeg-source.c) +if(UNIX AND NOT APPLE) + list(APPEND obs-ffmpeg_SOURCES + obs-ffmpeg-vaapi.c) + LIST(APPEND obs-ffmpeg_PLATFORM_DEPS + ${LIBVA_LBRARIES}) +endif() + +if(WIN32) + list(APPEND obs-ffmpeg_SOURCES + jim-nvenc.c + jim-nvenc-helpers.c) + list(APPEND obs-ffmpeg_HEADERS + jim-nvenc.h) +endif() + add_library(obs-ffmpeg MODULE ${obs-ffmpeg_HEADERS} ${obs-ffmpeg_SOURCES}) diff --git a/plugins/obs-ffmpeg/data/locale/ar-SA.ini b/plugins/obs-ffmpeg/data/locale/ar-SA.ini index ec69c93..59e22e4 100644 --- a/plugins/obs-ffmpeg/data/locale/ar-SA.ini +++ b/plugins/obs-ffmpeg/data/locale/ar-SA.ini @@ -4,15 +4,11 @@ Bitrate="معدل النقل" Preset="الإعداد المسبق" -NVENC.Preset.default="الإفتراضي" -NVENC.Preset.hq="جودة عالية" -NVENC.Preset.hp="أداء عالي" -NVENC.Preset.bd="بلوراي Bluray" -NVENC.Level="المستوى" FFmpegSource="مصدر وسائط" LocalFile="ملف محلي" Looping="تكرار حلقي" +ClearOnMediaEnd="عدم إظهار شيء عند انتهاء التشغيل" Advanced="متقدم" ColorRange="نطاق ألوان YUV" ColorRange.Auto="تلقائي" diff --git a/plugins/obs-ffmpeg/data/locale/bg-BG.ini b/plugins/obs-ffmpeg/data/locale/bg-BG.ini index f4957f4..e772f17 100644 --- a/plugins/obs-ffmpeg/data/locale/bg-BG.ini +++ b/plugins/obs-ffmpeg/data/locale/bg-BG.ini @@ -1,8 +1,38 @@ FFmpegOutput="FFmpeg изход" +FFmpegAAC="Основен кодек FFmpeg AAC" +FFmpegOpus="Кодек FFmpeg Opus" Bitrate="Битрейт" - - - +MaxBitrate="Максимален битрейт" +Preset="Шаблон" +RateControl="Управление на битрейт" +KeyframeIntervalSec="Интервал на ключови кадри (секунди, 0=автоматично)" +Lossless="Без загуба на качество" + + +NVENC.Preset.default="Производителност" +NVENC.Preset.hq="Качество" +NVENC.Preset.hp="Максимална производителност" +NVENC.Preset.mq="Максимално качество" +NVENC.Preset.ll="Ниска латентност" +NVENC.Preset.llhq="Ниска латенност, високо качество" +NVENC.Preset.llhp="Ниска латентност, висока производителност" +NVENC.LookAhead="Предопределяне" +NVENC.CQLevel="Ниво CQ" + +FFmpegSource="Източник на медия" +LocalFile="Локален файл" +Looping="Преповтаряне" +Input="Вход" +InputFormat="Формат за вход" +BufferingMB="Мрежова буферизация (MB)" +HardwareDecode="Използване на хардуерно декодиране, ако е налично" +Advanced="Разширено" +RestartWhenActivated="Възобновява възпроизвеждането когато източникът е активен" +CloseFileWhenInactive="Затваряне на файла при неактивност" +ColorRange.Auto="Автоматично" +ColorRange.Partial="Частично" +ColorRange.Full="Пълно" +SpeedPercentage="Скорост (процент)" diff --git a/plugins/obs-ffmpeg/data/locale/bn-BD.ini b/plugins/obs-ffmpeg/data/locale/bn-BD.ini index 5fc90b2..4c4a17c 100644 --- a/plugins/obs-ffmpeg/data/locale/bn-BD.ini +++ b/plugins/obs-ffmpeg/data/locale/bn-BD.ini @@ -3,14 +3,9 @@ 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 427a74c..c701ea6 100644 --- a/plugins/obs-ffmpeg/data/locale/ca-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/ca-ES.ini @@ -1,23 +1,28 @@ FFmpegOutput="Sortida FFmpeg" -FFmpegAAC="Codificador FFmpeg AAC predeterminat" +FFmpegAAC="Codificador FFmpeg AAC per defecte" FFmpegOpus="Codificador Opus FFmpeg" Bitrate="Taxa de bits" +MaxBitrate="Taxa de bits màxima" Preset="Valors predefinits" RateControl="Control de freqüència" KeyframeIntervalSec="Interval de fotograma clau (en segons, 0 = automàtic)" Lossless="Sense pèrdues" -BFrames="B-frames" +BFrames="Fotogrames-B màxims" NVENC.Use2Pass="Utilitza codificació en dues passades" -NVENC.Preset.default="Per defecte" -NVENC.Preset.hq="Alta Qualitat" -NVENC.Preset.hp="Alt rendiment" -NVENC.Preset.bd="BluRay" +NVENC.Preset.default="Rendiment" +NVENC.Preset.hq="Qualitat" +NVENC.Preset.hp="Rendiment màxim" +NVENC.Preset.mq="Qualitat màxima" NVENC.Preset.ll="Latencia baixa" -NVENC.Preset.llhq="Latència baixa Alta Qualitat" -NVENC.Preset.llhp="Latència baixa alt rendiment" -NVENC.Level="Nivell" +NVENC.Preset.llhq="Qualitat de latència baixa" +NVENC.Preset.llhp="Rendiment de latència baixa" +NVENC.LookAhead="Previsió" +NVENC.LookAhead.ToolTip="Habilita els Fotogrames-B dinàmics.\n\nEn inhabilitar-ho, el codificador utilitzarà sempre el nombre de Fotogrames-B indicat a l'opció «Fotogrames-B màxims».\n\nEn habilitar-ho, augmentarà la qualitat visual només utilitzant els Fotogrames-B necessaris,\nfins el màxim permès per l'ús de la GPU." +NVENC.PsychoVisualTuning="Ajust psico visual" +NVENC.PsychoVisualTuning.ToolTip="Permet la configuració del codificador que optimitza l'ús de la taxa de bits per millorar la qualitat visual percebuda,\nespecialment en situacions amb gran moviment, a costa d'una major utilització de la GPU." +NVENC.CQLevel="Nivell de quantificació constant" FFmpegSource="Font multimèdia" LocalFile="Fitxer local" @@ -26,7 +31,7 @@ Input="Entrada" InputFormat="Format d'entrada" BufferingMB="Memòria intermèdia de xarxa (MB)" HardwareDecode="Usa la descodificació per maquinari si és disponible" -ClearOnMediaEnd="Amaga l'origen en acabar la reproducció" +ClearOnMediaEnd="No mostris res quan acabi la reproducció" Advanced="Avançat" RestartWhenActivated="Reinicia la reproducció quan la font estigui activa" CloseFileWhenInactive="Tanca el fitxer quan estigui inactiu" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Desa la repetició" HelperProcessFailed="No s'ha pogut iniciar el procés d'assistència de gravació. Comproveu que els arxius d'OBS no han estat bloquejats o eliminats per qualsevol programa de seguretat de tercers." UnableToWritePath="No s'ha pogut escriure a %1. Assegureu-vos que utilitzeu una ruta de gravació a la qual el vostre compte d'usuari tingui permisos per escriure i que hi hagi prou espai al disc." +WarnWindowsDefender="Si la protecció contra Ransomware del Windows 10 està activada, també pot causar aquest error. Proveu inhabilitant la protecció contra amenaces del Windows a la configuració de seguretat/virus i protecció." diff --git a/plugins/obs-ffmpeg/data/locale/cs-CZ.ini b/plugins/obs-ffmpeg/data/locale/cs-CZ.ini index 5f688a3..2df3cac 100644 --- a/plugins/obs-ffmpeg/data/locale/cs-CZ.ini +++ b/plugins/obs-ffmpeg/data/locale/cs-CZ.ini @@ -2,22 +2,27 @@ FFmpegOutput="Výstup FFmpegu" FFmpegAAC="Výchozí FFmpeg AAC enkodér" FFmpegOpus="FFmpeg Opus enkodér" Bitrate="Bitrate" +MaxBitrate="Maximální bitrate" Preset="Předvolba" RateControl="Řízení toku" KeyframeIntervalSec="Interval klíč. snímků (vteřiny, 0=auto)" Lossless="Lossless" -BFrames="B-frames" +BFrames="Maximum B-snímků" NVENC.Use2Pass="Použít dvoustupňové enkódování" -NVENC.Preset.default="Výchozí" -NVENC.Preset.hq="Vysoká kvalita" -NVENC.Preset.hp="Vysoký výkon" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Výkon" +NVENC.Preset.hq="Kvalita" +NVENC.Preset.hp="Maximální výkon" +NVENC.Preset.mq="Maximální kvalita" NVENC.Preset.ll="Nízká odezva" NVENC.Preset.llhq="Nízká odezva, vysoká kvalita" NVENC.Preset.llhp="Nízká odezva, vysoký výkon" -NVENC.Level="Úrověň" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Zapne dynamické B-snímky.\n\nPokud vypnuto, enkodér vždy použije číslo uvedené v nastavení 'Maximum B-snímků'.\n\nPokud zapnuto, zvýší se vizuální kvalita použitím tolika B-smínků, jak je potřeba, až do maxima,\nale za cenu zvýšení využití GPU." +NVENC.PsychoVisualTuning="Psycho Visual Tuning" +NVENC.PsychoVisualTuning.ToolTip="Povolí nastavení enkodéru, které optimalizuje využití bitratu pro zvýšení vnímané vizuální kvality,\npředevším v situacích s velkým množstvím pohybu, ale za cenu zvýšení využití GPU." +NVENC.CQLevel="CQ úroveň" FFmpegSource="Médium" LocalFile="Místní soubor" @@ -26,7 +31,7 @@ Input="Vstup" InputFormat="Formát vstupu" BufferingMB="Vyrovnávací paměť pro síť (MB)" HardwareDecode="Použít hardwarové dekódování, pokud je k dispozici" -ClearOnMediaEnd="Skrýt zdroj po skončení přehrávání" +ClearOnMediaEnd="Po skončení přehrávání nezobrazovat nic" Advanced="Pokročilé" RestartWhenActivated="Restartovat přehrávání poté, co je zdroj aktivován" CloseFileWhenInactive="Zavřít soubor při neaktivitě" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Uložit záznam" HelperProcessFailed="Nelze spustit pomocný proces nahrávání. Zkontrolujte prosím, zda nejsou soubory OBS blokovány či odstraněny některý z antivirových programů třetích stran." UnableToWritePath="Nelze zapsat do %1. Zkontrolujte prosím, zda používáte úložiště, do kterého může váš uživatelský účet zapisovat a je zde dostatek volného místa." +WarnWindowsDefender="Pokud je zapnuta ochrana proti ransomwaru (Windows 10), může to také způsobit tuto chybu. Zkuste vypnout funkci kontrolovaných složek v nastavení Zabezpečení Windows / Ochrana proti virům." diff --git a/plugins/obs-ffmpeg/data/locale/da-DK.ini b/plugins/obs-ffmpeg/data/locale/da-DK.ini index 25277c1..8b3aad3 100644 --- a/plugins/obs-ffmpeg/data/locale/da-DK.ini +++ b/plugins/obs-ffmpeg/data/locale/da-DK.ini @@ -1,43 +1,48 @@ -FFmpegOutput="FFmpeg output" +FFmpegOutput="FFmpeg-output" FFmpegAAC="FFmpeg Standard AAC Encoder" FFmpegOpus="FFmpeg Opus-encoder" Bitrate="Bitrate" +MaxBitrate="Maks. bithastighed" Preset="Forudindstillet" RateControl="Tempokontrol" KeyframeIntervalSec="Keyframe-interval (sekunder, 0= auto)" Lossless="Tabsfri" -BFrames="B-rammer" +BFrames="Maks. B-billeder" NVENC.Use2Pass="Benyt to-trins kodning" -NVENC.Preset.default="Standard" -NVENC.Preset.hq="Høj kvalitet" -NVENC.Preset.hp="Høj ydeevne" -NVENC.Preset.bd="BluRay" -NVENC.Preset.ll="Lav latens" -NVENC.Preset.llhq="Lav-latens høj kvalitet" -NVENC.Preset.llhp="Lav latens høj ydeevne" -NVENC.Level="Niveau" +NVENC.Preset.default="Ydeevne" +NVENC.Preset.hq="Kvalitet" +NVENC.Preset.hp="Maks. ydeevne" +NVENC.Preset.mq="Maks. kvalitet" +NVENC.Preset.ll="Lavlatens" +NVENC.Preset.llhq="Lavlatenskvalitet" +NVENC.Preset.llhp="Lavlatensydeevne" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Aktiverer dynamiske B-frames.\n\nHvis deaktiveret, benytter encoderen altid antallet af B-frames angivet i indstillingen 'Maks. B-frames'.\n\nHvis aktiveret, forøges den visuelle kvalitet ved kun at benytte det nødvendige antal B-frames op til maksimum\npå bekostning af forøget GPU-forbrug." +NVENC.PsychoVisualTuning="Psychovisuel tuning" +NVENC.PsychoVisualTuning.ToolTip="Aktiverer encoderindstillinger, som optimerer brugen af bithastighed for øget opfattet visuel kvalitet,\nisær i situationer med megen bevægelse, på bekostning af forøget GPU-forbrug." +NVENC.CQLevel="CQ-niveau" FFmpegSource="Mediekilde" LocalFile="Lokal fil" Looping="Gentagelse" Input="Input" -InputFormat="Input format" +InputFormat="Inputformat" BufferingMB="Netværksbuffering (MB)" -HardwareDecode="Brug hardwareafkodning når tilgængelige" -ClearOnMediaEnd="Skjul kilde når afspilning slutter" +HardwareDecode="Benyt hardwareafkodning, når tilgængelig" +ClearOnMediaEnd="Vis intet, når afspilning afsluttes" Advanced="Avanceret" -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." +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 nogen opstartsforsinkelse kan forekomme, når kilden genaktiveres." ColorRange="YUV-farveområde" ColorRange.Auto="Auto" ColorRange.Partial="Delvis" ColorRange.Full="Fuld" RestartMedia="Genstart Media" SpeedPercentage="Hastighed (procent)" -Seekable="Seekable" +Seekable="Søgbar" MediaFileFilter.AllMediaFiles="Alle mediefiler" MediaFileFilter.VideoFiles="Videofiler" @@ -47,6 +52,7 @@ MediaFileFilter.AllFiles="Alle filer" ReplayBuffer="Genafspilningsbuffer" ReplayBuffer.Save="Gem Genafspilning" -HelperProcessFailed="Kan ikke starte optagelseshjælperprocessen. Tjek at OBS-filer ikke blokeres eller er fjernet af noget 3. parts antivirus-/sikkerhedssoftware." -UnableToWritePath="Kan ikke skrive til %1. Tjek at du benytter en optagelsessti, som din brugerkonto har skriverettighed til, og at der er tilstrækkelig ledig diskplads." +HelperProcessFailed="Kan ikke starte optagelseshjælperprocessen. Tjek, at OBS-filer ikke blokeres eller er fjernet af 3. parts antivirus-/sikkerhedssoftware." +UnableToWritePath="Kan ikke skrive til %1. Tjek, at du benytter en optagelsessti, som din brugerkonto har skriverettighed til, og at der er tilstrækkelig ledig diskplads." +WarnWindowsDefender="Er Windows 10 Ransomware-beskyttelse aktiveret, kan dette også medføre denne fejl. Prøv at deaktivere kontrolleret mappeadgang i Windows-indstillingen Sikkerhed/Virus- og Trusselsbeskyttelse." diff --git a/plugins/obs-ffmpeg/data/locale/de-DE.ini b/plugins/obs-ffmpeg/data/locale/de-DE.ini index 2025bfb..ad95bdc 100644 --- a/plugins/obs-ffmpeg/data/locale/de-DE.ini +++ b/plugins/obs-ffmpeg/data/locale/de-DE.ini @@ -1,23 +1,28 @@ -FFmpegOutput="FFmpeg Ausgabe" -FFmpegAAC="FFmpeg Standard AAC Codierer" -FFmpegOpus="FFmpeg Opus Codierer" +FFmpegOutput="FFmpeg-Ausgabe" +FFmpegAAC="FFmpeg-Standard-AAC-Kodierer" +FFmpegOpus="FFmpeg-Opus-Kodierer" Bitrate="Bitrate" +MaxBitrate="Max. Bitrate" Preset="Voreinstellung" RateControl="Qualitäts Regulierungsmethode" -KeyframeIntervalSec="Keyframeintervall (Sekunden, 0=auto)" +KeyframeIntervalSec="Keyframeintervall in Sek. (0 = auto)" Lossless="Verlustfrei" -BFrames="B-frames" +BFrames="Max. B-Frames" -NVENC.Use2Pass="Benutze Two-Pass Codierung" -NVENC.Preset.default="Standard" -NVENC.Preset.hq="Hohe Qualität" -NVENC.Preset.hp="Hohe Leistung" -NVENC.Preset.bd="Bluray" +NVENC.Use2Pass="Two-Pass-Kodierung verwenden" +NVENC.Preset.default="Leistung" +NVENC.Preset.hq="Qualität" +NVENC.Preset.hp="Max. Leistung" +NVENC.Preset.mq="Max. Qualität" NVENC.Preset.ll="Niedrige Latenz" -NVENC.Preset.llhq="Niedrige Latenz, Hohe Qualität" -NVENC.Preset.llhp="Niedrige Latenz, Hohe Leistung" -NVENC.Level="Level" +NVENC.Preset.llhq="Niedrige Latenz + Qualität" +NVENC.Preset.llhp="Niedrige Latenz + Leistung" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Aktiviert dynamische B-Frames.\n\nWenn deaktiviert, wird der Kodierer immer die Anzahl der B-Frames verwenden, die in der „Max B-Frames“-Einstellung angegeben sind.\n\nWenn aktiviert, wird er die visuelle Qualität erhöhen, indem nur so viele B-Frames verwendet werden wie benötigt, bis zum Maximum,\nzu den Kosten einer erhöhten GPU-Nutzung." +NVENC.PsychoVisualTuning="Psycho-visuelle Optimierung" +NVENC.PsychoVisualTuning.ToolTip="Aktiviert Kodierereinstellungen, die die Verwendung der Bitrate für eine erhöhte wahrgenommene visuelle Qualität optimieren,\ninsbesondere in Situationen mit hoher Bewegung, zu Kosten einer erhöhten GPU-Nutzung." +NVENC.CQLevel="CQ-Level" FFmpegSource="Medienquelle" LocalFile="Lokale Datei" @@ -25,17 +30,17 @@ Looping="Endlosschleife" Input="Eingabe" InputFormat="Eingabeformat" BufferingMB="Netzwerkpufferung (MB)" -HardwareDecode="Verwende Hardwaredecodierung, falls verfügbar" -ClearOnMediaEnd="Quelle verbergen, wenn Wiedergabe endet" +HardwareDecode="Hardwaredekodierung verwenden, falls verfügbar" +ClearOnMediaEnd="Nichts anzeigen, wenn Wiedergabe endet" Advanced="Erweitert" 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." +CloseFileWhenInactive.ToolTip="Schließt die Datei, wenn die Quelle im Stream oder der Aufnahme nicht angezeigt wird.\nDies ermöglicht, dass die Datei geändert werden kann, wenn die Quelle nicht aktiv ist,\n aber es gibt wahrscheinlich etwas Startverzögerung, wenn die Quelle reaktiviert wird." ColorRange="YUV-Farbmatrix" ColorRange.Auto="Automatisch" ColorRange.Partial="Teilweise" ColorRange.Full="Voll" -RestartMedia="Medium neu starten" +RestartMedia="Medium neustarten" SpeedPercentage="Geschwindigkeit (Prozent)" Seekable="Durch­such­bar" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Replay speichern" HelperProcessFailed="Der Aufnahmehelferprozeß kann nicht gestartet werden. Überprüfen Sie, ob OBS-Dateien nicht von einer Drittanbieter Antiviren- / Sicherheitssoftware blockiert oder entfernt wurden." UnableToWritePath="Kann nicht zu %1 schreiben. Vergewissern Sie sich, dass Sie einen Aufnahmepfad verwenden, für das Ihr Benutzerkonto Schreibrechte hat und dass genügend Speicherplatz zur Verfügung steht." +WarnWindowsDefender="Wenn Windows-10-Ransomware-Schutz aktiviert ist, kann dies auch den Fehler auslösen. Versuchen Sie, den überwachten Ordnerzugriff in Windows-Sicherheit → Viren- & Bedrohungsschutz auszuschalten." diff --git a/plugins/obs-ffmpeg/data/locale/el-GR.ini b/plugins/obs-ffmpeg/data/locale/el-GR.ini index 77562dc..818f3c8 100644 --- a/plugins/obs-ffmpeg/data/locale/el-GR.ini +++ b/plugins/obs-ffmpeg/data/locale/el-GR.ini @@ -7,17 +7,9 @@ RateControl="Έλεγχος ρυθμού" KeyframeIntervalSec="Συχνότητα Καρέ-Κλειδιού (δευτερόλεπτα, 0=αυτόματο)" Lossless="Χωρίς απώλειες" -BFrames="Β-πλαίσια" NVENC.Use2Pass="Χρήση κωδικοποίησης δύο περασμάτων" -NVENC.Preset.default="Προεπιλογή" -NVENC.Preset.hq="Υψηλή Ποιότητα" -NVENC.Preset.hp="Υψηλές Επιδόσεις" -NVENC.Preset.bd="BluRay" NVENC.Preset.ll="Χαμηλή Καθυστέρηση" -NVENC.Preset.llhq="Χαμηλή Καθυστέρηση Υψηλή Ποιότητα" -NVENC.Preset.llhp="Χαμηλή Καθυστέρηση Υψηλές Επιδόσεις" -NVENC.Level="Επίπεδο" FFmpegSource="Πηγή Πολυμέσων" LocalFile="Τοπικό αρχείο" @@ -26,7 +18,6 @@ Input="Είσοδος" InputFormat="Μορφή Εισόδου" BufferingMB="Μέγεθος προσωρινης αποθήκευσης Δικτύου (MB)" HardwareDecode="Χρήση αποκωδικοποίησης υλικού όταν είναι διαθέσιμη" -ClearOnMediaEnd="Απόκρυψη πηγής όταν τελειώνει η αναπαραγωγή" Advanced="Σύνθετες επιλογές" RestartWhenActivated="Επανεκκίνηση της αναπαραγωγής όταν η πηγή γίνεται ξανά ενεργή" CloseFileWhenInactive="Κλείσιμο του αρχείου όταν η πηγή είναι ανενεργή" diff --git a/plugins/obs-ffmpeg/data/locale/en-US.ini b/plugins/obs-ffmpeg/data/locale/en-US.ini index 486aff1..7d762ac 100644 --- a/plugins/obs-ffmpeg/data/locale/en-US.ini +++ b/plugins/obs-ffmpeg/data/locale/en-US.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg Output" FFmpegAAC="FFmpeg Default AAC Encoder" FFmpegOpus="FFmpeg Opus Encoder" Bitrate="Bitrate" +MaxBitrate="Max Bitrate" Preset="Preset" RateControl="Rate Control" KeyframeIntervalSec="Keyframe Interval (seconds, 0=auto)" Lossless="Lossless" -BFrames="B-frames" +BFrames="Max B-frames" NVENC.Use2Pass="Use Two-Pass Encoding" -NVENC.Preset.default="Default" -NVENC.Preset.hq="High Quality" -NVENC.Preset.hp="High Performance" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Performance" +NVENC.Preset.hq="Quality" +NVENC.Preset.hp="Max Performance" +NVENC.Preset.mq="Max Quality" NVENC.Preset.ll="Low-Latency" -NVENC.Preset.llhq="Low-Latency High Quality" -NVENC.Preset.llhp="Low-Latency High Performance" -NVENC.Level="Level" +NVENC.Preset.llhq="Low-Latency Quality" +NVENC.Preset.llhp="Low-Latency Performance" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Enables dynamic B-frames.\n\nIf disabled, the encoder will always use the number of B-frames specified in the 'Max B-frames' setting.\n\nIf enabled, it will increase visual quality by only using however many B-frames are necessary, up to the maximum,\nat the cost of increased GPU utilization." +NVENC.PsychoVisualTuning="Psycho Visual Tuning" +NVENC.PsychoVisualTuning.ToolTip="Enables encoder settings that optimize the use of bitrate for increased perceived visual quality,\nespecially in situations with high motion, at the cost of increased GPU utilization." +NVENC.CQLevel="CQ Level" FFmpegSource="Media Source" LocalFile="Local File" @@ -26,11 +31,11 @@ Input="Input" InputFormat="Input Format" BufferingMB="Network Buffering (MB)" HardwareDecode="Use hardware decoding when available" -ClearOnMediaEnd="Hide source when playback ends" +ClearOnMediaEnd="Show nothing when playback ends" Advanced="Advanced" 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." +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" @@ -49,3 +54,4 @@ ReplayBuffer.Save="Save Replay" HelperProcessFailed="Unable to start the recording helper process. Check that OBS files have not been blocked or removed by any 3rd party antivirus / security software." UnableToWritePath="Unable to write to %1. Make sure you're using a recording path which your user account is allowed to write to and that there is sufficient disk space." +WarnWindowsDefender="If Windows 10 Ransomware Protection is enabled it can also cause this error. Try turning off controlled folder access in Windows Security / Virus & threat protection settings." diff --git a/plugins/obs-ffmpeg/data/locale/es-ES.ini b/plugins/obs-ffmpeg/data/locale/es-ES.ini index 9dcb1cf..f218ee1 100644 --- a/plugins/obs-ffmpeg/data/locale/es-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/es-ES.ini @@ -2,22 +2,27 @@ FFmpegOutput="Salida de FFmpeg" FFmpegAAC="Codificador AAC FFmpeg predeterminado" FFmpegOpus="Codificador Opus de FFmpeg" Bitrate="Tasa de bits" +MaxBitrate="Tasa de bits máxima" Preset="Preajuste" RateControl="Control de la frecuencia" KeyframeIntervalSec="Intervalo de keyframes (segundos, 0=auto)" Lossless="Sin pérdidas" -BFrames="B-Frames" +BFrames="Máximos B-frames" NVENC.Use2Pass="Usar codificación en dos pasadas" -NVENC.Preset.default="Por defecto" -NVENC.Preset.hq="Alta Calidad" -NVENC.Preset.hp="Alto rendimiento" -NVENC.Preset.bd="BluRay" +NVENC.Preset.default="Rendimiento" +NVENC.Preset.hq="Calidad" +NVENC.Preset.hp="Rendimiento Máximo" +NVENC.Preset.mq="Máxima Calidad" NVENC.Preset.ll="Baja latencia" -NVENC.Preset.llhq="Baja latencia alta calidad" -NVENC.Preset.llhp="Baja latencia alto rendimiento" -NVENC.Level="Nivel" +NVENC.Preset.llhq="Calidad de baja Latencia" +NVENC.Preset.llhp="Rendimiento de baja Latencia" +NVENC.LookAhead="\"Look-Ahead\"" +NVENC.LookAhead.ToolTip="Habilita B-frames dinámicos.\n\nSi se deshabilita, el codificador siempre usará el número de B-frames especificado en el ajuste 'B-frames máximos'.\n\nSi se habilita, incrementará la calidad visual haciendo uso solo de la cantidad de B-frames necesaria, hasta el máximo,\na costa de incrementar el uso de la GPU." +NVENC.PsychoVisualTuning="Psycho Visual Tuning" +NVENC.PsychoVisualTuning.ToolTip="Activa ajustes del codificador que optimizan el uso del bitrate para aumentar la calidad visual percibida,\nespecialmente en situaciones con alto movimiento, a costa de una mayor utilización de la GPU." +NVENC.CQLevel="Nivel de CQ" FFmpegSource="Fuente multimedia" LocalFile="Archivo local" @@ -26,7 +31,7 @@ Input="Entrada" InputFormat="Formato de entrada" BufferingMB="Almacenamiento búfer de Red (MB)" HardwareDecode="Utilizar la decodificación por hardware cuando esté disponible" -ClearOnMediaEnd="Ocultar la fuente cuando finaliza la reproducción" +ClearOnMediaEnd="No mostrar nada al terminar la reproducción" Advanced="Avanzado" RestartWhenActivated="Reiniciar la reproducción cuando la fuente esté activa" CloseFileWhenInactive="Cerrar archivo cuando esté inactivo" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Guardar repetición" HelperProcessFailed="No se pudo iniciar el proceso ayudante de grabación. Compruebe que los archivos de OBS no han sido bloqueados o eliminados por cualquier programa de seguridad externo." UnableToWritePath="No se pudo escribir a %1. Asegúrese de que utiliza una ruta de grabación que su cuenta de usuario tenga permisos para escribir y que haya suficiente espacio en disco." +WarnWindowsDefender="Si la protección del Ransomware de Windows 10 está activada, también puede causar este error. Intenta desactivar el acceso controlado a la carpeta en la configuración de protección de virus y virus de Windows." diff --git a/plugins/obs-ffmpeg/data/locale/et-EE.ini b/plugins/obs-ffmpeg/data/locale/et-EE.ini index 9fbb92d..bd6ea1d 100644 --- a/plugins/obs-ffmpeg/data/locale/et-EE.ini +++ b/plugins/obs-ffmpeg/data/locale/et-EE.ini @@ -7,21 +7,13 @@ Lossless="Kadudeta" NVENC.Use2Pass="Kasuta Two-Pass kodeeringut" -NVENC.Preset.default="Vaikimisi" -NVENC.Preset.hq="Kõrge kvaliteet" -NVENC.Preset.hp="Suur jõudlus" -NVENC.Preset.bd="Bluray" NVENC.Preset.ll="Madal-viivitus" -NVENC.Preset.llhq="Madal-viivitus Kõrge kvaliteediga" -NVENC.Preset.llhp="Madal-viivitus suure jõudlusega" -NVENC.Level="Tase" FFmpegSource="Meedia allikas" LocalFile="Kohalik fail" Looping="Korda" Input="Sisend" InputFormat="Sisestus formaat" -ClearOnMediaEnd="Peida allikas kui taasesitus lõppeb" RestartWhenActivated="Taaskäivita taasesitus, kui allikas muutub aktiivseks" ColorRange="YUV värviruumi vahemik" ColorRange.Auto="Automaatne" diff --git a/plugins/obs-ffmpeg/data/locale/eu-ES.ini b/plugins/obs-ffmpeg/data/locale/eu-ES.ini index 13d38a1..3c54c22 100644 --- a/plugins/obs-ffmpeg/data/locale/eu-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/eu-ES.ini @@ -1,23 +1,28 @@ FFmpegOutput="FFmpeg irteera" FFmpegAAC="FFmpeg lehenetsitako AAC Kodetzailea" FFmpegOpus="FFmpeg Opus kodetzailea" -Bitrate="Bit-tasa" -Preset="Aurrezarpena" +Bitrate="Bit-emaria" +MaxBitrate="Bit-emari maximoa" +Preset="Aurre-ezarpena" RateControl="Tasaren kontrola" KeyframeIntervalSec="Gako-fotogramen tartea (segundoak, 0=auto)" Lossless="Galerarik gabe" -BFrames="B-fotogramak" +BFrames="B-fotogramen maximoa" -NVENC.Use2Pass="Erabili bi urratseko kodeketa" -NVENC.Preset.default="Lehenetsia" -NVENC.Preset.hq="Kalitate handia" -NVENC.Preset.hp="Eraginkortasun handia" -NVENC.Preset.bd="Bluray" +NVENC.Use2Pass="Erabili bi pasaldiko kodeketa" +NVENC.Preset.default="Errendimendua" +NVENC.Preset.hq="Kalitatea" +NVENC.Preset.hp="Errendimendu maximoa" +NVENC.Preset.mq="Kalitate maximoa" NVENC.Preset.ll="Latentzia txikia" -NVENC.Preset.llhq="Latentzia txikia kalitate handia" -NVENC.Preset.llhp="Latentzia txikia eraginkortasun handia" -NVENC.Level="Maila" +NVENC.Preset.llhq="Latentzia txikiko kalitatea" +NVENC.Preset.llhp="Latentzia txikiko errendimendua" +NVENC.LookAhead="Aurrera begira" +NVENC.LookAhead.ToolTip="Gaitu B-fotograma dinamikoak.\n\nGalerazita badaude, kodetzaileak beti erabiliko du 'Max B-fotogramak' ezarpenetan zehazten den B-fotogramen kopurua.\n\nGaituta badaude, ikusmen kalitatea hobetuko du beharrezkoak diren B-fotogramen kopurua erabiliz, maximora iritsi arte\nordainez GPUren erabilera handituko bada ere." +NVENC.PsychoVisualTuning="Psycho Visual Tuning" +NVENC.PsychoVisualTuning.ToolTip="Aukera ematen du kodetzailea konfiguratzeko optimizatzeko bit-ratioaren erabilera antzemandako ikusmen kalitatea hobetzeko,\nbatez ere mugimendu handiko egoeretan, ordainean GPUren erabilera handituz." +NVENC.CQLevel="CQ maila" FFmpegSource="Multimedia iturburua" LocalFile="Tokiko fitxategia" @@ -26,7 +31,7 @@ Input="Sarrera" InputFormat="Sarrera formatua" BufferingMB="Sareko bufferreratzea (MB)" HardwareDecode="Erabili hardware deskodeketa eskuragarri dagoenean" -ClearOnMediaEnd="Ezkutatu iturburua erreprodukzioa amaitzean" +ClearOnMediaEnd="Erreprodukzioa bukatzean ez erakutsi ezer" Advanced="Aurreratua" RestartWhenActivated="Berrabiarazi erreprodukzioa iturburua aktiboa dagoenean" CloseFileWhenInactive="Itxi fitxategia inaktibo dagoenean" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Gorde erreprodukzioa" HelperProcessFailed="Ezin izan da grabaziorako laguntza prozesua hasi. Begiratu hirugarren beten antibirusa edo seguritate-softwareak ez duela OBS fitxategiak blokeatzen edo ezabatzen." UnableToWritePath="Ezin izan da idatzi %1-ean. Egiaztatu grabazio-tokian idazteko baimena duzula eta diskoan leku libre nahikoa dagoela." +WarnWindowsDefender="Windows 10 Ransomware Protection aktibatuta egoteak errore hori ere sor dezake. Gehitu OBS Windows segurtasuna/ birus eta mehatxuen babeserako ezarpenen kontrolpeko karpeten sarbide-zerrendara." diff --git a/plugins/obs-ffmpeg/data/locale/fa-IR.ini b/plugins/obs-ffmpeg/data/locale/fa-IR.ini new file mode 100644 index 0000000..ea127f2 --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/fa-IR.ini @@ -0,0 +1,43 @@ +FFmpegOutput="خروجی FFmpeg" +FFmpegAAC="پیش فرض FFmpeg تخمین AAC" +FFmpegOpus="اپوس FFmpeg تخمین" +Bitrate="نرخ بیت" +MaxBitrate="حداکثر میزان نرخ بیت" +Preset="پیش فرض" +RateControl="کنترل نرخ" +KeyframeIntervalSec="فاصله Keyframe (ثانیه 0 = خودکار)" +Lossless="بدون تلف" + +BFrames="حد اکثر فریم های B" + +NVENC.Use2Pass="استفاده از کدگذاری 2 گذر" +NVENC.Preset.default="اجرا (کارایی)" +NVENC.Preset.hq="کیفیت" +NVENC.Preset.hp="حد اکثر اجرا (کارایی)" +NVENC.Preset.mq="حداکثر کیفیت" +NVENC.Preset.ll="زمان تاخیر کم" +NVENC.Preset.llhq="کیفیت پایین و زمان تاخیر" +NVENC.Preset.llhp="زمان تاخیر کم عملکرد" +NVENC.LookAhead="پیش رو نگاه" + +LocalFile="فایل محلی" +Looping="چرخه" +Input="ورودی" +InputFormat="فرمت های ورودی" +BufferingMB="شبکه بافری (مگابایت)" +ColorRange.Auto="خودکار" +ColorRange.Partial="جزئی" +ColorRange.Full="کامل" +RestartMedia="راه اندازی مجدد رسانه ها" +SpeedPercentage="سرعت (درصد)" +Seekable="جستجوگر" + +MediaFileFilter.AllMediaFiles="تمامی فایل های رسانه" +MediaFileFilter.VideoFiles="فایل‌های ویدئویی" +MediaFileFilter.AudioFiles="فایل‌های صوتی" +MediaFileFilter.AllFiles="همه‌ی فایل ها" + +ReplayBuffer="پخش مجدد بافر" +ReplayBuffer.Save="ذخیره پخش مجدد" + + diff --git a/plugins/obs-ffmpeg/data/locale/fi-FI.ini b/plugins/obs-ffmpeg/data/locale/fi-FI.ini index 1f24578..e36e7a6 100644 --- a/plugins/obs-ffmpeg/data/locale/fi-FI.ini +++ b/plugins/obs-ffmpeg/data/locale/fi-FI.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg-ulostulo" FFmpegAAC="FFmpeg oletus AAC-enkooderi" FFmpegOpus="FFmpeg Opus -enkooderi" Bitrate="Bitrate" +MaxBitrate="Maksimi bitrate" Preset="Esiasetus" RateControl="Rate Control -tila" KeyframeIntervalSec="Keyframe-väli (sekunteina, 0=automaattinen)" Lossless="Häviötön" -BFrames="B-kehykset" +BFrames="Maksimi B-kehykset" NVENC.Use2Pass="Käytä Two-Pass enkoodausta" -NVENC.Preset.default="Oletusarvo" -NVENC.Preset.hq="Korkea laatu" -NVENC.Preset.hp="Korkea suorituskyky" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Suorituskyky" +NVENC.Preset.hq="Laatu" +NVENC.Preset.hp="Korkein suorituskyky" +NVENC.Preset.mq="Korkein laatu" NVENC.Preset.ll="Alhainen latenssi" NVENC.Preset.llhq="Alhainen latenssi, korkea laatu" NVENC.Preset.llhp="Alhainen latenssi, korkea suorituskyky" -NVENC.Level="Taso" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Käytä dynaamisia B-kehyksiä.\n\nPois päältä pidettäessä enkooderi käyttää aina B-kehysten lukumäärää joka on asetettu 'Maksimi B-kehykset' -kohdassa.\n\nKäytössä ollessaan se parantaa visuaalista laatua käyttäen vain tarvittavan määrän B-kehyksiä maksimiin asti,\nGPU:n käytön kustannuksella." +NVENC.PsychoVisualTuning="Psykovisuaalinen viritys" +NVENC.PsychoVisualTuning.ToolTip="Ottaa käyttöön enkooderin asetuksen GPU:n kustannuksella joka optimoi bitraten käytön\nkorkeampaan visuaaliseen laatuun varsinkin tilanteissa joissa liike on nopeaa." +NVENC.CQLevel="CQ-taso" FFmpegSource="Lisää media" LocalFile="Paikallinen tiedosto" @@ -26,7 +31,7 @@ Input="Sisääntulo" InputFormat="Sisääntulon muoto" BufferingMB="Verkon puskurointi (MB)" HardwareDecode="Käytä laitteistotason purkua, kun mahdollista" -ClearOnMediaEnd="Piilota lähde kun toisto päättyy" +ClearOnMediaEnd="Älä näytä mitään kun toisto päättyy" Advanced="Lisäasetukset" RestartWhenActivated="Aloita toisto uudelleen kun lähde aktivoituu" CloseFileWhenInactive="Sulje tiedosto, kun toimeton" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Tallenna uusinta" HelperProcessFailed="Nauhoitusavustaja-prosessin käynnistäminen ei onnistunut. Tarkista, että OBS-tiedostoja ei ole estetty tai poistettu virustutkan tai muun turvallisuusohjelman johdosta." UnableToWritePath="Ei voida kirjoittaa %1. Tarkista, että käytät oikeaa tiedostopolkua johon käyttäjätililläsi on kirjoitusoikeus. Tarkista myös, että levyllä on tarpeeksi tilaa." +WarnWindowsDefender="Tämä virhe saattaa aiheutua myös jos Windowsin Hyökkäysten esto on käytössä. Yritä kytkeä valvottujen kansioiden seuranta pois päältä Windowsin suojaus / Virusten ja uhkien torjunta -asetuksissa." diff --git a/plugins/obs-ffmpeg/data/locale/fil-PH.ini b/plugins/obs-ffmpeg/data/locale/fil-PH.ini index 7da7422..53a58a0 100644 --- a/plugins/obs-ffmpeg/data/locale/fil-PH.ini +++ b/plugins/obs-ffmpeg/data/locale/fil-PH.ini @@ -7,17 +7,9 @@ RateControl="Kontrolin ang Antas" KeyframeIntervalSec="Ang pagitan ng Keyframe (segundos, 0=auto)" Lossless="Lossless" -BFrames="B-frames" NVENC.Use2Pass="Gamitin ang Two-Pass Encoding" -NVENC.Preset.default="I-Default" -NVENC.Preset.hq="Itaas ang Kalidad" -NVENC.Preset.hp="Itaas ang Pagganap" -NVENC.Preset.bd="Bluray" NVENC.Preset.ll="Mababa na Latency" -NVENC.Preset.llhq="Mababa na Latency Mataas na kalidad" -NVENC.Preset.llhp="Mababa na Latency Mataas na pagganap" -NVENC.Level="Lebel" FFmpegSource="Pagkunan ng Media" LocalFile="Ang File na lokal" @@ -26,7 +18,6 @@ Input="Ang Input" InputFormat="Ang Format ng Input" BufferingMB="Nag buffering ang Network (MB)" HardwareDecode="Gamitin ang hardware decoding kapag magagamit" -ClearOnMediaEnd="Itago ang pinagmulan kung tapos ang playback" Advanced="I-Advanced" RestartWhenActivated="Simulan mulang ang playback kapag aktibo ang pinagmulan" CloseFileWhenInactive="Isarado ang file pag di aktibo" diff --git a/plugins/obs-ffmpeg/data/locale/fr-FR.ini b/plugins/obs-ffmpeg/data/locale/fr-FR.ini index 9b15413..4d9881f 100644 --- a/plugins/obs-ffmpeg/data/locale/fr-FR.ini +++ b/plugins/obs-ffmpeg/data/locale/fr-FR.ini @@ -2,22 +2,27 @@ FFmpegOutput="Sortie FFmpeg" FFmpegAAC="Encodeur AAC FFmpeg par défaut" FFmpegOpus="Encodeur FFmpeg Opus" Bitrate="Débit" -Preset="Préréglage" +MaxBitrate="Débit maximal" +Preset="Pré-réglage" RateControl="Contrôle du débit" KeyframeIntervalSec="Intervalle d'image-clé (en secondes, 0 = auto)" Lossless="Sans perte" -BFrames="B-frames" +BFrames="Nombre maximal de B-frames" NVENC.Use2Pass="Utiliser l'encodage double passe" -NVENC.Preset.default="Défaut" -NVENC.Preset.hq="Haute qualité" -NVENC.Preset.hp="Haute performance" -NVENC.Preset.bd="Blu-ray" -NVENC.Preset.ll="Faible latence" -NVENC.Preset.llhq="Faible latence haute qualité" -NVENC.Preset.llhp="Faible latence haute performance" -NVENC.Level="Niveau" +NVENC.Preset.default="Performances" +NVENC.Preset.hq="Qualité" +NVENC.Preset.hp="Performances maximales" +NVENC.Preset.mq="Qualité maximale" +NVENC.Preset.ll="Faible Latence" +NVENC.Preset.llhq="Qualité avec Faible Latence" +NVENC.Preset.llhp="Performance avec Faible Latence" +NVENC.LookAhead="Prédiction (Look-ahead)" +NVENC.LookAhead.ToolTip="Active les B-frames dynamiques.\n\nSi désactivé, l'encodeur utilisera toujours le nombre de B-frames dans le paramètre \"Nombre maximal de B-frames\".\n\nSi activé, il augmentera la qualité visuelle en n'utilisant que le nombre de B-frames nécessaire, inférieur au maximum,\nau prix d'une utilisation accrue de la carte graphique." +NVENC.PsychoVisualTuning="Améliorations psycho-visuelles" +NVENC.PsychoVisualTuning.ToolTip="Active des paramètres de l'encodeur qui optimisent l'utilisation du débit afin d'avoir une meilleure qualité visuelle perçue,\nen particulier lors de scènes comportant beaucoup de mouvement, au prix d'une utilisation accrue de la carte graphique." +NVENC.CQLevel="Niveau CQ" FFmpegSource="Source média" LocalFile="Fichier local" @@ -26,17 +31,17 @@ Input="Entrée" InputFormat="Format d'entrée" BufferingMB="Mémoire tampon réseau (Mo)" HardwareDecode="Utiliser le décodage matériel si possible" -ClearOnMediaEnd="Cacher la source lorsque la lecture est finie" +ClearOnMediaEnd="Ne rien afficher lorsque la lecture se termine" Advanced="Options avancées" 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." +CloseFileWhenInactive.ToolTip="Ferme le fichier lorsque la source ne s'affiche pas sur le stream 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" -SpeedPercentage="Vitesse (pourcent)" +RestartMedia="Reprendre depuis le début" +SpeedPercentage="Vitesse (pourcentage)" Seekable="Navigable" MediaFileFilter.AllMediaFiles="Tous les fichiers multimédias" @@ -47,6 +52,7 @@ MediaFileFilter.AllFiles="Tous les fichiers" ReplayBuffer="Tampon de relecture" ReplayBuffer.Save="Sauvegarder la relecture" -HelperProcessFailed="Impossible de démarrer le processus d’assistance pour l’enregistrement. Vérifiez que les fichiers de OBS ne sont pas bloqués/supprimés par n’importe quel antivirus/logiciel de sécurité." -UnableToWritePath="Impossible d’écrire dans %1. Assurez-vous que vous utilisez un chemin d’enregistrement dont votre compte d’utilisateur est autorisé à écrire et qu’il y a suffisamment d’espace disque." +HelperProcessFailed="Impossible de démarrer le processus compagnon pour l’enregistrement. Vérifiez que les fichiers d'OBS ne sont pas bloqués/supprimés par un quelconque antivirus/logiciel de sécurité." +UnableToWritePath="Impossible d’écrire dans %1. Assurez-vous que vous utilisez un chemin d’enregistrement où votre compte d’utilisateur est autorisé à écrire et qu’il y a suffisamment d’espace disque." +WarnWindowsDefender="Si la Protection contre les ransomware de Windows 10 est activée, cela peut causer cette erreur. Essayez de désactiver le Dispositif d'accès contrôlé aux dossiers dans \"Sécurité Windows\" / \"Protection contre les virus et menaces\"." diff --git a/plugins/obs-ffmpeg/data/locale/gd-GB.ini b/plugins/obs-ffmpeg/data/locale/gd-GB.ini index 8c48562..3d519e0 100644 --- a/plugins/obs-ffmpeg/data/locale/gd-GB.ini +++ b/plugins/obs-ffmpeg/data/locale/gd-GB.ini @@ -2,22 +2,23 @@ FFmpegOutput="Às-chur FFmpeg" FFmpegAAC="Inneal-còdachaidh AAC tùsail airson FFmpeg" FFmpegOpus="Inneal-còdachaidh Opus airson FFmpeg" Bitrate="Reat bhiotaichean" +MaxBitrate="Reat bhiotaichean as motha" Preset="Ro-sheata" RateControl="Smachd air an reat" KeyframeIntervalSec="Eadaramh nam frèamaichean-iuchrach (diog, fèin-obrachail)" Lossless="Gun chall càileachd" -BFrames="Frèamaichean-B" +BFrames="Frèamaichean-B as motha" NVENC.Use2Pass="Cleachd còdachadh dà phas" -NVENC.Preset.default="Bun-roghainn" -NVENC.Preset.hq="Càileachd àrd" -NVENC.Preset.hp="Dèanadas àrd" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Dèanadas" +NVENC.Preset.hq="Càileachd" +NVENC.Preset.hp="Dèanadas as àirde" +NVENC.Preset.mq="Càileachd as àirde" NVENC.Preset.ll="Foillidheachd ìosal" -NVENC.Preset.llhq="Foillidheachd ìosal ⁊ càileachd àrd" -NVENC.Preset.llhp="Foillidheachd ìosal ⁊ dèanadas àrd" -NVENC.Level="Leibheil" +NVENC.Preset.llhq="Càileachd foillidheachd ìosail" +NVENC.Preset.llhp="Dèanadas foillidheachd ìosail" +NVENC.LookAhead="Coimhead roimhe" FFmpegSource="Tùs a’ mheadhain" LocalFile="Faidhle ionadail" @@ -26,7 +27,6 @@ Input="Ion-chur" InputFormat="Fòrmat an ion-chuir" BufferingMB="Bufair an lìonraidh (MB)" HardwareDecode="Cleachd dì-chòdachadh bathair-chruaidh ma bhios e ri fhaighinn" -ClearOnMediaEnd="Falaich an tùs nuair a bhios a’ chluiche deiseil" Advanced="Adhartach" RestartWhenActivated="Ath-thòisich a’ chluiche nuair a thig gnìomh on tùs" CloseFileWhenInactive="Dùin am faidhle mur eil gnìomh ann" diff --git a/plugins/obs-ffmpeg/data/locale/gl-ES.ini b/plugins/obs-ffmpeg/data/locale/gl-ES.ini index 1cde94a..196a04f 100644 --- a/plugins/obs-ffmpeg/data/locale/gl-ES.ini +++ b/plugins/obs-ffmpeg/data/locale/gl-ES.ini @@ -3,7 +3,6 @@ FFmpegAAC="Codificador AAC FFmpeg predefinido" Bitrate="Velocidade de bits" -NVENC.Level="Nivel" FFmpegSource="Fonte multimedia" LocalFile="Ficheiro local" @@ -11,7 +10,6 @@ Looping="Bucle" Input="Entrada" InputFormat="Formato de entrada" HardwareDecode="Utilizar a descodificación por hárdware cando estiver dispoñible" -ClearOnMediaEnd="Agochar a fonte cando a reprodución remata" Advanced="Avanzado" diff --git a/plugins/obs-ffmpeg/data/locale/he-IL.ini b/plugins/obs-ffmpeg/data/locale/he-IL.ini index 3d1c0db..dd7cfa2 100644 --- a/plugins/obs-ffmpeg/data/locale/he-IL.ini +++ b/plugins/obs-ffmpeg/data/locale/he-IL.ini @@ -8,14 +8,7 @@ Lossless="ללא אובדן נתונים" NVENC.Use2Pass="השתמש בקידוד שני מעברים" -NVENC.Preset.default="ברירת מחדל" -NVENC.Preset.hq="איכות גבוהה" -NVENC.Preset.hp="ביצועים גבוהים" -NVENC.Preset.bd="בלוריי" NVENC.Preset.ll="השהיה נמוכה" -NVENC.Preset.llhq="השהיה נמוכה איכות גבוהה" -NVENC.Preset.llhp="השהיה נמוכה ביצועים גבוהים" -NVENC.Level="רמה" FFmpegSource="מקור מדיה" LocalFile="קובץ מקומי" @@ -23,7 +16,6 @@ Looping="לולאה" Input="קלט" InputFormat="תבנית קלט" HardwareDecode="השתמש בפענוח חומרה כאשר היא זמין" -ClearOnMediaEnd="הסתר את מקור כאשר ההשמעה מסתיימת" Advanced="מתקדם" RestartWhenActivated="הפעל מחדש השמעה כאשר מקור הופך לפעיל" ColorRange="טווח צבעים YUV" diff --git a/plugins/obs-ffmpeg/data/locale/hr-HR.ini b/plugins/obs-ffmpeg/data/locale/hr-HR.ini index 839bc97..ee3d6fb 100644 --- a/plugins/obs-ffmpeg/data/locale/hr-HR.ini +++ b/plugins/obs-ffmpeg/data/locale/hr-HR.ini @@ -8,14 +8,7 @@ Lossless="Bez gubitka" NVENC.Use2Pass="Koristi enkoding duplog prolaza" -NVENC.Preset.default="Podrazumevani" -NVENC.Preset.hq="Visoki kvalitet" -NVENC.Preset.hp="Visoke performanse" -NVENC.Preset.bd="Bluray" NVENC.Preset.ll="Nisko kašnjenje" -NVENC.Preset.llhq="Visoki kvalitet niskog kašnjenja" -NVENC.Preset.llhp="Visoke performanse niskog kašnjenja" -NVENC.Level="Nivo" FFmpegSource="Medija izvor" LocalFile="Lokalna datoteka" @@ -23,7 +16,6 @@ Looping="Ponavljanje" Input="Ulaz" InputFormat="Format ulaza" HardwareDecode="Koristi hardversko enkodiranje kada je dostupno" -ClearOnMediaEnd="Sakrij izvor kada se reprodukcija završi" Advanced="Napredno" RestartWhenActivated="Ponovi reprodukciju kada izvor postane aktivan" ColorRange="YUV opseg boja" diff --git a/plugins/obs-ffmpeg/data/locale/hu-HU.ini b/plugins/obs-ffmpeg/data/locale/hu-HU.ini index d1339a7..9f3c3b2 100644 --- a/plugins/obs-ffmpeg/data/locale/hu-HU.ini +++ b/plugins/obs-ffmpeg/data/locale/hu-HU.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg kimenet" FFmpegAAC="FFmpeg alapértelmezett AAC kódoló" FFmpegOpus="FFmpeg Opus kódoló" Bitrate="Bitsebesség" +MaxBitrate="Max bitsebesség" Preset="Készlet" RateControl="Sebesség Vezérlés" KeyframeIntervalSec="Kulcsképkocka időköze (másodperc, 0=auto)" Lossless="Veszteségmentes" -BFrames="B képkocka" +BFrames="Max B-kockák" NVENC.Use2Pass="Kétmenetes kódolás" -NVENC.Preset.default="Alapértelmezett" -NVENC.Preset.hq="Kiváló minőség" -NVENC.Preset.hp="Nagy teljesítmény" -NVENC.Preset.bd="BluRay" +NVENC.Preset.default="Teljesítménycentrikus" +NVENC.Preset.hq="Minőségcentrikus" +NVENC.Preset.hp="Maximális teljesítmény" +NVENC.Preset.mq="Maximális minőség" NVENC.Preset.ll="Alacsony-késleltetés" -NVENC.Preset.llhq="Alacsony-késleltetés kiváló minőséggel" -NVENC.Preset.llhp="Alacsony-késleltetés nagy teljesítménnyel" -NVENC.Level="Szint" +NVENC.Preset.llhq="Alacsony késleltetés minőséggel" +NVENC.Preset.llhp="Alacsony késleltetés nagyobb teljesítménnyel" +NVENC.LookAhead="Előretekintés" +NVENC.LookAhead.ToolTip="Lehetővé teszi a dinamikus B-kockákat.\n\nHa le van tiltva, a kódoló mindig a \"Max B-kockák\" beállításnál megadott értéket használja.\n\nHa engedélyezve van, az növeli vizuális minőséget és annyi B-kockát használ a kódoló, amennyire szükség van, akár a maximumot is,\nmegnövekedett GPU kihasználtság árán." +NVENC.PsychoVisualTuning="Psichovizuális Tuning" +NVENC.PsychoVisualTuning.ToolTip="Lehetővé teszi a kódoló beállításait bitráta optimalizáláshoz az érzékelhető vizuális minőség megnöveléséhez,\nkülönösen olyan helyzetekben ahol sok a mozgás, megnövekedett GPU kihasználtság árán." +NVENC.CQLevel="CQ szint" FFmpegSource="Médiaforrás" LocalFile="Helyi fájl" @@ -26,11 +31,11 @@ Input="Bemenet" InputFormat="Bemeneti formátum" BufferingMB="Hálózati pufferelés (MB)" HardwareDecode="Hardveres dekódolás használata, ha rendelkezésre áll" -ClearOnMediaEnd="Forrás elrejtése a lejátszás végeztével" +ClearOnMediaEnd="Semmit se mutasson, a lejátszás végeztével" Advanced="Haladó" 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." +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" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Visszajátszás mentése" HelperProcessFailed="Nem lehet indítani a felvétel segítő folyamatot. Ellenőrizze, hogy az OBS fájlok nincsenek e blokkolva vagy eltávolítva bármilyen harmadik féltől származó antivírus / biztonsági szoftver által." UnableToWritePath="Nem lehet megírni a %1. Győződjön meg róla, hogy a felhasználói fiókjának megvannak a szükséges engedélyei az íráshoz vagy rendelkezésre áll az elegendő lemezterület." +WarnWindowsDefender="Amennyiben a Windows 10 Ransomware Védelem aktiválva van, úgy az is képes ezt a hibát generálni. Adja hozzá a kontrollált könyvtárlistához a Windows Biztonság / Vírus és fenyegetésvédelem beállítások között." diff --git a/plugins/obs-ffmpeg/data/locale/it-IT.ini b/plugins/obs-ffmpeg/data/locale/it-IT.ini index 2d69185..f0a14fd 100644 --- a/plugins/obs-ffmpeg/data/locale/it-IT.ini +++ b/plugins/obs-ffmpeg/data/locale/it-IT.ini @@ -1,52 +1,58 @@ FFmpegOutput="Uscita FFmpeg" -FFmpegAAC="Codificatore FFmpeg predefinito AAC" -FFmpegOpus="FFmpeg Opus Encoder" -Bitrate="Bitrate" +FFmpegAAC="Codifica FFmpeg predefinita per AAC" +FFmpegOpus="Codifica FFmpeg Opus" +Bitrate="Velocità in bit" +MaxBitrate="Velocità in bit massima" Preset="Preset" -RateControl="Controllo frequenza" -KeyframeIntervalSec="Intervallo Keyframe (secondi, 0=automatico)" +RateControl="Tipo di controllo della frequenza" +KeyframeIntervalSec="Intervallo dei fotogrammi chiave (in secondi, 0=automatico)" Lossless="Lossless" -BFrames="B-frames" +BFrames="B-frame massimi" -NVENC.Use2Pass="Usa codifica in due passaggi" -NVENC.Preset.default="Predefinito" -NVENC.Preset.hq="Alta Qualità" -NVENC.Preset.hp="Alte Prestazioni" -NVENC.Preset.bd="Bluray" +NVENC.Use2Pass="Utilizza la codifica in due passaggi" +NVENC.Preset.default="Prestazioni" +NVENC.Preset.hq="Qualità" +NVENC.Preset.hp="Massime prestazioni" +NVENC.Preset.mq="Massima qualità" NVENC.Preset.ll="Bassa latenza" -NVENC.Preset.llhq="Bassa latenza Alta Qualità" -NVENC.Preset.llhp="Bassa latenza ad Alte Prestazioni" -NVENC.Level="Livello" +NVENC.Preset.llhq="Bassa latenza e qualità" +NVENC.Preset.llhp="Bassa latenza e prestazioni" +NVENC.LookAhead="Previsione (look-ahead)" +NVENC.LookAhead.ToolTip="Attiva i B-frame dinamici.\n\nSe disattivato, la codifica utilizzerà sempre il numero di B-frame specificato nell'impostazione «B-frame massimi».\n\nSe attivato, aumenterà la qualità visiva utilizzando comunque solo i B-frame necessari,\nfino al massimo, al costo di un maggiore utilizzo della GPU." +NVENC.PsychoVisualTuning="Ottimizzazione visiva di Psycho" +NVENC.PsychoVisualTuning.ToolTip="Attiva le impostazioni di codifica che ottimizzano l'utilizzo della velocità in bit per ottenere una maggiore qualità visiva,\nsoprattutto in situazioni con molti movimenti, al costo di un maggiore utilizzo della GPU." +NVENC.CQLevel="Livello di quantificazione costante" -FFmpegSource="Origine multimediale" +FFmpegSource="Fonte multimediale" LocalFile="File locale" Looping="Ripeti" Input="Input" -InputFormat="Formato di input" -BufferingMB="Rete Buffering (MB)" +InputFormat="Formato dell'input" +BufferingMB="Buffering della rete (in MB)" HardwareDecode="Utilizza la decodifica hardware quando disponibile" -ClearOnMediaEnd="Nascondi la fonte quando termina la riproduzione" +ClearOnMediaEnd="Non mostrare nulla quando la riproduzione finisce" Advanced="Avanzate" -RestartWhenActivated="Riattiva playback quando la fonte torna attiva" -CloseFileWhenInactive="Chiudi file quando inattivo" -CloseFileWhenInactive.ToolTip="Chiude il file quando l'origine non viene visualizzato sullo stream o\nrecording il flusso. In questo modo permette di modificare il file quando la fonte non è attiva, \nma ci può essere qualche ritardo di avvio quando si riattiva l'origine." +RestartWhenActivated="Riavvia la riproduzione quando la fonte torna attiva" +CloseFileWhenInactive="Chiudi il file quando la fonte diventa inattiva" +CloseFileWhenInactive.ToolTip="Chiude il file quando la fonte non viene più visualizzata nell'inquadratura.\nQuesto permette di modificare il file quando la fonte non è attiva,\nma potrebbe esserci un po' di ritardo all'avvio quando si riattiva la fonte." ColorRange="Gamma di colore YUV" -ColorRange.Auto="Autom." +ColorRange.Auto="Automatico" ColorRange.Partial="Parziale" ColorRange.Full="Intero" -RestartMedia="Riavvia Media" -SpeedPercentage="Velocità (percentuale)" +RestartMedia="Riavvia media dall'inizio" +SpeedPercentage="Velocità (in percentuale)" Seekable="Ricercabile" -MediaFileFilter.AllMediaFiles="Tutti i file media" +MediaFileFilter.AllMediaFiles="Tutti i file multimediali" MediaFileFilter.VideoFiles="File video" MediaFileFilter.AudioFiles="File audio" MediaFileFilter.AllFiles="Tutti i file" -ReplayBuffer="Buffer di Replay" -ReplayBuffer.Save="Salva Replay" +ReplayBuffer="Buffer di replay" +ReplayBuffer.Save="Salva il replay" -HelperProcessFailed="Impossibile avviare il processo di supporto di registrazione. Verifica che i file di OBS non siano stati bloccati o rimossi da software di sicurezza od antivirus di terze parti." -UnableToWritePath="Impossibile scrivere su %1. Assicurati che il tuo account possa accedere al percorso di registrazione, e che ci sia spazio sufficiente su disco." +HelperProcessFailed="Impossibile avviare il processo di assistenza alla registrazione. Controlla che i file di OBS non siano stati bloccati o rimossi da software di sicurezza di terze parti o antivirus." +UnableToWritePath="Impossibile scrivere su %1. Assicurati che il tuo account utente possa accedere al percorso di registrazione, e che ci sia spazio sufficiente sul disco." +WarnWindowsDefender="Se la protezione ransomware di Windows 10 è attivata, può causare questo errore. Prova a disattivare l'accesso alle cartelle controllato, lo puoi trovare in Sicurezza di Windows → Protezione da virus e minacce → Gestisci protezione ransomware." diff --git a/plugins/obs-ffmpeg/data/locale/ja-JP.ini b/plugins/obs-ffmpeg/data/locale/ja-JP.ini index c291993..e790cb3 100644 --- a/plugins/obs-ffmpeg/data/locale/ja-JP.ini +++ b/plugins/obs-ffmpeg/data/locale/ja-JP.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg の出力" FFmpegAAC="FFmpeg 既定のAAC エンコーダ" FFmpegOpus="FFmpeg Opus エンコーダ" Bitrate="ビットレート" +MaxBitrate="最大ビットレート" Preset="プリセット" RateControl="レート制御" KeyframeIntervalSec="キーフレーム間隔 (秒, 0=自動)" Lossless="無損失" -BFrames="B-フレーム" +BFrames="最大 B フレーム" NVENC.Use2Pass="2パスエンコードを使用" -NVENC.Preset.default="既定" -NVENC.Preset.hq="高品質" -NVENC.Preset.hp="高性能" -NVENC.Preset.bd="ブルーレイ" -NVENC.Preset.ll="低遅延" -NVENC.Preset.llhq="低遅延高品質" -NVENC.Preset.llhp="低遅延高性能" -NVENC.Level="レベル" +NVENC.Preset.default="Performance" +NVENC.Preset.hq="Quality" +NVENC.Preset.hp="Max Performance" +NVENC.Preset.mq="Max Quality" +NVENC.Preset.ll="Low-Latency" +NVENC.Preset.llhq="Low-Latency Quality" +NVENC.Preset.llhp="Low-Latency Performance" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="動的Bフレームを有効にします。 \n\n無効にすると、エンコーダは常に’最大Bフレーム数’設定で指定されたBフレーム数を使用します。 \n\n有効にした場合、GPU使用率の増加を犠牲にして、\n\n最大数まで、必要な分だけ多くのBフレームを使用することで視覚的品質を向上させます。" +NVENC.PsychoVisualTuning="心理視覚チューニング" +NVENC.PsychoVisualTuning.ToolTip="GPU使用率の増加を犠牲にして、特に動きの激しい状況で、\n視覚品質の向上のためにビットレートの使用を最適化するエンコーダ設定を有効にします。" +NVENC.CQLevel="CQ レベル" FFmpegSource="メディアソース" LocalFile="ローカルファイル" @@ -26,7 +31,7 @@ Input="入力" InputFormat="入力フォーマット" BufferingMB="ネットワークバッファリング (MB)" HardwareDecode="可能な場合ハードウェアデコードを使用" -ClearOnMediaEnd="再生終了時にソースを非表示にする" +ClearOnMediaEnd="再生終了時に何も表示しない" Advanced="高度な設定" RestartWhenActivated="ソースがアクティブになったときに再生を再開する" CloseFileWhenInactive="アクティブでないときにファイルを閉じる" @@ -44,9 +49,10 @@ MediaFileFilter.VideoFiles="ビデオファイル" MediaFileFilter.AudioFiles="オーディオファイル" MediaFileFilter.AllFiles="すべてのファイル" -ReplayBuffer="リプレイバッファー" +ReplayBuffer="リプレイバッファ" ReplayBuffer.Save="リプレイ保存" HelperProcessFailed="録画の補助プロセスを開始できません。 OBSのファイルがサードパーティ製のアンチウイルス/セキュリティソフトウェアによってブロックまたは削除されていないことを確認してください。" UnableToWritePath="%1 に書き込めません。 あなたのユーザーアカウントが書き込みを許可されている録画のパスを使用していることと十分なディスク容量があることを確認してください。" +WarnWindowsDefender="Windows 10 ランサムウェア保護が有効になっていると、このエラーが発生する可能性があります。Windowsのセキュリティ/ウイルス対策と脅威対策の設定で、フォルダへのアクセス制御を無効にしてみてください。" diff --git a/plugins/obs-ffmpeg/data/locale/ka-GE.ini b/plugins/obs-ffmpeg/data/locale/ka-GE.ini index f1d9220..cd244e0 100644 --- a/plugins/obs-ffmpeg/data/locale/ka-GE.ini +++ b/plugins/obs-ffmpeg/data/locale/ka-GE.ini @@ -1,23 +1,28 @@ -FFmpegOutput="FFmpeg გამომავალი სიგნალი" +FFmpegOutput="FFmpeg-გამოტანა" FFmpegAAC="FFmpeg ნაგულისხმევი AAC დამშიფრავი" FFmpegOpus="FFmpeg Opus დამშიფრავი" Bitrate="ბიტური სიხშირე" +MaxBitrate="უმაღლესი დასაშვები ბიტური სიხშირე" Preset="მზა პარამეტრები" RateControl="სიხშირის მართვა" KeyframeIntervalSec="საკვანძო კადრებს შორის შუალედი (წამი, 0=თვითშერჩევა)" Lossless="უდანაკარგო" -BFrames="B-კადრები" +BFrames="B-კადრების დასაშვები რაოდენობა" NVENC.Use2Pass="ორმაგი დაშიფვრის გამოყენება" -NVENC.Preset.default="ნაგულისხმევი" -NVENC.Preset.hq="მაღალი ხარისხი" -NVENC.Preset.hp="მაღალი წარმადობა" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="წარმადობა" +NVENC.Preset.hq="ხარისხი" +NVENC.Preset.hp="უმაღლესი წარმადობა" +NVENC.Preset.mq="უმაღლესი ხარისხი" NVENC.Preset.ll="მცირე დაყოვნება" -NVENC.Preset.llhq="მცირე დაყოვნება, მაღალი ხარისხი" -NVENC.Preset.llhp="მცირე დაყოვნება, მაღალი წარმადობა" -NVENC.Level="საფეხური" +NVENC.Preset.llhq="მცირე დაყოვნება მაღალი ხარისხით" +NVENC.Preset.llhp="მცირე დაყოვნება მაღალი წარმადობით" +NVENC.LookAhead="თვითგანსაზღვრა" +NVENC.LookAhead.ToolTip="ჩაირთვება ცვალებადი რაოდენობის B-კადრები.\n\nგამორთვის შემთხვევაში, დამშიფრავი ყოველთვის „B-კადრების დასაშვები რაოდენობის“ ველში მითითებულს გამოიყენებს.\n\nჩართვის შედეგად, გაიზრდება გამოსახულების ხარისხი საჭირო რაოდენობის B-კადრების დამატებით, უმაღლეს დასაშვებ რაოდენობამდე,\nგრაფიკული პროცესორის გაზრდილი დატვირთვის ხარჯზე." +NVENC.PsychoVisualTuning="ფსიქოვიზუალური გამართვა" +NVENC.PsychoVisualTuning.ToolTip="საშუალებას აძლევს დამშიფრავს, საუკეთესოდ შეარჩიოს ბიტური სიხშირე გამოსახულების ხარისხის უკეთ აღქმისთვის,\nგანსაკუთრებით მოძრავ სცენებში, გრაფიკული პროცესორის გაზრდილი დატვირთვის ხარჯზე." +NVENC.CQLevel="CQ-დონე (მუდმივ ნაწილაკებად დაყოფა)" FFmpegSource="მასალის წყარო" LocalFile="ადგილობრივი ფაილი" @@ -26,11 +31,11 @@ Input="შეტანა" InputFormat="შეტანის ფორმატი" BufferingMB="ქსელის ბუფერიზაცია (მბაიტი)" HardwareDecode="აპარატურული დაშიფვრის გამოყენება, ხელმისაწვდომობის შემთხვევაში" -ClearOnMediaEnd="წყაროს დამალვა, გაშვების დამთავრებისას" +ClearOnMediaEnd="აღარაფერი გამოჩნდეს, ჩვენების დასრულების შემდგომ" Advanced="გაფართოებული" RestartWhenActivated="ხელახლა გაშვება წყაროს ამოქმედებისას" CloseFileWhenInactive="ფაილის დახურვა უმოქმედობისას" -CloseFileWhenInactive.ToolTip="ფაილი დაიხურება, თუ წყარო არ იქნება ეთერში ან\nჩაწერაზე გაშვებული. ეს საშუალებას იძლევა შეიცვალოს ფაილი, როცა წყარო არაა მოქმედი,\nთუმცა ხელახლა ამოქმედებისას, შესაძლოა გარკვეული დროით დაყოვნებას ჰქონდეს ადგილი." +CloseFileWhenInactive.ToolTip="ფაილი დაიხურება, თუ წყარო არ იქნება ეთერში ან\nჩაწერაზე გაშვებული. ეს საშუალებას იძლევა შეიცვალოს ფაილი, როცა წყარო არაა მოქმედი,\nთუმცა ხელახლა ამოქმედებისას, შესაძლოა გარკვეული დროით დაყოვნებას ჰქონდეს ადგილი." ColorRange="YUV ფერთა გამა" ColorRange.Auto="ავტომატური" ColorRange.Partial="ნაწილობრივი" @@ -49,4 +54,5 @@ ReplayBuffer.Save="გადახვევის შენახვა" HelperProcessFailed="ჩაწერის პროცესის გაშვება ვერ ხერხდება. გადაამოწმეთ, ხომ არ არის OBS ფაილები შეზღუდული ან წაშლილი ანტივირუსის / უსაფრთხოების სხვა პროგრამების მიერ." UnableToWritePath="%1-ში ჩაწერა ვერ ხერხდება. დარწმუნდით, რომ ჩაწერისთვის ისეთი მისამართი გაქვთ მითითებული, სადაც ჩაწერის ნებართვაც გააჩნია თქვენს ანგარიშს და ამასთან, არის საკმარისი ადგილი დისკზე." +WarnWindowsDefender="თუ ჩართულია Windows 10-ის დაცვა ფაილების მიმტაცებელი პროგრამებისგან (Ransomware), ამ შეცდომას ეგეც შეიძლება იწვევდეს. სცადეთ საქაღალდეებთან წვდომის შეზღუდვის მოხსნა, Windows-ის უსაფრთხოების ან ანტივირუსის პარამეტრებიდან." diff --git a/plugins/obs-ffmpeg/data/locale/ko-KR.ini b/plugins/obs-ffmpeg/data/locale/ko-KR.ini index 535fdb8..292f8b3 100644 --- a/plugins/obs-ffmpeg/data/locale/ko-KR.ini +++ b/plugins/obs-ffmpeg/data/locale/ko-KR.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg 출력" FFmpegAAC="FFmpeg 기본 AAC 인코더" FFmpegOpus="FFmpeg Opus 인코더" Bitrate="비트레이트" +MaxBitrate="최대 비트레이트" Preset="사전 설정" RateControl="데이터율 제어" KeyframeIntervalSec="키프레임 간격 (초 단위, 0=자동)" Lossless="무손실" -BFrames="B-화면" +BFrames="최대 B-프레임" NVENC.Use2Pass="2 패스 인코딩 사용" -NVENC.Preset.default="기본" -NVENC.Preset.hq="우수한 품질" -NVENC.Preset.hp="우수한 성능" -NVENC.Preset.bd="블루레이" +NVENC.Preset.default="성능 우선" +NVENC.Preset.hq="품질 우선" +NVENC.Preset.hp="최대 성능" +NVENC.Preset.mq="최대 품질" NVENC.Preset.ll="낮은 지연 시간" -NVENC.Preset.llhq="낮은 지연 시간 우수한 품질" -NVENC.Preset.llhp="낮은 지연 시간 우수한 성능" -NVENC.Level="수준" +NVENC.Preset.llhq="낮은 지연 시간 및 품질" +NVENC.Preset.llhp="낮은 지연 시간 및 성능" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="동적 B-프레임을 활성화합니다.\n\n이 설정을 사용하지 않으면 인코더는 항상 지정한 수 만큼만 B-프레임을 사용합니다.\n\n이 설정을 켜면 B-프레임을 품질을 올리는데 필요한 만큼 사용하지만,\nGPU사용률이 증가합니다." +NVENC.PsychoVisualTuning="Psycho Visual Tuning" +NVENC.PsychoVisualTuning.ToolTip="비트레이트 사용을 최적화하여 영상 품질을 높일 수 있습니다.\n특히 영상 내 빠른 동작을 처리할 때 도움이 됩니다. 다만 GPU 사용률이 증가합니다." +NVENC.CQLevel="CQ 수준" FFmpegSource="미디어 소스" LocalFile="로컬 파일" @@ -26,7 +31,7 @@ Input="입력" InputFormat="입력 형식" BufferingMB="네트워크 버퍼링 (MB)" HardwareDecode="가능한 경우 하드웨어 디코딩 사용" -ClearOnMediaEnd="재생이 끝나면 소스를 숨기기" +ClearOnMediaEnd="재생이 끝나면 아무 것도 표시하지 않기" Advanced="고급" RestartWhenActivated="소스가 활성화될 때 재생을 다시 시작" CloseFileWhenInactive="비활성화 상태일 때 파일 닫기" @@ -49,4 +54,5 @@ ReplayBuffer.Save="리플레이 저장" HelperProcessFailed="녹화 도우미를 시작할 수 없습니다. 백신이나 보안 소프트웨어가 OBS 파일을 차단 혹은 제거하지 않았는지 확인하십시오." UnableToWritePath="%1에 기록할 수 없습니다. 사용자 계정이 녹화 계정에 접근할 수 있는지 혹은 저장 공간이 충분한지 확인하십시오." +WarnWindowsDefender="윈도우 10에서 제공하는 랜섬웨어 보호 기능이 켜져 있으면 이 오류가 일어날 수 있습니다. 윈도우 설정에서 '보호된 폴더' 기능을 검색하여 꺼주세요." diff --git a/plugins/obs-ffmpeg/data/locale/nb-NO.ini b/plugins/obs-ffmpeg/data/locale/nb-NO.ini index 7439c6e..30849de 100644 --- a/plugins/obs-ffmpeg/data/locale/nb-NO.ini +++ b/plugins/obs-ffmpeg/data/locale/nb-NO.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg utdata" FFmpegAAC="Standard FFmpeg AAC-koder" FFmpegOpus="FFmpeg Opus enkoder" Bitrate="Bitrate" +MaxBitrate="Maks bitrate" Preset="Forhåndsinnstilling" RateControl="Hastighetskontroll" KeyframeIntervalSec="Nøkkelbildeintervall (sekunder, 0 = automatisk)" Lossless="Tapsfri" -BFrames="B-bilder" +BFrames="Maksimalt antall B-frames" NVENC.Use2Pass="Bruk tostegskoding" -NVENC.Preset.default="Standard" -NVENC.Preset.hq="Høy kvalitet" -NVENC.Preset.hp="Høy ytelse" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Ytelse" +NVENC.Preset.hq="Kvalitet" +NVENC.Preset.hp="Best ytelse" +NVENC.Preset.mq="Best kvalitet" NVENC.Preset.ll="Lav latens" -NVENC.Preset.llhq="Lav latens, høy kvalitet" -NVENC.Preset.llhp="Lav latens, høy ytelse" -NVENC.Level="Nivå" +NVENC.Preset.llhq="Lav latens og kvalitet" +NVENC.Preset.llhp="Lav latens og ytelse" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Aktiverer dynamiske B-frames. \n\nHvis deaktivert, vil koderen alltid bruke nummeret spesifisert i \"Maks B-frames\" instillingen. \n\nHvis aktivert, vil bildekvaliteten forbedres ved å bare bruke antallet nødvendige B-frames, opp til maksimum antallet,\npå bekostning av økt GPU-forbruk." +NVENC.PsychoVisualTuning="Psykovisuell justering" +NVENC.PsychoVisualTuning.ToolTip="Aktiverer kodingsinstillinger som optimerer bruken av bitrate for forbedret oppfattet bildekvalitet,\nspesielt i situasjonen med mye bevegelse, på bekostning av økt GPU-forbruk." +NVENC.CQLevel="QC-nivå" FFmpegSource="Mediekilde" LocalFile="Lokal fil" @@ -26,7 +31,7 @@ Input="Inngang" InputFormat="Inngangsformat" BufferingMB="Nettverksbuffer (Mb)" HardwareDecode="Bruk maskinvaredekoding når tilgjengelig" -ClearOnMediaEnd="Skjul kilde når avspilling ender" +ClearOnMediaEnd="Vis ingenting når avspillingen slutter" Advanced="Avansert" RestartWhenActivated="Start avspilling omigjen når kilde blir aktiv" CloseFileWhenInactive="Lukk fil når inaktiv" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Lagre replay" HelperProcessFailed="Kan ikke starte prosess for opptakshjelper. Sjekk at OBS ikke har noen blokkerte filer, eller at tredjeparts antivirus/sikkerhetsprogramvare har slettet noen filer." UnableToWritePath="Kan ikke skrive til %1. Kontroller at du bruker en filbane som kontoen din har rettigheter til å skrive på, og at du har nok diskplass tilgjengelig." +WarnWindowsDefender="Hvis Windows 10 Ransomvare-beskyttelse er aktivert, kan dette medføre denne feilen. Forsøk å deaktivere kontrollert mappetilgang i Windows-instillingen Sikkerhet > Virus- og Trusselbeskyttelse." diff --git a/plugins/obs-ffmpeg/data/locale/nl-NL.ini b/plugins/obs-ffmpeg/data/locale/nl-NL.ini index 62c9163..7f996d7 100644 --- a/plugins/obs-ffmpeg/data/locale/nl-NL.ini +++ b/plugins/obs-ffmpeg/data/locale/nl-NL.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg-uitvoer" FFmpegAAC="FFmpeg Standaard AAC Encoder" FFmpegOpus="FFmpeg Opus Encoder" Bitrate="Bitrate" +MaxBitrate="Maximale bitrate" Preset="Preset" RateControl="Rate Control" KeyframeIntervalSec="Tijd tussen keyframes (seconden, 0=auto)" Lossless="Lossless" -BFrames="B-frames" +BFrames="Max B-frames" NVENC.Use2Pass="Gebruik two-pass encoding" -NVENC.Preset.default="Standaard" -NVENC.Preset.hq="Hoge kwaliteit" -NVENC.Preset.hp="Hoge prestaties" -NVENC.Preset.bd="Blu-ray" +NVENC.Preset.default="Prestaties" +NVENC.Preset.hq="Kwaliteit" +NVENC.Preset.hp="Hoogste Prestaties" +NVENC.Preset.mq="Hoogste Kwaliteit" NVENC.Preset.ll="Lage Latency" -NVENC.Preset.llhq="Lage latency, hoge kwaliteit" -NVENC.Preset.llhp="Lage latency, hoge prestaties" -NVENC.Level="Niveau" +NVENC.Preset.llhq="Lage Latency Kwaliteit" +NVENC.Preset.llhp="Lage Latency Prestaties" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Maakt dynamische B-frames mogelijk.\n\nIndien uitgeschakeld, zal de encoder altijd het aantal B-frames gebruiken dat is opgegeven in de instelling 'Max B-frames'.\n\nIndien ingeschakeld, zal het de visuele kwaliteit verhogen door alleen het aantal B-frames te gebruiken dat nodig is, tot het maximum,\nten koste van een verhoogd GPU-gebruik." +NVENC.PsychoVisualTuning="Psycho Visuele Stemming" +NVENC.PsychoVisualTuning.ToolTip="Maakt encoderinstellingen mogelijk die het gebruik van de bitsnelheid optimaliseren voor een hogere waargenomen visuele kwaliteit,\nvooral in situaties met veel beweging, ten koste van een hoger GPU-gebruik." +NVENC.CQLevel="CQ effen" FFmpegSource="Mediabron" LocalFile="Lokaal bestand" @@ -26,7 +31,7 @@ Input="Invoer" InputFormat="Invoerformaat" BufferingMB="Netwerk Buffering (MB)" HardwareDecode="Gebruik hardware-decoding wanneer mogelijk" -ClearOnMediaEnd="Verberg de bron na het afspelen" +ClearOnMediaEnd="Toon niets wanneer het afspelen eindigt" Advanced="Geavanceerd" RestartWhenActivated="Opnieuw starten met afspelen zodra de bron actief wordt" CloseFileWhenInactive="Sluit bestand wanneer niet actief" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Replay Opslaan" HelperProcessFailed="Kan het opnamehulp-proces niet starten. Controleer of er geen OBS bestanden geblokkeerd of verwijderd zijn door antivirus of beveiligingssoftware." UnableToWritePath="Kan niet naar %1 schrijven. Controller of je een opnamepad gebruikt waar je gebruikersaccount naartoe kan schrijven, en dat er voldoende schijfruimte beschikbaar is." +WarnWindowsDefender="Als Windows 10 Ransomware Protection is ingeschakeld, kan dit ook deze fout veroorzaken. Probeer gecontroleerde toegang tot mappen uit te schakelen in de instellingen van Windows Security / Virus & Threat Protection." diff --git a/plugins/obs-ffmpeg/data/locale/pl-PL.ini b/plugins/obs-ffmpeg/data/locale/pl-PL.ini index 09f1f5d..2be79c9 100644 --- a/plugins/obs-ffmpeg/data/locale/pl-PL.ini +++ b/plugins/obs-ffmpeg/data/locale/pl-PL.ini @@ -1,23 +1,28 @@ FFmpegOutput="Wyjście FFmpeg" FFmpegAAC="Domyślny enkoder AAC w FFmpeg" FFmpegOpus="Enkoder FFmpeg Opus" -Bitrate="Przepływność bitowa" +Bitrate="Przepływność (bitrate)" +MaxBitrate="Maksymalna przepływność (bitrate)" Preset="Profil ustawień" RateControl="Typ przepływności" KeyframeIntervalSec="Odstęp między klatkami kluczowymi (sekundy, 0=automatyczny)" Lossless="Bezstratny" -BFrames="B-ramki" +BFrames="Maksymalna liczba klatek B-frame" NVENC.Use2Pass="Użyj enkodowania dwuprzebiegowego" -NVENC.Preset.default="Domyślny" -NVENC.Preset.hq="Wysoka jakość" -NVENC.Preset.hp="Wysoka wydajność" -NVENC.Preset.bd="Blu-ray" +NVENC.Preset.default="Wydajność" +NVENC.Preset.hq="Jakość" +NVENC.Preset.hp="Najlepsza wydajność" +NVENC.Preset.mq="Najlepsza jakość" NVENC.Preset.ll="Niskie opóźnienie" -NVENC.Preset.llhq="Niskie opóźnienie - wysoka jakość" -NVENC.Preset.llhp="Niskie opóźnienie - wysoka wydajność" -NVENC.Level="Poziom" +NVENC.Preset.llhq="Niskie opóźnienie - jakość" +NVENC.Preset.llhp="Niskie opóźnienie - wydajność" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Umożliwia dynamiczne klatki B.\n\nJeśli jest wyłączony, koder będzie zawsze używać liczbę klatek B określonego w ustawieniach 'Maksymalnych klatkach B'.\n\nJeśli włączony, zwiększy jakość obrazu przy użyciu jakkolwiek wiele klatek B które są konieczne, do maksymum\nkosztem zwiększenia wykorzystania procesora graficznego." +NVENC.PsychoVisualTuning="Psycho Visual Tuning" +NVENC.PsychoVisualTuning.ToolTip="Umożliwia ustawienia kodera, które optymalizują użycie bitratów dla zwiększonej jakość wizualnej,\n, zwłaszcza w sytuacjach z wysokim ruchem, kosztem zwiększonego wykorzystania procesora graficznego." +NVENC.CQLevel="Poziom CQ" FFmpegSource="Źródło danych" LocalFile="Plik lokalny" @@ -26,7 +31,7 @@ Input="Wejście" InputFormat="Format wejściowy" BufferingMB="Bufor sieciowy (MB)" HardwareDecode="Użyj sprzętowego dekodowania gdy to możliwe" -ClearOnMediaEnd="Ukryj źródło po zakończeniu odtwarzania" +ClearOnMediaEnd="Po zakończeniu odtwarzania nie pokazuj nic" Advanced="Zaawansowane" RestartWhenActivated="Zrestartuj odtwarzanie, gdy źródła będą aktywne" CloseFileWhenInactive="Zamknij plik, gdy niekatywne" @@ -44,9 +49,10 @@ MediaFileFilter.VideoFiles="Pliki video" MediaFileFilter.AudioFiles="Pliki audio" MediaFileFilter.AllFiles="Wszystkie pliki" -ReplayBuffer="Bufor replay" -ReplayBuffer.Save="Zapisz replay" +ReplayBuffer="Nagrywanie powtórek" +ReplayBuffer.Save="Zapisz nagraną powtórkę" HelperProcessFailed="Nie można uruchomić procesu nagrywania. Sprawdź, czy pliki OBS nie są blokowane lub usunięte przez oprogramowanie zewnętrzne, np. antywirusowe lub chroniące system." UnableToWritePath="Nie można zapisać do %1. Sprawdź poprawność ścieżki zapisu, prawa dostępu do niej oraz wolne miejsce na dysku." +WarnWindowsDefender="Błąd ten może być spowodowany również włączoną ochroną Windows 10 przed ransomware. Prosimy spróbować wyłączyć \"Kontrolowany dostęp do folderu\" w sekcji Ochrona przed wirusami i zagrożeniami." diff --git a/plugins/obs-ffmpeg/data/locale/pt-BR.ini b/plugins/obs-ffmpeg/data/locale/pt-BR.ini index 2004e1a..85fa70e 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-BR.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-BR.ini @@ -2,22 +2,27 @@ FFmpegOutput="Saída do FFmpeg" FFmpegAAC="Codificador AAC Padrão do FFmpeg" FFmpegOpus="Codificador FFmpeg Optus" Bitrate="Taxa de Bits" +MaxBitrate="Taxa de Bits Máxima" Preset="Predefinição" RateControl="Controle da Taxa de Bits" KeyframeIntervalSec="Intervalo de Keyframe (segundos, 0=auto)" Lossless="Sem perdas" -BFrames="B-frames" +BFrames="B-Frames Máximo" NVENC.Use2Pass="Utilizar a codificação em dois passos" -NVENC.Preset.default="Padrão" -NVENC.Preset.hq="Alta Qualidade" -NVENC.Preset.hp="Alta Performance" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Desempenho" +NVENC.Preset.hq="Qualidade" +NVENC.Preset.hp="Alto Desempenho" +NVENC.Preset.mq="Qualidade Máxima" NVENC.Preset.ll="Baixa Latência" -NVENC.Preset.llhq="Baixa Latência e Alta Qualidade" -NVENC.Preset.llhp="Baixa latência e Alta Performance" -NVENC.Level="Nível" +NVENC.Preset.llhq="Qualidade com Baixa Latência" +NVENC.Preset.llhp="Desempenho com Baixa Latência" +NVENC.LookAhead="Olhar quadros futuros" +NVENC.LookAhead.ToolTip="Habilita B-Frames dinâmicos.\n\nSe desabilitado, o codificador sempre usará o número de B-Frames especificados na configuração de 'B-Frames Máximo'.\n\nSe habilitado, aumentará a qualidade visual usando apenas B-Frames necessários, até o máximo,\nao custo de uma maior utilização da GPU." +NVENC.PsychoVisualTuning="Ajuste Psico-Visual" +NVENC.PsychoVisualTuning.ToolTip="Permite configurações do codificador que otimizam o uso da taxa de bits para uma melhoria\n perceptível na qualidade - especialmente em situações com muitos movimentos - com o custo de mais utilização da GPU." +NVENC.CQLevel="Nível de CQ" FFmpegSource="Fonte de mídia" LocalFile="Arquivo Local" @@ -26,11 +31,11 @@ Input="Entrada" InputFormat="Formato de entrada" BufferingMB="Buffer de Rede (MB)" HardwareDecode="Utilizar descodificação de hardware quando disponível" -ClearOnMediaEnd="Ocultar fonte quando a reprodução terminar" +ClearOnMediaEnd="Não mostrar nada quando terminar a reprodução" Advanced="Avançado" 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." +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" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Salvar Replay" HelperProcessFailed="Não foi possível iniciar o processo auxiliar de gravação. Verifique se os arquivos do OBS não foram bloqueados ou removidos por qualquer outro software (Ex: antivírus)." UnableToWritePath="Não foi possível escrever em %1. Certifique-se de que você está usando um caminho de gravação que sua conta de usuário possui permissão para gravar e que há espaço suficiente no disco." +WarnWindowsDefender="Este erro pode acontecer também por causa da Proteção contra Ransomware do Windows 10. Experimente desligar o acesso controlado a pastas em: Configurações do Windows Defender > Proteção contra vírus e ameaças." diff --git a/plugins/obs-ffmpeg/data/locale/pt-PT.ini b/plugins/obs-ffmpeg/data/locale/pt-PT.ini index 54a5ba0..d8e2cee 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-PT.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-PT.ini @@ -1,20 +1,21 @@ FFmpegOutput="Saída de FFmpeg" FFmpegAAC="Codificador AAC padrão do FFmpeg" +FFmpegOpus="FFmpeg Opus Encoder" Bitrate="Bitrate" +MaxBitrate="Bitrate Máximo" Preset="Predefinição" RateControl="Controle de Taxa" KeyframeIntervalSec="Intervalo do keyframe (segundos, 0=automático)" Lossless="Sem perdas" +BFrames="B-frames Máximas" -NVENC.Preset.default="Predefinido" -NVENC.Preset.hq="Alta Qualidade" -NVENC.Preset.hp="Alto Desempenho" -NVENC.Preset.bd="Bluray" +NVENC.Use2Pass="Usar codificação a dois passos" +NVENC.Preset.default="Desempenho" +NVENC.Preset.hq="Qualidade" +NVENC.Preset.hp="Máximo Desempenho" +NVENC.Preset.mq="Qualidade Máxima" NVENC.Preset.ll="Baixa latência" -NVENC.Preset.llhq="Baixa latência Alta Qualidade" -NVENC.Preset.llhp="Baixa latência Alto Desempenho" -NVENC.Level="Nível" FFmpegSource="Fonte de multimédia" LocalFile="Ficheiro local" @@ -22,13 +23,14 @@ Looping="Repetir" Input="Entrada" InputFormat="Formato de entrada" HardwareDecode="Utilizar descodificação de hardware quando disponível" -ClearOnMediaEnd="Ocultar fonte quando a reprodução terminar" Advanced="Avançado" RestartWhenActivated="Reiniciar reprodução quando a fonte se torna ativo" +CloseFileWhenInactive="Fechar ficheiro quando inativo" ColorRange="Gama de cor YUV" ColorRange.Auto="Auto" ColorRange.Partial="Parcial" ColorRange.Full="Completo" +SpeedPercentage="Velocidade (percentagem)" MediaFileFilter.AllMediaFiles="Todos os Arquivos de Media" MediaFileFilter.VideoFiles="Arquivos de Vídeo" diff --git a/plugins/obs-ffmpeg/data/locale/ro-RO.ini b/plugins/obs-ffmpeg/data/locale/ro-RO.ini index 6ac99f2..535519b 100644 --- a/plugins/obs-ffmpeg/data/locale/ro-RO.ini +++ b/plugins/obs-ffmpeg/data/locale/ro-RO.ini @@ -2,19 +2,25 @@ FFmpegOutput="Ieșire FFmpeg" FFmpegAAC="Codificator AAC implicit FFmpeg" Bitrate="Rată de biți" Preset="Presetare" +RateControl="Controlul ratei" +KeyframeIntervalSec="Interval de cadre cheie (secunde, 0=auto)" -NVENC.Preset.default="Implicită" -NVENC.Preset.bd="Bluray" -NVENC.Level="Nivel" +NVENC.Preset.default="Performanță" +NVENC.Preset.hq="Calitate" +NVENC.Preset.hp="Performanță maximă" +NVENC.Preset.mq="Calitate maximă" +NVENC.Preset.ll="Latență redusă" +NVENC.Preset.llhq="Calitate cu latență redusă" +NVENC.Preset.llhp="Performanță cu latență redusă" FFmpegSource="Sursă media" LocalFile="Fișier local" Looping="Buclă" Input="Intrare" InputFormat="Format de intrare" +BufferingMB="Zonă tampon pentru rețea (MB)" HardwareDecode="Folosește decodarea hardware când este disponibilă" -ClearOnMediaEnd="Ascunde sursa atunci când se termină redarea" Advanced="Avansat" RestartWhenActivated="Repornește redarea când sursa devine activă" ColorRange="Gamă de culori YUV" diff --git a/plugins/obs-ffmpeg/data/locale/ru-RU.ini b/plugins/obs-ffmpeg/data/locale/ru-RU.ini index c0aeeef..c68c87a 100644 --- a/plugins/obs-ffmpeg/data/locale/ru-RU.ini +++ b/plugins/obs-ffmpeg/data/locale/ru-RU.ini @@ -2,22 +2,27 @@ FFmpegOutput="Вывод FFmpeg" FFmpegAAC="Стандартный AAC-кодер FFmpeg" FFmpegOpus="Кодировщик FFmpeg Opus" Bitrate="Битрейт" +MaxBitrate="Макс. битрейт" Preset="Пресет" RateControl="Управление битрейтом" KeyframeIntervalSec="Интервал ключевых кадров (сек, 0=авто)" Lossless="Без потерь" -BFrames="B-Кадры" +BFrames="Макс. кол-во B-кадров" NVENC.Use2Pass="Использовать двухпроходное кодирование" -NVENC.Preset.default="По умолчанию" -NVENC.Preset.hq="Высокое качество" -NVENC.Preset.hp="Высокая производительность" -NVENC.Preset.bd="Blu-ray" +NVENC.Preset.default="Производительность" +NVENC.Preset.hq="Качество" +NVENC.Preset.hp="Макс. производительность" +NVENC.Preset.mq="Макс. качество" NVENC.Preset.ll="Малая задержка" NVENC.Preset.llhq="Малая задержка, высокое качество" NVENC.Preset.llhp="Малая задержка, высокая производительность" -NVENC.Level="Уровень" +NVENC.LookAhead="Предугадывание" +NVENC.LookAhead.ToolTip="Включает динамические B-кадры.\n\nЕсли отключено, кодировщик всегда будет использовать количество B-кадров, указанное в параметре 'Макс. кол-во B-кадров'.\n\nЕсли включено, то это увеличит визуальное качество изображения путем использования любого количества B-кадров, вплоть до максимума,\nза счет увеличения использования GPU." +NVENC.PsychoVisualTuning="Психовизуальная корректировка" +NVENC.PsychoVisualTuning.ToolTip="Включает настройки кодировщика, которые оптимизируют использование битрейта для повышенного восприятия визуального качества,\nособенно в ситуациях с высоким движением, за счет увеличения использования GPU." +NVENC.CQLevel="Уровень QC" FFmpegSource="Источник медиа" LocalFile="Локальный файл" @@ -26,7 +31,7 @@ Input="Ввод" InputFormat="Формат ввода" BufferingMB="Сетевая буферизация (МБ)" HardwareDecode="Использовать аппаратное декодирование при наличии" -ClearOnMediaEnd="Скрывать источник, когда воспроизведение заканчивается" +ClearOnMediaEnd="Ничего не показывать, когда воспроизведение заканчивается" Advanced="Дополнительно" RestartWhenActivated="Перезапустить воспроизведение, когда источник становится активным" CloseFileWhenInactive="Закрыть файл при отсутствии активности" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Сохранить повтор" HelperProcessFailed="Невозможно запустить вспомогательный процесс для записи. Проверьте, не были ли заблокированы или удалены файлы OBS сторонним антивирусом / защитным ПО." UnableToWritePath="Невозможно записать в %1. Убедитесь, что для вашей учетной записи разрешена запись по этому пути, и что на диске достаточно свободного пространства." +WarnWindowsDefender="Если включена Защита от программ-шантажистов Windows 10, это так же может вызывать эту ошибку. Попробуйте выключить контролируемый доступ к папкам в параметрах Безопасности Windows / Защита от вирусов и угроз." diff --git a/plugins/obs-ffmpeg/data/locale/sk-SK.ini b/plugins/obs-ffmpeg/data/locale/sk-SK.ini index 67fbe86..451fd83 100644 --- a/plugins/obs-ffmpeg/data/locale/sk-SK.ini +++ b/plugins/obs-ffmpeg/data/locale/sk-SK.ini @@ -3,13 +3,13 @@ FFmpegAAC="Predvolený FFmpeg AAC enkodér" FFmpegOpus="FFmpeg Opus enkodér" Bitrate="Bitrate" Preset="Predvoľba" +RateControl="Riadenie toku" +KeyframeIntervalSec="Interval kľúčových snímok (sekúnd, 0 = automaticky)" +Lossless="Bezstratová" -NVENC.Preset.default="Predvolená" -NVENC.Preset.hq="Vysoká kvalita" -NVENC.Preset.hp="Vysoký výkon" -NVENC.Preset.bd="BluRay" -NVENC.Level="Úroveň" +NVENC.Use2Pass="Použiť dvojfázové enkódovanie" +NVENC.Preset.ll="Nízka odozva" FFmpegSource="Zdroj médií" LocalFile="Lokálny súbor" @@ -19,15 +19,25 @@ InputFormat="Vstupný formát" BufferingMB="Sieťové zapisovanie do medzipamäte (MB)" HardwareDecode="Použiť hardvérové dekódovanie podľa dostupnosti" Advanced="Rozšírené" +RestartWhenActivated="Obnoviť prehrávanie pri aktivovaní zdroja" +CloseFileWhenInactive="Zatvoriť súbor pri neaktivite" +CloseFileWhenInactive.ToolTip="Zatvorí súbor, ak zdroj nie je práve zobrazovaný vo vysielaní\nalebo nahrávaní. Toto umožní zmeny súboru pri neaktivite zdroja,\nale môže spôsobiť oneskorenie pri opätovnom použití zdroja." ColorRange="Rozsah farieb YUV" ColorRange.Auto="Automaticky" ColorRange.Partial="Čiastočný" ColorRange.Full="Plný" +RestartMedia="Reštartuj mediálny zdroj" +SpeedPercentage="Rýchlosť (v percentách)" +Seekable="Posúvateľný" MediaFileFilter.AllMediaFiles="Všetky mediálne súbory" MediaFileFilter.VideoFiles="Video súbory" MediaFileFilter.AudioFiles="Zvukové súbory" MediaFileFilter.AllFiles="Všetky súbory" +ReplayBuffer="Medzipamäť znovuprehratia" +ReplayBuffer.Save="Uložiť záznam" +HelperProcessFailed="Nepodarilo sa spustiť pomocný proces nahrávania. Skontrolujte, či neboli súbory OBS zablokované alebo odstránené zabezpečovacím softvérom/antivírusom tretej strany." +UnableToWritePath="Nedá sa zapisovať do %1. Uistite sa, že v ceste nahrávania má váš používateľský účet právo na zápis a že máte dostatok miesta na disku." diff --git a/plugins/obs-ffmpeg/data/locale/sl-SI.ini b/plugins/obs-ffmpeg/data/locale/sl-SI.ini index 3d54ab6..2a40a24 100644 --- a/plugins/obs-ffmpeg/data/locale/sl-SI.ini +++ b/plugins/obs-ffmpeg/data/locale/sl-SI.ini @@ -10,7 +10,6 @@ Looping="Ponavljaj" Input="Vhod" InputFormat="Format vnosa" HardwareDecode="Uporabi strojno pospeševanje, ko je na voljo" -ClearOnMediaEnd="Skri vir, ko se predvajanje konča" Advanced="Napredno" diff --git a/plugins/obs-ffmpeg/data/locale/sr-CS.ini b/plugins/obs-ffmpeg/data/locale/sr-CS.ini index 839bc97..d2830ac 100644 --- a/plugins/obs-ffmpeg/data/locale/sr-CS.ini +++ b/plugins/obs-ffmpeg/data/locale/sr-CS.ini @@ -1,40 +1,57 @@ FFmpegOutput="FFmpeg izlaz" FFmpegAAC="FFmpeg podrazumevani AAC enkoder" +FFmpegOpus="FFmpeg Opus enkoder" Bitrate="Protok" +MaxBitrate="Maksimalni protok" Preset="Šablon" RateControl="Kontrola protoka" KeyframeIntervalSec="Interval ključnih frejmova (sekunde, 0=automatski)" Lossless="Bez gubitka" +BFrames="Maksimalni B-frejmovi" NVENC.Use2Pass="Koristi enkoding duplog prolaza" -NVENC.Preset.default="Podrazumevani" -NVENC.Preset.hq="Visoki kvalitet" -NVENC.Preset.hp="Visoke performanse" -NVENC.Preset.bd="Bluray" -NVENC.Preset.ll="Nisko kašnjenje" -NVENC.Preset.llhq="Visoki kvalitet niskog kašnjenja" -NVENC.Preset.llhp="Visoke performanse niskog kašnjenja" -NVENC.Level="Nivo" +NVENC.Preset.default="Performanse" +NVENC.Preset.hq="Kvalitet" +NVENC.Preset.hp="Maksimalne performanse" +NVENC.Preset.mq="Maksimalni kvalitet" +NVENC.Preset.ll="Malo kašnjenje" +NVENC.Preset.llhq="Kvalitet malog kašnjenja" +NVENC.Preset.llhp="Performanse malog kašnjenja" +NVENC.LookAhead="Gledanje unapred" +NVENC.LookAhead.ToolTip="Uključuje dinamičke B-frejmove.\n\n Ako je isključeno, enkoder će uvek koristiti broj B-frejmova naveden u 'Maksimalni B-frejmovi' delu.\n\nUkoliko je uključeno, povećaće vizuelni kvalitet korišćenjem onoliko B-frejmova koliko je potrebno, do maksimalnog broja,\npo cenu povećane upotrebe GPU-a." +NVENC.PsychoVisualTuning="Psiho vizuelna podešavanja" +NVENC.PsychoVisualTuning.ToolTip="Uključuje podešavanja enkodera koja optimizuju korišćenje protoka za povećani vizuelni kvalitet,\nposebno u situacijama sa ubrzanim pokretima, po cenu povećane upotrebe GPU-a." +NVENC.CQLevel="CQ Nivo" FFmpegSource="Medija izvor" LocalFile="Lokalna datoteka" Looping="Ponavljanje" Input="Ulaz" InputFormat="Format ulaza" +BufferingMB="Baferovanje mreže (MB)" HardwareDecode="Koristi hardversko enkodiranje kada je dostupno" -ClearOnMediaEnd="Sakrij izvor kada se reprodukcija završi" Advanced="Napredno" RestartWhenActivated="Ponovi reprodukciju kada izvor postane aktivan" +CloseFileWhenInactive="Zatvori fajl kada je neaktivan" +CloseFileWhenInactive.ToolTip="Zatvorite fajl kada izvor nije prikazan tokom strimovanja ili\nna snimku. Ovo omogućava fajlu da bude izmenjen kada izvor nije aktivan,\nali je moguće da se javi određeno kašnjenje kada se izvor ponovo aktivira." ColorRange="YUV opseg boja" ColorRange.Auto="Automatski" ColorRange.Partial="Delimični" ColorRange.Full="Potpuni" +RestartMedia="Restartuj medij" +SpeedPercentage="Brzina (procenat)" +Seekable="Pretraživanje" MediaFileFilter.AllMediaFiles="Sve medija datoteke" MediaFileFilter.VideoFiles="Video datoteke" MediaFileFilter.AudioFiles="Zvučne datoteke" MediaFileFilter.AllFiles="Sve datoteke" +ReplayBuffer="Bafer za ponovno reprodukovanje" +ReplayBuffer.Save="Sačuvaj ponovno reprodukovanje" +HelperProcessFailed="Nemoguće je pokrenuti pomoć za snimanje. Proverite da li su OBS fajlovi blokirani ili premešteni od strane nekog antivirus softvera/softvera za zaštitu." +UnableToWritePath="Nemoguće je pisati na %1. Proverite da li koristite putanju kojoj vaš nalog ima pravo da pristupi i da li imate dovoljno prostora na disku." +WarnWindowsDefender="Ova greška takođe može da nastane ako je Windows 10 Ransomware Protection uključen. Pokušajte da isključite kontrolisani pristup folderu u podešavanjima za Windows Sigurnost/Zaštitu od virusa." diff --git a/plugins/obs-ffmpeg/data/locale/sr-SP.ini b/plugins/obs-ffmpeg/data/locale/sr-SP.ini index ecfb53a..abf1911 100644 --- a/plugins/obs-ffmpeg/data/locale/sr-SP.ini +++ b/plugins/obs-ffmpeg/data/locale/sr-SP.ini @@ -1,40 +1,57 @@ FFmpegOutput="FFmpeg излаз" FFmpegAAC="FFmpeg подразумевани AAC енкодер" +FFmpegOpus="FFmpeg Opus енкодер" Bitrate="Проток" +MaxBitrate="Максимални проток" Preset="Шаблон" RateControl="Контрола протока" KeyframeIntervalSec="Интервал кључних фрејмова (секунде, 0=аутоматски)" Lossless="Без губитка" +BFrames="Максимални Б-фрејм" NVENC.Use2Pass="Користи енкодинг дуплог пролаза" -NVENC.Preset.default="Подразумевани" -NVENC.Preset.hq="Високи квалитет" -NVENC.Preset.hp="Високе перформансе" -NVENC.Preset.bd="Bluray" -NVENC.Preset.ll="Ниско кашњење" -NVENC.Preset.llhq="Високи квалитет ниског кашњења" -NVENC.Preset.llhp="Високе перформансе ниског кашњења" -NVENC.Level="Ниво" +NVENC.Preset.default="Перформансе" +NVENC.Preset.hq="Квалитет" +NVENC.Preset.hp="Максималне перформансе" +NVENC.Preset.mq="Максимални квалитет" +NVENC.Preset.ll="Мало кашњење" +NVENC.Preset.llhq="Квалитет малог кашњења" +NVENC.Preset.llhp="Перформансе малог кашњења" +NVENC.LookAhead="Гледање унапред" +NVENC.LookAhead.ToolTip="Укључује динамичке Б-фрејмове..\n\n Ако је искључено, енкодер ће увек користити број Б-фрејмова наведен у 'Максимални Б-фрејмови' делу.\n\nУколико је укључено, повећаће визуелни квалитет коришћењем онолико Б-фрејмова колико је потребно, до максималног броја,\nпо цену повећане употребе GPU-a." +NVENC.PsychoVisualTuning="Психо визуелна подешавања" +NVENC.PsychoVisualTuning.ToolTip="Укључује подешавања енкодера која оптимизују коришћење протока за повећани визуелни квалитет,\nпосебно у ситуацијама са убрзаним покретима, по цену повећане употребе GPU-а." +NVENC.CQLevel="CQ Ниво" FFmpegSource="Медија извор" LocalFile="Локална датотека" Looping="Понављање" Input="Улаз" InputFormat="Формат улаза" +BufferingMB="Баферовање мреже (мегабајти)" HardwareDecode="Користи хардверско енкодирање када је доступно" -ClearOnMediaEnd="Сакриј извор када се репродукција заврши" Advanced="Напредно" RestartWhenActivated="Понови репродукцију када извор постане активан" +CloseFileWhenInactive="Затвори фајл када је неактиван" +CloseFileWhenInactive.ToolTip="Затворите фајл када извор није приказан током стримовања или\nна снимку. Ово омогућава фајлу да буде измењен када извор није активан, \nали је могуће да се јави одређено кашњење када се извор поново активира." ColorRange="YUV опсег боја" ColorRange.Auto="Аутоматски" ColorRange.Partial="Делимични" ColorRange.Full="Потпуни" +RestartMedia="Рестартуј медиј" +SpeedPercentage="Брзина (проценат)" +Seekable="Претраживање" MediaFileFilter.AllMediaFiles="Све медија датотеке" MediaFileFilter.VideoFiles="Видео датотеке" MediaFileFilter.AudioFiles="Звучне датотеке" MediaFileFilter.AllFiles="Све датотеке" +ReplayBuffer="Бафер за поновно репродуковање" +ReplayBuffer.Save="Сачувај поновно репродуковање" +HelperProcessFailed="Немогуће је покренути помоћ за снимање. Проверите да ли су OBS фајлови блокирани или премештени од стране неког антивирус софтвера/софтвера за заштиту." +UnableToWritePath="Немогуће је писати на %1. Проверите да ли користите путању којој ваш налог има право да приступи и да ли имате довољно простора на диску." +WarnWindowsDefender="Ова грешка такође може да настане ако је Windows 10 Ransomware Protection укључен. Покушајте да искључите контролисани приступ фолдеру у подешавањима за Windows Сигурност/Заштиту од вируса." diff --git a/plugins/obs-ffmpeg/data/locale/sv-SE.ini b/plugins/obs-ffmpeg/data/locale/sv-SE.ini index e098af9..9a33cb5 100644 --- a/plugins/obs-ffmpeg/data/locale/sv-SE.ini +++ b/plugins/obs-ffmpeg/data/locale/sv-SE.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg-utmatning" FFmpegAAC="AAC-kodare (FFmpeg standard)" FFmpegOpus="FFmpeg Opus-kodare" Bitrate="Bithastighet" +MaxBitrate="Max bithastighet" Preset="Förinställning" RateControl="Hastighetskontroll" KeyframeIntervalSec="Intervall för keyframes (sekunder, 0=automatisk)" Lossless="Förlustfri" -BFrames="B-bildrutor" +BFrames="Maximalt antal B-frames" NVENC.Use2Pass="Använd tvåpassavkodning" -NVENC.Preset.default="Standard" -NVENC.Preset.hq="Hög kvalitet" -NVENC.Preset.hp="Hög prestanda" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Prestanda" +NVENC.Preset.hq="Kvalitet" +NVENC.Preset.hp="Maximal prestanda" +NVENC.Preset.mq="Maximal kvalitet" NVENC.Preset.ll="Låg latens" -NVENC.Preset.llhq="Låg latens, hög kvalitet" -NVENC.Preset.llhp="Låg latens, hög prestanda" -NVENC.Level="Nivå" +NVENC.Preset.llhq="Kvalitet med låg latens" +NVENC.Preset.llhp="Prestanda med låg latens" +NVENC.LookAhead="Look-ahead" +NVENC.LookAhead.ToolTip="Aktiverar dynamiska B-frames.\n\nOm detta inaktiveras kommer kodaren alltid använda antalet B-frames som anges i inställningen \"Max B-frames\".\n\nOm detta aktiveras kommer det öka den visuella kvaliteten genom att endast använda så många B-frames som är nödvändigt, upp till det som är möjligt\npå bekostnad av ökad användning av grafikprocessorn." +NVENC.PsychoVisualTuning="Psykovisuell justering" +NVENC.PsychoVisualTuning.ToolTip="Aktiverar kodarinställningar som optimerar hur bithastigheten används för förbättrad visuell kvalitet,\nspeciellt i situationer med snabba rörelser på bekostnad av ökad användning av grafikprocessorn." +NVENC.CQLevel="CQ-nivå" FFmpegSource="Mediakälla" LocalFile="Lokal fil" @@ -26,7 +31,7 @@ Input="Infoga" InputFormat="Inmatningsformat" BufferingMB="Nätverksbuffring (MB)" HardwareDecode="Använda hårdvareavkodning när tillgängligt" -ClearOnMediaEnd="Dölja källa när uppspelningen slutar" +ClearOnMediaEnd="Visa ingenting när uppspelningen slutar" Advanced="Avancerat" RestartWhenActivated="Starta om uppspelning när källa blir aktiv" CloseFileWhenInactive="Stäng filen vid inaktivitet" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Spara repris" HelperProcessFailed="Kan inte spela in hjälpprocessen. Kontrollera att OBS-filer inte har blockerats eller tagits bort av antivirus-/säkerhetsprogram från tredjepart." UnableToWritePath="Kan inte skriva till %1. Se till att du använder en inspelningssökväg som ditt användarkonto har tillåtelse att skriva till och att det finns tillräckligt mycket diskutrymme." +WarnWindowsDefender="Om skyddet mot ransomware i Windows 10 är aktiverat kan det också orsaka detta fel. Försök att inaktivera kontrollerad mappåtkomst i Windows-säkerhet / Inställningar för skydd mot virus & hot." diff --git a/plugins/obs-ffmpeg/data/locale/tl-PH.ini b/plugins/obs-ffmpeg/data/locale/tl-PH.ini index b3b3189..a9417f9 100644 --- a/plugins/obs-ffmpeg/data/locale/tl-PH.ini +++ b/plugins/obs-ffmpeg/data/locale/tl-PH.ini @@ -7,17 +7,9 @@ RateControl="Kontrolin ang Rate" KeyframeIntervalSec="Ang Pagitan ng Keyframe (segundo, 0=awto)" Lossless="Walang Pagkawala" -BFrames="Ang mga B-frame" NVENC.Use2Pass="Gamitin ang Dalawang Pass ng Encoding" -NVENC.Preset.default="I-default" -NVENC.Preset.hq="Mataas na Kalidad" -NVENC.Preset.hp="Mataas na Pagganap" -NVENC.Preset.bd="Ang Bluray" NVENC.Preset.ll="Mababang Pagkawalang kilos" -NVENC.Preset.llhq="Mababang-Pagkawalang kilos na Mataas ang Kalidad" -NVENC.Preset.llhp="Mababang-Pagkawalang kilos na Mataan ang Pagganap" -NVENC.Level="Antas" FFmpegSource="Pinagmulan ng Media" LocalFile="Ang Lokal na File" @@ -26,7 +18,6 @@ Input="Pampasok" InputFormat="Pampasok na Format" BufferingMB="Ang Network Buffering (MB)" HardwareDecode="Gamitin ang hardware sa pag-decode kapag itong magagamit na" -ClearOnMediaEnd="Itago ang pinagmulan kapag ang playback ay natapos" Advanced="Nauuna" RestartWhenActivated="I-restart ang playback kapag ang pinagmulan ay naging aktibo na" CloseFileWhenInactive="Isarado ang file kapag hindi ito aktibo" diff --git a/plugins/obs-ffmpeg/data/locale/tr-TR.ini b/plugins/obs-ffmpeg/data/locale/tr-TR.ini index c4c3b77..1c7b908 100644 --- a/plugins/obs-ffmpeg/data/locale/tr-TR.ini +++ b/plugins/obs-ffmpeg/data/locale/tr-TR.ini @@ -2,22 +2,23 @@ FFmpegOutput="FFmpeg Çıkışı" FFmpegAAC="FFmpeg Varsayılan AAC Kodlayıcı" FFmpegOpus="FFmpeg Opus Kodlayıcı" Bitrate="Bit hızı" +MaxBitrate="Maksimum Bit Hızı" Preset="Ön Tanımlı" RateControl="Oran Kontrolü" KeyframeIntervalSec="Anahtar Kare Aralığı (saniye, 0=otomatik)" Lossless="Kayıpsız" -BFrames="B-Kareleri" +BFrames="Maksimum B-kareleri" NVENC.Use2Pass="İki Taramalı Kodlama Kullan" -NVENC.Preset.default="Varsayılan" -NVENC.Preset.hq="Yüksek Kalite" -NVENC.Preset.hp="Yüksek Performans" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Performans" +NVENC.Preset.hq="Kalite" +NVENC.Preset.hp="Maksimum Performans" +NVENC.Preset.mq="Maksimum Kalite" NVENC.Preset.ll="Düşük Gecikme" -NVENC.Preset.llhq="Düşük Gecikme Yüksek Kalite" -NVENC.Preset.llhp="Düşük Gecikme Yüksek Performans" -NVENC.Level="Seviye" +NVENC.Preset.llhq="Düşük Gecikme, Düşük Kalite" +NVENC.Preset.llhp="Düşük Gecikme, Düşük Performans" +NVENC.PsychoVisualTuning.ToolTip="Bit hızı kullanımını, arttırılmış algılanan görsel kalite için optimize eden ayarları etkinleştirir,\nÖzellikle yüksek haraketli durumlarda, arttırılmış GPU kullanımı pahasına." FFmpegSource="Ortam Kaynağı" LocalFile="Yerel Dosya" @@ -26,7 +27,6 @@ Input="Giriş" InputFormat="Giriş Biçimi" BufferingMB="Ağ Arabelleğe Alma (MB)" HardwareDecode="Kullanılabilir ise, donanım kod çözmeyi kullan" -ClearOnMediaEnd="Kayıttan yürütme bittiğinde kaynağı gizle" Advanced="Gelişmiş" RestartWhenActivated="Yeniden oynatmayı kaynak etkin olduğunda yeniden başlat" CloseFileWhenInactive="Etkin değilken dosyayı kapat" @@ -49,4 +49,5 @@ ReplayBuffer.Save="Yeniden Oynatmayı Kaydet" HelperProcessFailed="Kayıt yardımcısı işlemi başlatılamadı. OBS dosyalarının herhangi bir 3. taraf antivirüs / güvenlik yazılımı tarafından engellenmediğini veya kaldırılmadığını kontrol edin." UnableToWritePath="%1 yazılamadı. Kullanıcı hesabınızın yazmasına izin verilen bir kayıt konumu kullanıyor olduğunuzdan ve yeterli disk alanı olduğundan emin olun." +WarnWindowsDefender="Eğer Windows 10 Fidye Virüsü koruması etkinse bu hataya neden olabilir. Windows Güvenlik / Virüs & tehdit koruması ayarlarından kontrollü klasör erişimini kapatmayı dene." diff --git a/plugins/obs-ffmpeg/data/locale/uk-UA.ini b/plugins/obs-ffmpeg/data/locale/uk-UA.ini index 88c2d44..549cf29 100644 --- a/plugins/obs-ffmpeg/data/locale/uk-UA.ini +++ b/plugins/obs-ffmpeg/data/locale/uk-UA.ini @@ -2,22 +2,27 @@ FFmpegOutput="Вивід FFmpeg" FFmpegAAC="FFmpeg AAC енкодер за замовчанням" FFmpegOpus="FFmpeg Opus енкодер" Bitrate="Бітрейт" +MaxBitrate="Максимальний бітрейт" Preset="Шаблон" RateControl="Керування потоком" KeyframeIntervalSec="Інтервал ключових кадрів (секунд, 0 = авто)" Lossless="Без втрат" -BFrames="B-кадри" +BFrames="B-кадрів, максимально" NVENC.Use2Pass="Використовувати двопрохідне кодування" -NVENC.Preset.default="Стандартний" -NVENC.Preset.hq="Висока якість" -NVENC.Preset.hp="Висока продуктивність" -NVENC.Preset.bd="Blu-ray" +NVENC.Preset.default="Продуктивність" +NVENC.Preset.hq="Якість" +NVENC.Preset.hp="Максимальна продуктивність" +NVENC.Preset.mq="Максимальна якість" NVENC.Preset.ll="З низькою затримкою" -NVENC.Preset.llhq="З низькою затримкою, висока Якість" -NVENC.Preset.llhp="З низькою затримкою, висока Продуктивність" -NVENC.Level="Рівень" +NVENC.Preset.llhq="З низькою затримкою, Якість" +NVENC.Preset.llhp="З низькою затримкою, Продуктивність" +NVENC.LookAhead="Передбачення" +NVENC.LookAhead.ToolTip="Дозволяє використовувати динамічні B-кадри.\n\nЯкщо вимкнено, енкодер завжди буде використовувати кількість B-кадрів,\nвказаних у налаштуванні: B-кадрів, максимально.\n\nЯкщо увімкнено, це поліпшить якість завдяки використанню необхідної\nта достатньої кількості B-кадрів (не більше вказаного максимуму), однак це відбудеться\nза рахунок збільшення навантаження на графічний процессор." +NVENC.PsychoVisualTuning="Психо-візуальні спрощення" +NVENC.PsychoVisualTuning.ToolTip="Дозволяє енкодеру використовувати методи оптимізації з розподілення бітрейту\nдля підвищення візуального сприйняття якості, особливо в сценах з швидким рухом.\nВідбувається за рахунок збільшення навантаження на графічний процесор." +NVENC.CQLevel="CQ (постійне квантування), рівень" FFmpegSource="Мультимедіа" LocalFile="Локальний файл" @@ -49,4 +54,5 @@ ReplayBuffer.Save="Зберегти Повтор" HelperProcessFailed="Не вдалося розпочати допоміжний процес для запису. Перевірте, що OBS файли не було заблоковано чи видалено антивірусом або будь-яким іншим програмним забезпеченням з безпеки." UnableToWritePath="Не вдалося записати до %1. Переконайтеся, що ви використовуєте для запису шлях, до якого ваш обліковий запис має дозвіл на запис, і що там є достатньо вільного місця." +WarnWindowsDefender="Якщо Windows 10 Ransomware Protection (захист від програм-вимагачів) увімкнуто у системі - це також може бути причиною появи цієї помилки. Спробуйте вимкнути контроль за доступом до папок у Безпека Windows Захист від вірусів та загроз." diff --git a/plugins/obs-ffmpeg/data/locale/vi-VN.ini b/plugins/obs-ffmpeg/data/locale/vi-VN.ini index b558018..3132009 100644 --- a/plugins/obs-ffmpeg/data/locale/vi-VN.ini +++ b/plugins/obs-ffmpeg/data/locale/vi-VN.ini @@ -1,22 +1,21 @@ FFmpegOutput="FFmpeg đầu ra" FFmpegAAC="FFmpeg AAC Encoder mặc định" Bitrate="Bitrate" +MaxBitrate="'Tốc độ bit' tối đa" Preset="Mẫu thiết lập" RateControl="Cách kiểm soát bitrate" KeyframeIntervalSec="Thời gian đặt Keyframe (giây, 0=tự động)" Lossless="Lossless" -BFrames="B-Frames" NVENC.Use2Pass="Sử dụng 2-Pass Encoding" -NVENC.Preset.default="Mặc định" -NVENC.Preset.hq="Chất lượng cao" -NVENC.Preset.hp="Hiệu suất cao" -NVENC.Preset.bd="Bluray" +NVENC.Preset.default="Hiệu suất" +NVENC.Preset.hq="Chất lượng" +NVENC.Preset.hp="Hiệu suất tối đa" +NVENC.Preset.mq="Chất lượng tối đa" NVENC.Preset.ll="Độ trễ thấp" -NVENC.Preset.llhq="Độ trễ thấp chất lượng cao" -NVENC.Preset.llhp="Độ trễ thấp hiệu suất cao" -NVENC.Level="Cấp độ" +NVENC.Preset.llhq="Chất lượng \"độ trễ thấp\"" +NVENC.Preset.llhp="Hiệu suất \"độ trễ thấp\"" FFmpegSource="Nguồn media" LocalFile="Tập tin cục bộ" @@ -38,5 +37,6 @@ MediaFileFilter.AudioFiles="Tập tin âm thanh" MediaFileFilter.AllFiles="Tất cả tập tin" ReplayBuffer="Replay Buffer" +ReplayBuffer.Save="Lưu bản phát lại" diff --git a/plugins/obs-ffmpeg/data/locale/zh-CN.ini b/plugins/obs-ffmpeg/data/locale/zh-CN.ini index f630e62..5ef0fc1 100644 --- a/plugins/obs-ffmpeg/data/locale/zh-CN.ini +++ b/plugins/obs-ffmpeg/data/locale/zh-CN.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg 输出" FFmpegAAC="FFmpeg 默认 AAC 编码器" FFmpegOpus="FFmpeg Opus 编码器" Bitrate="比特率" +MaxBitrate="最大比特率" Preset="预设" RateControl="速率控制" KeyframeIntervalSec="关键帧间隔(秒, 0=自动)" Lossless="无损" -BFrames="B 帧" +BFrames="最大B帧" NVENC.Use2Pass="使用 Two-Pass 编码" -NVENC.Preset.default="默认" -NVENC.Preset.hq="高质量" -NVENC.Preset.hp="高性能" -NVENC.Preset.bd="蓝光" +NVENC.Preset.default="性能" +NVENC.Preset.hq="质量" +NVENC.Preset.hp="最大性能" +NVENC.Preset.mq="最高质量" NVENC.Preset.ll="低延迟" -NVENC.Preset.llhq="低延迟高质量" -NVENC.Preset.llhp="低延迟高性能" -NVENC.Level="等级" +NVENC.Preset.llhq="低延迟质量" +NVENC.Preset.llhp="低延迟性能" +NVENC.LookAhead="前向考虑" +NVENC.LookAhead.ToolTip="启用动态B帧。\n\n如果禁用,编码器将始终使用“最大B帧”设置中指定的B帧数。\n\n如果启用,它将通过仅使用足够多的B帧来提高视觉质量,直到最大,\n但以增加 GPU 使用率为代价。" +NVENC.PsychoVisualTuning="心理视觉调整" +NVENC.PsychoVisualTuning.ToolTip="启用编码器设置以优化比特率使用,以提高 GPU 使用率为代价,\n可在快速运动场景下提高人眼感知的视频质量。" +NVENC.CQLevel="CQ 级别" FFmpegSource="媒体源" LocalFile="本地文件" @@ -26,11 +31,11 @@ Input="输入" InputFormat="输入格式" BufferingMB="网络缓冲 (MB)" HardwareDecode="在可用时使用硬件解码" -ClearOnMediaEnd="当播放结束时隐藏源" +ClearOnMediaEnd="播放结束时不显示任何内容" Advanced="高级" RestartWhenActivated="当源变为活动状态时重新启动播放" CloseFileWhenInactive="非活跃状态时关闭文件" -CloseFileWhenInactive.ToolTip="当源没有显示在推流或者\n录像时关闭文件。这使得在源不活跃状态时可以更改文件,\n但是当当源重新激活时, 可能有一些启动延迟。" +CloseFileWhenInactive.ToolTip="当源没有被用以串流或录像时关闭文件。\n这允许在源未被使用时更改文件,\n但在重新启动源时可能会有些许的启动延迟。" ColorRange="YUV 颜色范围" ColorRange.Auto="自动" ColorRange.Partial="局部" @@ -47,6 +52,7 @@ MediaFileFilter.AllFiles="所有文件" ReplayBuffer="回放缓存" ReplayBuffer.Save="保存回放" -HelperProcessFailed="无法启动录音助手进程。检查 OBS 文件未被任何第三方防病毒 / 安全软件阻止或删除。" -UnableToWritePath="无法写入到 %1。请确保您使用的录制路径您的用户帐户允许写入,并有足够的磁盘空间。" +HelperProcessFailed="无法启动录制助手进程。检查 OBS 文件未被任何第三方防病毒 / 安全软件阻止或删除。" +UnableToWritePath="无法写入到 %1。请确保您使用的录像路径允许您的用户帐户写入,并且磁盘空间充足。" +WarnWindowsDefender="Windows 10 的勒索软件防护机制也可能导致该错误的发生。请尝试关闭 Windows 安全中心 - 勒索软件防护中的文件夹限制访问。" diff --git a/plugins/obs-ffmpeg/data/locale/zh-TW.ini b/plugins/obs-ffmpeg/data/locale/zh-TW.ini index 88ee839..f1683d8 100644 --- a/plugins/obs-ffmpeg/data/locale/zh-TW.ini +++ b/plugins/obs-ffmpeg/data/locale/zh-TW.ini @@ -2,22 +2,27 @@ FFmpegOutput="FFmpeg 輸出" FFmpegAAC="FFmpeg 預設 AAC 編碼器" FFmpegOpus="FFmpeg Opus 編碼器" Bitrate="位元率" +MaxBitrate="最大位元率" Preset="預置" RateControl="位元率控制" KeyframeIntervalSec="關鍵訊框間隔 (秒,0 = 自動)" Lossless="無損" -BFrames="B 訊框" +BFrames="最大 B 畫格數" NVENC.Use2Pass="使用 Two-Pass 編碼" -NVENC.Preset.default="預設" -NVENC.Preset.hq="高品質" -NVENC.Preset.hp="高性能" -NVENC.Preset.bd="藍光" +NVENC.Preset.default="效能" +NVENC.Preset.hq="畫質" +NVENC.Preset.hp="效能最高" +NVENC.Preset.mq="品質最高" NVENC.Preset.ll="低延遲" -NVENC.Preset.llhq="低延遲高品質" -NVENC.Preset.llhp="低延遲高性能" -NVENC.Level="级别" +NVENC.Preset.llhq="低延遲品質" +NVENC.Preset.llhp="低延遲效能" +NVENC.LookAhead="編碼緩衝預測" +NVENC.LookAhead.ToolTip="啟用動態B幀。 \n\n如果禁用,編碼器將始終使用“最大B幀”設置中指定的B幀數。 \n\n如果啟用,它將僅通過使用盡可能多的B幀來提高視覺品質,直到最大,\n但以增加 GPU 使用率為代價。" +NVENC.PsychoVisualTuning="心理視覺調整" +NVENC.PsychoVisualTuning.ToolTip="啟用優化比特率使用的編碼器設置,以提高感知的視覺品質,\n特別是在高運動的情況下,以提高 GPU 使用率為代價。" +NVENC.CQLevel="固定量化等級" FFmpegSource="媒體來源" LocalFile="本機檔案" @@ -26,7 +31,7 @@ Input="輸入" InputFormat="輸入格式" BufferingMB="網路緩衝 (MB)" HardwareDecode="盡可能使用硬體解碼" -ClearOnMediaEnd="當播放結束時隱藏來源" +ClearOnMediaEnd="播放結束時不顯示任何內容" Advanced="進階" RestartWhenActivated="當來源可使用時重新播放" CloseFileWhenInactive="非使用狀態時關閉檔案" @@ -49,4 +54,5 @@ ReplayBuffer.Save="儲存重播" HelperProcessFailed="無法啟動錄影協助程式。請確定 OBS 檔案沒有被防毒/安全軟體所阻擋或移除。" UnableToWritePath="無法寫入到 %1。請確定使用者帳戶可以寫入錄影檔路徑以及有足夠的磁碟空間。" +WarnWindowsDefender="如果啟用了 windows 10 勒索軟體保護, 也可能導致此錯誤。請嘗試將 obs 從 windows 安全/病毒和威脅防護設置中的受控資料夾訪問清單中移除。" diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt index 8c564d8..c464b8e 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt +++ b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt @@ -1,26 +1,20 @@ -project(ffmpeg-mux) +project(obs-ffmpeg-mux) find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat) include_directories(${FFMPEG_INCLUDE_DIRS}) -set(ffmpeg-mux_SOURCES +set(obs-ffmpeg-mux_SOURCES ffmpeg-mux.c) -set(ffmpeg-mux_HEADERS +set(obs-ffmpeg-mux_HEADERS ffmpeg-mux.h) -add_executable(ffmpeg-mux - ${ffmpeg-mux_SOURCES} - ${ffmpeg-mux_HEADERS}) +add_executable(obs-ffmpeg-mux + ${obs-ffmpeg-mux_SOURCES} + ${obs-ffmpeg-mux_HEADERS}) -target_link_libraries(ffmpeg-mux +target_link_libraries(obs-ffmpeg-mux ${FFMPEG_LIBRARIES}) -if(WIN32) - set_target_properties(ffmpeg-mux - PROPERTIES - OUTPUT_NAME "ffmpeg-mux${_output_suffix}") -endif() - -install_obs_datatarget(ffmpeg-mux "obs-plugins/obs-ffmpeg") +install_obs_core(obs-ffmpeg-mux) diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c index b3c5c88..728555d 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c @@ -268,7 +268,7 @@ static bool new_stream(struct ffmpeg_mux *ffm, AVStream **stream, AVCodec *codec; if (!desc) { - printf("Couldn't find encoder '%s'\n", name); + fprintf(stderr, "Couldn't find encoder '%s'\n", name); return false; } @@ -276,13 +276,13 @@ static bool new_stream(struct ffmpeg_mux *ffm, AVStream **stream, codec = avcodec_find_encoder(desc->id); if (!codec) { - printf("Couldn't create encoder"); + fprintf(stderr, "Couldn't create encoder"); return false; } *stream = avformat_new_stream(ffm->output, codec); if (!*stream) { - printf("Couldn't create stream for encoder '%s'\n", name); + fprintf(stderr, "Couldn't create stream for encoder '%s'\n", name); return false; } @@ -469,7 +469,7 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) ret = avio_open(&ffm->output->pb, ffm->params.file, AVIO_FLAG_WRITE); if (ret < 0) { - printf("Couldn't open '%s', %s", + fprintf(stderr, "Couldn't open '%s', %s", ffm->params.file, av_err2str(ret)); return FFM_ERROR; } @@ -482,7 +482,7 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) AVDictionary *dict = NULL; if ((ret = av_dict_parse_string(&dict, ffm->params.muxer_settings, "=", " ", 0))) { - printf("Failed to parse muxer settings: %s\n%s", + fprintf(stderr, "Failed to parse muxer settings: %s\n%s", av_err2str(ret), ffm->params.muxer_settings); av_dict_free(&dict); @@ -501,7 +501,7 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) ret = avformat_write_header(ffm->output, &dict); if (ret < 0) { - printf("Error opening '%s': %s", + fprintf(stderr, "Error opening '%s': %s", ffm->params.file, av_err2str(ret)); av_dict_free(&dict); @@ -521,7 +521,7 @@ static int ffmpeg_mux_init_context(struct ffmpeg_mux *ffm) output_format = av_guess_format(NULL, ffm->params.file, NULL); if (output_format == NULL) { - printf("Couldn't find an appropriate muxer for '%s'\n", + fprintf(stderr, "Couldn't find an appropriate muxer for '%s'\n", ffm->params.file); return FFM_ERROR; } @@ -529,7 +529,7 @@ static int ffmpeg_mux_init_context(struct ffmpeg_mux *ffm) ret = avformat_alloc_output_context2(&ffm->output, output_format, NULL, NULL); if (ret < 0) { - printf("Couldn't initialize output context: %s\n", + fprintf(stderr, "Couldn't initialize output context: %s\n", av_err2str(ret)); return FFM_ERROR; } @@ -679,7 +679,7 @@ int main(int argc, char *argv[]) ret = ffmpeg_mux_init(&ffm, argc, argv); if (ret != FFM_SUCCESS) { - puts("Couldn't initialize muxer"); + fprintf(stderr, "Couldn't initialize muxer\n"); return ret; } diff --git a/plugins/obs-ffmpeg/jim-nvenc-helpers.c b/plugins/obs-ffmpeg/jim-nvenc-helpers.c new file mode 100644 index 0000000..68d20b3 --- /dev/null +++ b/plugins/obs-ffmpeg/jim-nvenc-helpers.c @@ -0,0 +1,149 @@ +#include "jim-nvenc.h" +#include +#include + +static void *nvenc_lib = NULL; +static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; +NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER}; +NV_CREATE_INSTANCE_FUNC nv_create_instance = NULL; + +#define error(format, ...) \ + blog(LOG_ERROR, "[jim-nvenc] " format, ##__VA_ARGS__) + +static inline bool nv_failed(NVENCSTATUS err, const char *func, const char *call) +{ + if (err == NV_ENC_SUCCESS) + return false; + + error("%s: %s failed: %d (%s)", func, call, (int)err, + nv_error_name(err)); + return true; +} + +#define NV_FAILED(x) nv_failed(x, __FUNCTION__, #x) + +bool load_nvenc_lib(void) +{ + if (sizeof(void*) == 8) { + nvenc_lib = os_dlopen("nvEncodeAPI64.dll"); + } else { + nvenc_lib = os_dlopen("nvEncodeAPI.dll"); + } + + return !!nvenc_lib; +} + +static void *load_nv_func(const char *func) +{ + void *func_ptr = os_dlsym(nvenc_lib, func); + if (!func_ptr) { + error("Could not load function: %s", func); + } + return func_ptr; +} + +typedef NVENCSTATUS (NVENCAPI *NV_MAX_VER_FUNC)(uint32_t*); + +const char *nv_error_name(NVENCSTATUS err) +{ +#define RETURN_CASE(x) \ + case x: return #x + + switch (err) { + RETURN_CASE(NV_ENC_SUCCESS); + RETURN_CASE(NV_ENC_ERR_NO_ENCODE_DEVICE); + RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_DEVICE); + RETURN_CASE(NV_ENC_ERR_INVALID_ENCODERDEVICE); + RETURN_CASE(NV_ENC_ERR_INVALID_DEVICE); + RETURN_CASE(NV_ENC_ERR_DEVICE_NOT_EXIST); + RETURN_CASE(NV_ENC_ERR_INVALID_PTR); + RETURN_CASE(NV_ENC_ERR_INVALID_EVENT); + RETURN_CASE(NV_ENC_ERR_INVALID_PARAM); + RETURN_CASE(NV_ENC_ERR_INVALID_CALL); + RETURN_CASE(NV_ENC_ERR_OUT_OF_MEMORY); + RETURN_CASE(NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_PARAM); + RETURN_CASE(NV_ENC_ERR_LOCK_BUSY); + RETURN_CASE(NV_ENC_ERR_NOT_ENOUGH_BUFFER); + RETURN_CASE(NV_ENC_ERR_INVALID_VERSION); + RETURN_CASE(NV_ENC_ERR_MAP_FAILED); + RETURN_CASE(NV_ENC_ERR_NEED_MORE_INPUT); + RETURN_CASE(NV_ENC_ERR_ENCODER_BUSY); + RETURN_CASE(NV_ENC_ERR_EVENT_NOT_REGISTERD); + RETURN_CASE(NV_ENC_ERR_GENERIC); + RETURN_CASE(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY); + RETURN_CASE(NV_ENC_ERR_UNIMPLEMENTED); + RETURN_CASE(NV_ENC_ERR_RESOURCE_REGISTER_FAILED); + RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_REGISTERED); + RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_MAPPED); + } +#undef RETURN_CASE + + return "Unknown Error"; +} + +static inline bool init_nvenc_internal(void) +{ + static bool initialized = false; + static bool success = false; + + if (initialized) + return success; + initialized = true; + + NV_MAX_VER_FUNC nv_max_ver = (NV_MAX_VER_FUNC) + load_nv_func("NvEncodeAPIGetMaxSupportedVersion"); + if (!nv_max_ver) { + return false; + } + + uint32_t ver = 0; + if (NV_FAILED(nv_max_ver(&ver))) { + return false; + } + + uint32_t cur_ver = + (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + if (cur_ver > ver) { + error("Current driver version does not support this NVENC " + "version, please upgrade your driver"); + return false; + } + + nv_create_instance = (NV_CREATE_INSTANCE_FUNC) + load_nv_func("NvEncodeAPICreateInstance"); + if (!nv_create_instance) { + return false; + } + + if (NV_FAILED(nv_create_instance(&nv))) { + return false; + } + + success = true; + return true; +} + +bool init_nvenc(void) +{ + bool success; + + pthread_mutex_lock(&init_mutex); + success = init_nvenc_internal(); + pthread_mutex_unlock(&init_mutex); + + return success; +} + +extern struct obs_encoder_info nvenc_info; + +void jim_nvenc_load(void) +{ + pthread_mutex_init(&init_mutex, NULL); + obs_register_encoder(&nvenc_info); +} + +void jim_nvenc_unload(void) +{ + pthread_mutex_destroy(&init_mutex); +} diff --git a/plugins/obs-ffmpeg/jim-nvenc.c b/plugins/obs-ffmpeg/jim-nvenc.c new file mode 100644 index 0000000..6eae7f9 --- /dev/null +++ b/plugins/obs-ffmpeg/jim-nvenc.c @@ -0,0 +1,945 @@ +#include "jim-nvenc.h" +#include +#include +#include +#include +#define INITGUID +#include +#include +#include + +/* ========================================================================= */ + +#define EXTRA_BUFFERS 5 + +#define do_log(level, format, ...) \ + blog(level, "[jim-nvenc: '%s'] " format, \ + obs_encoder_get_name(enc->encoder), ##__VA_ARGS__) + +#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__) +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) + +#define error_hr(msg) \ + error("%s: %s: 0x%08lX", __FUNCTION__, msg, (uint32_t)hr); + +struct nv_bitstream; +struct nv_texture; + +struct handle_tex { + uint32_t handle; + ID3D11Texture2D *tex; + IDXGIKeyedMutex *km; +}; + +/* ------------------------------------------------------------------------- */ +/* Main Implementation Structure */ + +struct nvenc_data { + obs_encoder_t *encoder; + + void *session; + NV_ENC_INITIALIZE_PARAMS params; + NV_ENC_CONFIG config; + size_t buf_count; + size_t output_delay; + size_t buffers_queued; + size_t next_bitstream; + size_t cur_bitstream; + bool encode_started; + bool first_packet; + bool can_change_bitrate; + bool bframes; + + DARRAY(struct nv_bitstream) bitstreams; + DARRAY(struct nv_texture) textures; + DARRAY(struct handle_tex) input_textures; + struct circlebuf dts_list; + + DARRAY(uint8_t) packet_data; + int64_t packet_pts; + bool packet_keyframe; + + ID3D11Device *device; + ID3D11DeviceContext *context; + + uint32_t cx; + uint32_t cy; + + uint8_t *header; + size_t header_size; + + uint8_t *sei; + size_t sei_size; +}; + +/* ------------------------------------------------------------------------- */ +/* Bitstream Buffer */ + +struct nv_bitstream { + void *ptr; + HANDLE event; +}; + +static inline bool nv_failed(struct nvenc_data *enc, NVENCSTATUS err, + const char *func, const char *call) +{ + if (err == NV_ENC_SUCCESS) + return false; + + error("%s: %s failed: %d (%s)", func, call, (int)err, + nv_error_name(err)); + return true; +} + +#define NV_FAILED(x) nv_failed(enc, x, __FUNCTION__, #x) + +static bool nv_bitstream_init(struct nvenc_data *enc, struct nv_bitstream *bs) +{ + NV_ENC_CREATE_BITSTREAM_BUFFER buf = {NV_ENC_CREATE_BITSTREAM_BUFFER_VER}; + NV_ENC_EVENT_PARAMS params = {NV_ENC_EVENT_PARAMS_VER}; + HANDLE event = NULL; + + if (NV_FAILED(nv.nvEncCreateBitstreamBuffer(enc->session, &buf))) { + return false; + } + + event = CreateEvent(NULL, true, true, NULL); + if (!event) { + error("%s: %s", __FUNCTION__, "Failed to create event"); + goto fail; + } + + params.completionEvent = event; + if (NV_FAILED(nv.nvEncRegisterAsyncEvent(enc->session, ¶ms))) { + goto fail; + } + + bs->ptr = buf.bitstreamBuffer; + bs->event = event; + return true; + +fail: + if (event) { + CloseHandle(event); + } + if (buf.bitstreamBuffer) { + nv.nvEncDestroyBitstreamBuffer(enc->session, + buf.bitstreamBuffer); + } + return false; +} + +static void nv_bitstream_free(struct nvenc_data *enc, struct nv_bitstream *bs) +{ + if (bs->ptr) { + nv.nvEncDestroyBitstreamBuffer(enc->session, bs->ptr); + + NV_ENC_EVENT_PARAMS params = {NV_ENC_EVENT_PARAMS_VER}; + params.completionEvent = bs->event; + nv.nvEncUnregisterAsyncEvent(enc->session, ¶ms); + CloseHandle(bs->event); + } +} + +/* ------------------------------------------------------------------------- */ +/* Texture Resource */ + +struct nv_texture { + void *res; + ID3D11Texture2D *tex; + void *mapped_res; +}; + +static bool nv_texture_init(struct nvenc_data *enc, struct nv_texture *nvtex) +{ + ID3D11Device *device = enc->device; + ID3D11Texture2D *tex; + HRESULT hr; + + D3D11_TEXTURE2D_DESC desc = {0}; + desc.Width = enc->cx; + desc.Height = enc->cy; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_NV12; + desc.SampleDesc.Count = 1; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + + hr = device->lpVtbl->CreateTexture2D(device, &desc, NULL, &tex); + if (FAILED(hr)) { + error_hr("Failed to create texture"); + return false; + } + + tex->lpVtbl->SetEvictionPriority(tex, DXGI_RESOURCE_PRIORITY_MAXIMUM); + + NV_ENC_REGISTER_RESOURCE res = {NV_ENC_REGISTER_RESOURCE_VER}; + res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; + res.resourceToRegister = tex; + res.width = enc->cx; + res.height = enc->cy; + res.bufferFormat = NV_ENC_BUFFER_FORMAT_NV12; + + if (NV_FAILED(nv.nvEncRegisterResource(enc->session, &res))) { + tex->lpVtbl->Release(tex); + return false; + } + + nvtex->res = res.registeredResource; + nvtex->tex = tex; + return true; +} + +static void nv_texture_free(struct nvenc_data *enc, struct nv_texture *nvtex) +{ + if (nvtex->res) { + if (nvtex->mapped_res) { + nv.nvEncUnmapInputResource(enc->session, + nvtex->mapped_res); + } + nv.nvEncUnregisterResource(enc->session, nvtex->res); + nvtex->tex->lpVtbl->Release(nvtex->tex); + } +} + +/* ------------------------------------------------------------------------- */ +/* Implementation */ + +static const char *nvenc_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC H.264 (new)"; +} + +static inline int nv_get_cap(struct nvenc_data *enc, NV_ENC_CAPS cap) +{ + if (!enc->session) + return 0; + + NV_ENC_CAPS_PARAM param = {NV_ENC_CAPS_PARAM_VER}; + int v; + + param.capsToQuery = cap; + nv.nvEncGetEncodeCaps(enc->session, NV_ENC_CODEC_H264_GUID, ¶m, &v); + return v; +} + +static bool nvenc_update(void *data, obs_data_t *settings) +{ + struct nvenc_data *enc = data; + + /* Only support reconfiguration of CBR bitrate */ + if (enc->can_change_bitrate) { + int bitrate = (int)obs_data_get_int(settings, "bitrate"); + + enc->config.rcParams.averageBitRate = bitrate * 1000; + enc->config.rcParams.maxBitRate = bitrate * 1000; + + NV_ENC_RECONFIGURE_PARAMS params = {0}; + params.version = NV_ENC_RECONFIGURE_PARAMS_VER; + params.reInitEncodeParams = enc->params; + + if (FAILED(nv.nvEncReconfigureEncoder(enc->session, ¶ms))) { + return false; + } + } + + return true; +} + +static HANDLE get_lib(struct nvenc_data *enc, const char *lib) +{ + HMODULE mod = GetModuleHandleA(lib); + if (mod) + return mod; + + mod = LoadLibraryA(lib); + if (!mod) + error("Failed to load %s", lib); + return mod; +} + +typedef HRESULT (WINAPI *CREATEDXGIFACTORY1PROC)(REFIID, void **); + +static bool init_d3d11(struct nvenc_data *enc, obs_data_t *settings) +{ + HMODULE dxgi = get_lib(enc, "DXGI.dll"); + HMODULE d3d11 = get_lib(enc, "D3D11.dll"); + CREATEDXGIFACTORY1PROC create_dxgi; + PFN_D3D11_CREATE_DEVICE create_device; + IDXGIFactory1 *factory; + IDXGIAdapter *adapter; + ID3D11Device *device; + ID3D11DeviceContext *context; + HRESULT hr; + + if (!dxgi || !d3d11) { + return false; + } + + create_dxgi = (CREATEDXGIFACTORY1PROC)GetProcAddress(dxgi, + "CreateDXGIFactory1"); + create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d11, + "D3D11CreateDevice"); + + if (!create_dxgi || !create_device) { + error("Failed to load D3D11/DXGI procedures"); + return false; + } + + hr = create_dxgi(&IID_IDXGIFactory1, &factory); + if (FAILED(hr)) { + error_hr("CreateDXGIFactory1 failed"); + return false; + } + + hr = factory->lpVtbl->EnumAdapters(factory, 0, &adapter); + factory->lpVtbl->Release(factory); + if (FAILED(hr)) { + error_hr("EnumAdapters failed"); + return false; + } + + hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, + NULL, 0, D3D11_SDK_VERSION, &device, NULL, &context); + adapter->lpVtbl->Release(adapter); + if (FAILED(hr)) { + error_hr("D3D11CreateDevice failed"); + return false; + } + + enc->device = device; + enc->context = context; + return true; +} + +static bool init_session(struct nvenc_data *enc) +{ + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = + {NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; + params.device = enc->device; + params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX; + params.apiVersion = NVENCAPI_VERSION; + + if (NV_FAILED(nv.nvEncOpenEncodeSessionEx(¶ms, &enc->session))) { + return false; + } + return true; +} + +static bool init_encoder(struct nvenc_data *enc, obs_data_t *settings) +{ + const char *rc = obs_data_get_string(settings, "rate_control"); + int bitrate = (int)obs_data_get_int(settings, "bitrate"); + int max_bitrate = (int)obs_data_get_int(settings, "max_bitrate"); + int cqp = (int)obs_data_get_int(settings, "cqp"); + int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); + const char *preset = obs_data_get_string(settings, "preset"); + const char *profile = obs_data_get_string(settings, "profile"); + bool psycho_aq = obs_data_get_bool(settings, "psycho_aq"); + bool lookahead = obs_data_get_bool(settings, "lookahead"); + int bf = (int)obs_data_get_int(settings, "bf"); + bool vbr = astrcmpi(rc, "VBR") == 0; + NVENCSTATUS err; + + video_t *video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + + enc->cx = voi->width; + enc->cy = voi->height; + + /* -------------------------- */ + /* get preset */ + + GUID nv_preset = NV_ENC_PRESET_DEFAULT_GUID; + bool twopass = false; + bool hp = false; + bool ll = false; + + if (astrcmpi(preset, "hq") == 0) { + nv_preset = NV_ENC_PRESET_HQ_GUID; + + } else if (astrcmpi(preset, "mq") == 0) { + nv_preset = NV_ENC_PRESET_HQ_GUID; + twopass = true; + + } else if (astrcmpi(preset, "hp") == 0) { + nv_preset = NV_ENC_PRESET_HP_GUID; + hp = true; + + } else if (astrcmpi(preset, "ll") == 0) { + nv_preset = NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID; + ll = true; + + } else if (astrcmpi(preset, "llhq") == 0) { + nv_preset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; + ll = true; + + } else if (astrcmpi(preset, "llhp") == 0) { + nv_preset = NV_ENC_PRESET_LOW_LATENCY_HP_GUID; + hp = true; + ll = true; + } + + if (astrcmpi(rc, "lossless") == 0) { + nv_preset = hp + ? NV_ENC_PRESET_LOSSLESS_HP_GUID + : NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID; + } + + /* -------------------------- */ + /* get preset default config */ + + NV_ENC_PRESET_CONFIG preset_config = + {NV_ENC_PRESET_CONFIG_VER, {NV_ENC_CONFIG_VER}}; + + err = nv.nvEncGetEncodePresetConfig(enc->session, + NV_ENC_CODEC_H264_GUID, nv_preset, &preset_config); + if (nv_failed(enc, err, __FUNCTION__, "nvEncGetEncodePresetConfig")) { + return false; + } + + /* -------------------------- */ + /* main configuration */ + + enc->config = preset_config.presetCfg; + + uint32_t gop_size = (keyint_sec) + ? keyint_sec * voi->fps_num / voi->fps_den + : 250; + + NV_ENC_INITIALIZE_PARAMS *params = &enc->params; + NV_ENC_CONFIG *config = &enc->config; + NV_ENC_CONFIG_H264 *h264_config = &config->encodeCodecConfig.h264Config; + NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui_params = + &h264_config->h264VUIParameters; + + memset(params, 0, sizeof(*params)); + params->version = NV_ENC_INITIALIZE_PARAMS_VER; + params->encodeGUID = NV_ENC_CODEC_H264_GUID; + params->presetGUID = nv_preset; + params->encodeWidth = voi->width; + params->encodeHeight = voi->height; + params->darWidth = voi->width; + params->darHeight = voi->height; + params->frameRateNum = voi->fps_num; + params->frameRateDen = voi->fps_den; + params->enableEncodeAsync = 1; + params->enablePTD = 1; + params->encodeConfig = &enc->config; + params->maxEncodeWidth = voi->width; + params->maxEncodeHeight = voi->height; + config->gopLength = gop_size; + config->frameIntervalP = 1 + bf; + h264_config->idrPeriod = gop_size; + vui_params->videoSignalTypePresentFlag = 1; + vui_params->videoFullRangeFlag = (voi->range == VIDEO_RANGE_FULL); + vui_params->colourDescriptionPresentFlag = 1; + vui_params->colourMatrix = (voi->colorspace == VIDEO_CS_709) ? 1 : 5; + vui_params->colourPrimaries = 1; + vui_params->transferCharacteristics = 1; + + enc->bframes = bf > 0; + + /* lookahead */ + if (lookahead && nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_LOOKAHEAD)) { + config->rcParams.lookaheadDepth = 8; + config->rcParams.enableLookahead = 1; + } + + /* psycho aq */ + if (nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ)) { + config->rcParams.enableAQ = psycho_aq; + config->rcParams.enableTemporalAQ = psycho_aq; + } + + /* -------------------------- */ + /* rate control */ + + enc->can_change_bitrate = + nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE); + + config->rcParams.rateControlMode = twopass + ? NV_ENC_PARAMS_RC_VBR_HQ + : NV_ENC_PARAMS_RC_VBR; + + if (astrcmpi(rc, "cqp") == 0 || astrcmpi(rc, "lossless") == 0) { + if (astrcmpi(rc, "lossless") == 0) + cqp = 0; + + config->rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + config->rcParams.constQP.qpInterP = cqp; + config->rcParams.constQP.qpInterB = cqp; + config->rcParams.constQP.qpIntra = cqp; + enc->can_change_bitrate = false; + + bitrate = 0; + max_bitrate = 0; + + } else if (astrcmpi(rc, "vbr") != 0) { /* CBR by default */ + h264_config->outputBufferingPeriodSEI = 1; + config->rcParams.rateControlMode = twopass + ? NV_ENC_PARAMS_RC_2_PASS_QUALITY + : NV_ENC_PARAMS_RC_CBR; + } + + h264_config->outputPictureTimingSEI = 1; + config->rcParams.averageBitRate = bitrate * 1000; + config->rcParams.maxBitRate = vbr ? max_bitrate * 1000 : bitrate * 1000; + + /* -------------------------- */ + /* profile */ + + if (astrcmpi(profile, "main") == 0) { + config->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID; + } else if (astrcmpi(profile, "baseline") == 0) { + config->profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID; + } else { + config->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID; + } + + /* -------------------------- */ + /* initialize */ + + if (NV_FAILED(nv.nvEncInitializeEncoder(enc->session, params))) { + return false; + } + + enc->buf_count = config->frameIntervalP + + config->rcParams.lookaheadDepth + EXTRA_BUFFERS; + enc->output_delay = enc->buf_count - 1; + + info("settings:\n" + "\trate_control: %s\n" + "\tbitrate: %d\n" + "\tcqp: %d\n" + "\tkeyint: %d\n" + "\tpreset: %s\n" + "\tprofile: %s\n" + "\twidth: %d\n" + "\theight: %d\n" + "\t2-pass: %s\n" + "\tb-frames: %d\n" + "\tlookahead: %s\n" + "\tpsycho_aq: %s\n", + rc, bitrate, cqp, gop_size, + preset, profile, + enc->cx, enc->cy, + twopass ? "true" : "false", + bf, + lookahead ? "true" : "false", + psycho_aq ? "true" : "false"); + + return true; +} + +static bool init_bitstreams(struct nvenc_data *enc) +{ + da_reserve(enc->bitstreams, enc->buf_count); + for (size_t i = 0; i < enc->buf_count; i++) { + struct nv_bitstream bitstream; + if (!nv_bitstream_init(enc, &bitstream)) { + return false; + } + + da_push_back(enc->bitstreams, &bitstream); + } + + return true; +} + +static bool init_textures(struct nvenc_data *enc) +{ + da_reserve(enc->bitstreams, enc->buf_count); + for (size_t i = 0; i < enc->buf_count; i++) { + struct nv_texture texture; + if (!nv_texture_init(enc, &texture)) { + return false; + } + + da_push_back(enc->textures, &texture); + } + + return true; +} + +static void nvenc_destroy(void *data); + +static void *nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + NV_ENCODE_API_FUNCTION_LIST init = {NV_ENCODE_API_FUNCTION_LIST_VER}; + struct nvenc_data *enc = bzalloc(sizeof(*enc)); + enc->encoder = encoder; + enc->first_packet = true; + + /* this encoder requires shared textures, this cannot be used on a + * gpu other than the one OBS is currently running on. */ + int gpu = (int)obs_data_get_int(settings, "gpu"); + if (gpu != 0) { + goto fail; + } + + if (!obs_nv12_tex_active()) { + goto fail; + } + if (!init_nvenc()) { + goto fail; + } + if (NV_FAILED(nv_create_instance(&init))) { + goto fail; + } + if (!init_d3d11(enc, settings)) { + goto fail; + } + if (!init_session(enc)) { + goto fail; + } + if (!init_encoder(enc, settings)) { + goto fail; + } + if (!init_bitstreams(enc)) { + goto fail; + } + if (!init_textures(enc)) { + goto fail; + } + + return enc; + +fail: + nvenc_destroy(enc); + return obs_encoder_create_rerouted(encoder, "ffmpeg_nvenc"); +} + +static bool get_encoded_packet(struct nvenc_data *enc, bool finalize); + +static void nvenc_destroy(void *data) +{ + struct nvenc_data *enc = data; + + if (enc->encode_started) { + size_t next_bitstream = enc->next_bitstream; + HANDLE next_event = enc->bitstreams.array[next_bitstream].event; + + NV_ENC_PIC_PARAMS params = {NV_ENC_PIC_PARAMS_VER}; + params.encodePicFlags = NV_ENC_PIC_FLAG_EOS; + params.completionEvent = next_event; + nv.nvEncEncodePicture(enc->session, ¶ms); + get_encoded_packet(enc, true); + } + for (size_t i = 0; i < enc->textures.num; i++) { + nv_texture_free(enc, &enc->textures.array[i]); + } + for (size_t i = 0; i < enc->bitstreams.num; i++) { + nv_bitstream_free(enc, &enc->bitstreams.array[i]); + } + if (enc->session) { + nv.nvEncDestroyEncoder(enc->session); + } + for (size_t i = 0; i < enc->input_textures.num; i++) { + ID3D11Texture2D *tex = enc->input_textures.array[i].tex; + IDXGIKeyedMutex *km = enc->input_textures.array[i].km; + tex->lpVtbl->Release(tex); + km->lpVtbl->Release(km); + } + if (enc->context) { + enc->context->lpVtbl->Release(enc->context); + } + if (enc->device) { + enc->device->lpVtbl->Release(enc->device); + } + + bfree(enc->header); + bfree(enc->sei); + circlebuf_free(&enc->dts_list); + da_free(enc->textures); + da_free(enc->bitstreams); + da_free(enc->input_textures); + da_free(enc->packet_data); + bfree(enc); +} + +static ID3D11Texture2D *get_tex_from_handle(struct nvenc_data *enc, + uint32_t handle, IDXGIKeyedMutex **km_out) +{ + ID3D11Device *device = enc->device; + IDXGIKeyedMutex *km; + ID3D11Texture2D *input_tex; + HRESULT hr; + + for (size_t i = 0; i < enc->input_textures.num; i++) { + struct handle_tex *ht = &enc->input_textures.array[i]; + if (ht->handle == handle) { + *km_out = ht->km; + return ht->tex; + } + } + + hr = device->lpVtbl->OpenSharedResource(device, + (HANDLE)(uintptr_t)handle, + &IID_ID3D11Texture2D, &input_tex); + if (FAILED(hr)) { + error_hr("OpenSharedResource failed"); + return NULL; + } + + hr = input_tex->lpVtbl->QueryInterface(input_tex, &IID_IDXGIKeyedMutex, + &km); + if (FAILED(hr)) { + error_hr("QueryInterface(IDXGIKeyedMutex) failed"); + input_tex->lpVtbl->Release(input_tex); + return NULL; + } + + input_tex->lpVtbl->SetEvictionPriority(input_tex, + DXGI_RESOURCE_PRIORITY_MAXIMUM); + + *km_out = km; + + struct handle_tex new_ht = {handle, input_tex, km}; + da_push_back(enc->input_textures, &new_ht); + return input_tex; +} + +static bool get_encoded_packet(struct nvenc_data *enc, bool finalize) +{ + void *s = enc->session; + + da_resize(enc->packet_data, 0); + + if (!enc->buffers_queued) + return true; + if (!finalize && enc->buffers_queued < enc->output_delay) + return true; + + size_t count = finalize ? enc->buffers_queued : 1; + + for (size_t i = 0; i < count; i++) { + size_t cur_bs_idx = enc->cur_bitstream; + struct nv_bitstream *bs = &enc->bitstreams.array[cur_bs_idx]; + struct nv_texture *nvtex = &enc->textures.array[cur_bs_idx]; + + /* ---------------- */ + + NV_ENC_LOCK_BITSTREAM lock = {NV_ENC_LOCK_BITSTREAM_VER}; + lock.outputBitstream = bs->ptr; + lock.doNotWait = false; + + if (NV_FAILED(nv.nvEncLockBitstream(s, &lock))) { + return false; + } + + if (enc->first_packet) { + uint8_t *new_packet; + size_t size; + + enc->first_packet = false; + obs_extract_avc_headers( + lock.bitstreamBufferPtr, + lock.bitstreamSizeInBytes, + &new_packet, &size, + &enc->header, &enc->header_size, + &enc->sei, &enc->sei_size); + + da_copy_array(enc->packet_data, new_packet, size); + bfree(new_packet); + } else { + da_copy_array(enc->packet_data, + lock.bitstreamBufferPtr, + lock.bitstreamSizeInBytes); + } + + enc->packet_pts = (int64_t)lock.outputTimeStamp; + enc->packet_keyframe = lock.pictureType == NV_ENC_PIC_TYPE_IDR; + + if (NV_FAILED(nv.nvEncUnlockBitstream(s, bs->ptr))) { + return false; + } + + /* ---------------- */ + + if (nvtex->mapped_res) { + NVENCSTATUS err; + err = nv.nvEncUnmapInputResource(s, nvtex->mapped_res); + if (nv_failed(enc, err, __FUNCTION__, "unmap")) { + return false; + } + nvtex->mapped_res = NULL; + } + + /* ---------------- */ + + if (++enc->cur_bitstream == enc->buf_count) + enc->cur_bitstream = 0; + + enc->buffers_queued--; + } + + return true; +} + +static bool nvenc_encode_tex(void *data, uint32_t handle, int64_t pts, + uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, bool *received_packet) +{ + struct nvenc_data *enc = data; + ID3D11Device *device = enc->device; + ID3D11DeviceContext *context = enc->context; + ID3D11Texture2D *input_tex; + ID3D11Texture2D *output_tex; + IDXGIKeyedMutex *km; + struct nv_texture *nvtex; + struct nv_bitstream *bs; + NVENCSTATUS err; + + if (handle == GS_INVALID_HANDLE) { + error("Encode failed: bad texture handle"); + *next_key = lock_key; + return false; + } + + bs = &enc->bitstreams.array[enc->next_bitstream]; + nvtex = &enc->textures.array[enc->next_bitstream]; + + input_tex = get_tex_from_handle(enc, handle, &km); + output_tex = nvtex->tex; + + if (!input_tex) { + *next_key = lock_key; + return false; + } + + circlebuf_push_back(&enc->dts_list, &pts, sizeof(pts)); + + /* ------------------------------------ */ + /* wait for output bitstream/tex */ + + WaitForSingleObject(bs->event, INFINITE); + + /* ------------------------------------ */ + /* copy to output tex */ + + km->lpVtbl->AcquireSync(km, lock_key, INFINITE); + + context->lpVtbl->CopyResource(context, + (ID3D11Resource *)output_tex, + (ID3D11Resource *)input_tex); + + km->lpVtbl->ReleaseSync(km, *next_key); + + /* ------------------------------------ */ + /* map output tex so nvenc can use it */ + + NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; + map.registeredResource = nvtex->res; + if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) { + return false; + } + + nvtex->mapped_res = map.mappedResource; + + /* ------------------------------------ */ + /* do actual encode call */ + + NV_ENC_PIC_PARAMS params = {0}; + params.version = NV_ENC_PIC_PARAMS_VER; + params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; + params.inputBuffer = nvtex->mapped_res; + params.bufferFmt = NV_ENC_BUFFER_FORMAT_NV12; + params.inputTimeStamp = (uint64_t)pts; + params.inputWidth = enc->cx; + params.inputHeight = enc->cy; + params.outputBitstream = bs->ptr; + params.completionEvent = bs->event; + + err = nv.nvEncEncodePicture(enc->session, ¶ms); + if (err != NV_ENC_SUCCESS && err != NV_ENC_ERR_NEED_MORE_INPUT) { + nv_failed(enc, err, __FUNCTION__, "nvEncEncodePicture"); + return false; + } + + enc->encode_started = true; + enc->buffers_queued++; + + if (++enc->next_bitstream == enc->buf_count) { + enc->next_bitstream = 0; + } + + /* ------------------------------------ */ + /* check for encoded packet and parse */ + + if (!get_encoded_packet(enc, false)) { + return false; + } + + /* ------------------------------------ */ + /* output encoded packet */ + + if (enc->packet_data.num) { + int64_t dts; + circlebuf_pop_front(&enc->dts_list, &dts, sizeof(dts)); + + /* subtract bframe delay from dts */ + if (enc->bframes) + dts -= packet->timebase_num; + + *received_packet = true; + packet->data = enc->packet_data.array; + packet->size = enc->packet_data.num; + packet->type = OBS_ENCODER_VIDEO; + packet->pts = enc->packet_pts; + packet->dts = dts; + packet->keyframe = enc->packet_keyframe; + } else { + *received_packet = false; + } + + return true; +} + +extern void nvenc_defaults(obs_data_t *settings); +extern obs_properties_t *nvenc_properties(void *unused); + +static bool nvenc_extra_data(void *data, uint8_t **header, size_t *size) +{ + struct nvenc_data *enc = data; + + if (!enc->header) { + return false; + } + + *header = enc->header; + *size = enc->header_size; + return true; +} + +static bool nvenc_sei_data(void *data, uint8_t **sei, size_t *size) +{ + struct nvenc_data *enc = data; + + if (!enc->sei) { + return false; + } + + *sei = enc->sei; + *size = enc->sei_size; + return true; +} + +struct obs_encoder_info nvenc_info = { + .id = "jim_nvenc", + .codec = "h264", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE, + .get_name = nvenc_get_name, + .create = nvenc_create, + .destroy = nvenc_destroy, + .update = nvenc_update, + .encode_texture = nvenc_encode_tex, + .get_defaults = nvenc_defaults, + .get_properties = nvenc_properties, + .get_extra_data = nvenc_extra_data, + .get_sei_data = nvenc_sei_data, +}; diff --git a/plugins/obs-ffmpeg/jim-nvenc.h b/plugins/obs-ffmpeg/jim-nvenc.h new file mode 100644 index 0000000..c45ecdd --- /dev/null +++ b/plugins/obs-ffmpeg/jim-nvenc.h @@ -0,0 +1,14 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include "nvEncodeAPI.h" + +typedef NVENCSTATUS (NVENCAPI *NV_CREATE_INSTANCE_FUNC)(NV_ENCODE_API_FUNCTION_LIST*); + +extern const char *nv_error_name(NVENCSTATUS err); +extern NV_ENCODE_API_FUNCTION_LIST nv; +extern NV_CREATE_INSTANCE_FUNC nv_create_instance; +extern bool init_nvenc(void); diff --git a/plugins/obs-ffmpeg/nvEncodeAPI.h b/plugins/obs-ffmpeg/nvEncodeAPI.h index e662880..df13b2f 100644 --- a/plugins/obs-ffmpeg/nvEncodeAPI.h +++ b/plugins/obs-ffmpeg/nvEncodeAPI.h @@ -1,7 +1,7 @@ /* * This copyright notice applies to this header file only: * - * Copyright (c) 2010-2017 NVIDIA Corporation + * Copyright (c) 2010-2018 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -30,7 +30,7 @@ * NVIDIA GPUs - beginning with the Kepler generation - contain a hardware-based encoder * (referred to as NVENC) which provides fully-accelerated hardware-based video encoding. * NvEncodeAPI provides the interface for NVIDIA video encoder (NVENC). - * \date 2011-2017 + * \date 2011-2018 * This file contains the interface constants, structure definitions and function prototypes. */ @@ -67,17 +67,13 @@ extern "C" { * @{ */ -#if defined(_WIN32) || defined(__CYGWIN__) -#define NVENCAPI __stdcall -#else -#define NVENCAPI -#endif - #ifdef _WIN32 +#define NVENCAPI __stdcall typedef RECT NVENC_RECT; #else +#define NVENCAPI // ========================================================================================= -#if !defined(GUID) && !defined(GUID_DEFINED) +#ifndef GUID /*! * \struct GUID * Abstracts the GUID structure for non-windows platforms. @@ -114,7 +110,7 @@ typedef void* NV_ENC_OUTPUT_PTR; /**< NVENCODE API output buffer*/ typedef void* NV_ENC_REGISTERED_PTR; /**< A Resource that has been registered with NVENCODE API*/ #define NVENCAPI_MAJOR_VERSION 8 -#define NVENCAPI_MINOR_VERSION 0 +#define NVENCAPI_MINOR_VERSION 1 #define NVENCAPI_VERSION (NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24)) @@ -262,6 +258,30 @@ typedef enum _NV_ENC_PARAMS_RC_MODE NV_ENC_PARAMS_RC_VBR_HQ = 0x20 /**< VBR, high quality (slower) */ } NV_ENC_PARAMS_RC_MODE; +/** + * Emphasis Levels + */ +typedef enum _NV_ENC_EMPHASIS_MAP_LEVEL +{ + NV_ENC_EMPHASIS_MAP_LEVEL_0 = 0x0, /**< Emphasis Map Level 0, for zero Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_1 = 0x1, /**< Emphasis Map Level 1, for very low Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_2 = 0x2, /**< Emphasis Map Level 2, for low Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_3 = 0x3, /**< Emphasis Map Level 3, for medium Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_4 = 0x4, /**< Emphasis Map Level 4, for high Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_5 = 0x5 /**< Emphasis Map Level 5, for very high Delta QP value */ +} NV_ENC_EMPHASIS_MAP_LEVEL; + +/** + * QP MAP MODE + */ +typedef enum _NV_ENC_QP_MAP_MODE +{ + NV_ENC_QP_MAP_DISABLED = 0x0, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap have no effect. */ + NV_ENC_QP_MAP_EMPHASIS = 0x1, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as Empasis level. Currently this is only supported for H264 */ + NV_ENC_QP_MAP_DELTA = 0x2, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as QP delta map. */ + NV_ENC_QP_MAP = 0x3, /**< Currently This is not supported. Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as QP value. */ +} NV_ENC_QP_MAP_MODE; + #define NV_ENC_PARAMS_RC_VBR_MINQP (NV_ENC_PARAMS_RC_MODE)0x4 /**< Deprecated */ #define NV_ENC_PARAMS_RC_2_PASS_QUALITY NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ /**< Deprecated */ #define NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP NV_ENC_PARAMS_RC_CBR_HQ /**< Deprecated */ @@ -585,6 +605,15 @@ typedef enum _NV_ENC_MEMORY_HEAP NV_ENC_MEMORY_HEAP_SYSMEM_UNCACHED = 3 /**< Memory heap is in uncached system memory */ } NV_ENC_MEMORY_HEAP; +/** + * B-frame used as reference modes + */ +typedef enum _NV_ENC_BFRAME_REF_MODE +{ + NV_ENC_BFRAME_REF_MODE_DISABLED = 0x0, /**< B frame is not used for reference */ + NV_ENC_BFRAME_REF_MODE_EACH = 0x1, /**< Each B-frame will be used for reference. currently not supported */ + NV_ENC_BFRAME_REF_MODE_MIDDLE = 0x2, /**< Only(Number of B-frame)/2 th B-frame will be used for reference */ +} NV_ENC_BFRAME_REF_MODE; /** * H.264 entropy coding modes. @@ -955,7 +984,34 @@ typedef enum _NV_ENC_CAPS */ NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION, + /** + * On managed (vGPU) platforms (Windows only), this API, in conjunction with other GRID Management APIs, can be used + * to estimate the residual capacity of the hardware encoder on the GPU as a percentage of the total available encoder capacity. + * This API can be called at any time; i.e. during the encode session or before opening the encode session. + * If the available encoder capacity is returned as zero, applications may choose to switch to software encoding + * and continue to call this API (e.g. polling once per second) until capacity becomes available. + * + * On baremetal (non-virtualized GPU) and linux platforms, this API always returns 100. + */ + NV_ENC_CAPS_DYNAMIC_QUERY_ENCODER_CAPACITY, + + /** + * Indicates B as refererence support. + * \n 0 : B as reference is not supported. + * \n 1 : each B-Frame as reference is supported. + * \n 2 : only Middle B-frame as reference is supported. + */ + NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, + + /** + * Indicates HW support for Emphasis Level Map based delta QP computation. + * \n 0 : Emphasis Level Map based delta QP not supported. + * \n 1 : Emphasis Level Map based delta QP is supported. + */ + NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP, + + /** * Reserved - Not to be used by clients. */ NV_ENC_CAPS_EXPOSED_COUNT @@ -1100,7 +1156,7 @@ typedef struct _NV_ENC_QP uint32_t enableMaxQP :1; /**< [in]: Set this to 1 if maximum QP used for rate control. */ uint32_t enableInitialRCQP :1; /**< [in]: Set this to 1 if user suppplied initial QP is used for rate control. */ uint32_t enableAQ :1; /**< [in]: Set this to 1 to enable adaptive quantization (Spatial). */ - uint32_t enableExtQPDeltaMap :1; /**< [in]: Set this to 1 to enable additional QP modifier for each MB supplied by client though signed byte array pointed to by NV_ENC_PIC_PARAMS::qpDeltaMap (Not Supported when AQ(Spatial/Temporal) is enabled) */ + uint32_t reservedBitField1 :1; /**< [in]: Reserved bitfields and must be set to 0. */ uint32_t enableLookahead :1; /**< [in]: Set this to 1 to enable lookahead with depth (if lookahead is enabled, input frames must remain available to the encoder until encode completion) */ uint32_t disableIadapt :1; /**< [in]: Set this to 1 to disable adaptive I-frame insertion at scene cuts (only has an effect when lookahead is enabled) */ uint32_t disableBadapt :1; /**< [in]: Set this to 1 to disable adaptive B-frame decision (only has an effect when lookahead is enabled) */ @@ -1118,7 +1174,25 @@ typedef struct _NV_ENC_QP uint8_t targetQuality; /**< [in]: Target CQ (Constant Quality) level for VBR mode (range 0-51 with 0-automatic) */ uint8_t targetQualityLSB; /**< [in]: Fractional part of target quality (as 8.8 fixed point format) */ uint16_t lookaheadDepth; /**< [in]: Maximum depth of lookahead with range 0-32 (only used if enableLookahead=1) */ - uint32_t reserved[9]; + uint32_t reserved1; + NV_ENC_QP_MAP_MODE qpMapMode; /**< [in]: This flag is used to interpret values in array pecified by NV_ENC_PIC_PARAMS::qpDeltaMap. + Set this to NV_ENC_QP_MAP_EMPHASIS to treat values specified by NV_ENC_PIC_PARAMS::qpDeltaMap as Emphasis level Map. + Emphasis Level can be assigned any value specified in enum NV_ENC_EMPHASIS_MAP_LEVEL. + Emphasis Level Map is used to specify regions to be encoded at varying levels of quality. + The hardware encoder adjusts the quantization within the image as per the provided emphasis map, + by adjusting the quantization parameter (QP) assigned to each macroblock. This adjustment is commonly called “Delta QP”. + The adjustment depends on the absolute QP decided by the rate control algorithm, and is applied after the rate control has decided each macroblock’s QP. + Since the Delta QP overrides rate control, enabling emphasis level map may violate bitrate and VBV buffersize constraints. + Emphasis level map is useful in situations when client has a priori knowledge of the image complexity (e.g. via use of NVFBC's Classification feature) and encoding those high-complexity areas at higher quality (lower QP) is important, even at the possible cost of violating bitrate/VBV buffersize constraints + This feature is not supported when AQ( Spatial/Temporal) is enabled. + This feature is only supported for H264 codec currently. + + Set this to NV_ENC_QP_MAP_DELTA to treat values specified by NV_ENC_PIC_PARAMS::qpDeltaMap as QPDelta. This specify QP modifier to be applied on top of the QP chosen by rate control + + Set this to NV_ENC_QP_MAP_DISABLED to ignore NV_ENC_PIC_PARAMS::qpDeltaMap values. In this case, qpDeltaMap should be set to NULL. + + Other values are reserved for future use.*/ + uint32_t reserved[7]; } NV_ENC_RC_PARAMS; /** macro for constructing the version field of ::_NV_ENC_RC_PARAMS */ @@ -1235,7 +1309,7 @@ typedef struct _NV_ENC_CONFIG_H264 is recommended to use a large DPB size so that the encoder can keep old reference frames which can be used if recent frames are invalidated. */ uint32_t sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in which the picture is divided into slices - sliceMode = 0 MB based slices, sliceMode = 1 Byte based slices, sliceMode = 2 MB row based slices, sliceMode = 3, numSlices in Picture + sliceMode = 0 MB based slices, sliceMode = 1 Byte based slices, sliceMode = 2 MB row based slices, sliceMode = 3 numSlices in Picture. When forceIntraRefreshWithFrameCnt is set it will have priority over sliceMode setting When sliceMode == 0 and sliceModeData == 0 whole picture will be coded with one slice */ uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For: @@ -1254,11 +1328,11 @@ typedef struct _NV_ENC_CONFIG_H264 uint32_t chromaFormatIDC; /**< [in]: Specifies the chroma format. Should be set to 1 for yuv420 input, 3 for yuv444 input. Check support for YUV444 encoding using ::NV_ENC_CAPS_SUPPORT_YUV444_ENCODE caps.*/ uint32_t maxTemporalLayers; /**< [in]: Specifies the max temporal layer used for hierarchical coding. */ - uint32_t reserved1[270]; /**< [in]: Reserved and must be set to 0 */ + NV_ENC_BFRAME_REF_MODE useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode. Check support for useBFramesAsRef mode using ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/ + uint32_t reserved1[269]; /**< [in]: Reserved and must be set to 0 */ void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ } NV_ENC_CONFIG_H264; - /** * \struct _NV_ENC_CONFIG_HEVC * HEVC encoder configuration parameters to be set during initialization. @@ -1312,7 +1386,7 @@ typedef struct _NV_ENC_CONFIG_HEVC Set to 1 to use "LTR Trust" mode of LTR operation. Clients are discouraged to use "LTR Trust" mode as this mode may be deprecated in future releases. Set to 0 when using "LTR Per Picture" mode of LTR operation. */ - uint32_t reserved1[217]; /**< [in]: Reserved and must be set to 0.*/ + uint32_t reserved1[217]; /**< [in]: Reserved and must be set to 0.*/ void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ } NV_ENC_CONFIG_HEVC; @@ -1382,7 +1456,7 @@ typedef struct _NV_ENC_CONFIG } NV_ENC_CONFIG; /** macro for constructing the version field of ::_NV_ENC_CONFIG */ -#define NV_ENC_CONFIG_VER (NVENCAPI_STRUCT_VERSION(6) | ( 1<<31 )) +#define NV_ENC_CONFIG_VER (NVENCAPI_STRUCT_VERSION(7) | ( 1<<31 )) /** @@ -1436,7 +1510,21 @@ typedef struct _NV_ENC_INITIALIZE_PARAMS typedef struct _NV_ENC_RECONFIGURE_PARAMS { uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_RECONFIGURE_PARAMS_VER. */ - NV_ENC_INITIALIZE_PARAMS reInitEncodeParams; /**< [in]: Encoder session re-initialization parameters. */ + NV_ENC_INITIALIZE_PARAMS reInitEncodeParams; /**< [in]: Encoder session re-initialization parameters. + If reInitEncodeParams.encodeConfig is NULL and + reInitEncodeParams.presetGUID is the same as the preset + GUID specified on the call to NvEncInitializeEncoder(), + EncodeAPI will continue to use the existing encode + configuration. + If reInitEncodeParams.encodeConfig is NULL and + reInitEncodeParams.presetGUID is different from the preset + GUID specified on the call to NvEncInitializeEncoder(), + EncodeAPI will try to use the default configuration for + the preset specified by reInitEncodeParams.presetGUID. + In this case, reconfiguration may fail if the new + configuration is incompatible with the existing + configuration (e.g. the new configuration results in + a change in the GOP structure). */ uint32_t resetEncoder :1; /**< [in]: This resets the rate control states and other internal encoder states. This should be used only with an IDR frame. If NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1, encoder will force the frame type to IDR */ uint32_t forceIDR :1; /**< [in]: Encode the current picture as an IDR picture. This flag is only valid when Picture type decision is taken by the Encoder @@ -1560,7 +1648,6 @@ typedef struct _NV_ENC_PIC_PARAMS_HEVC void* reserved3[61]; /**< [in]: Reserved and must be set to NULL. */ } NV_ENC_PIC_PARAMS_HEVC; - /** * Codec specific per-picture encoding parameters. */ @@ -1599,7 +1686,11 @@ typedef struct _NV_ENC_PIC_PARAMS + 4*meHintCountsPerBlock[Lx].numCandsPerBlk8x8. For frames using bidirectional ME , the total number of candidates for single macroblock is sum of total number of candidates per MB for each direction (L0 and L1) */ uint32_t reserved1[6]; /**< [in]: Reserved and must be set to 0 */ void* reserved2[2]; /**< [in]: Reserved and must be set to NULL */ - int8_t *qpDeltaMap; /**< [in]: Specifies the pointer to signed byte array containing QP delta value per MB in raster scan order in the current picture. This QP modifier is applied on top of the QP chosen by rate control. */ + int8_t *qpDeltaMap; /**< [in]: Specifies the pointer to signed byte array containing value per MB in raster scan order for the current picture, which will be Interperated depending on NV_ENC_RC_PARAMS::qpMapMode. + If NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_DELTA , This specify QP modifier to be applied on top of the QP chosen by rate control. + If NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_EMPHASIS, it specifies emphasis level map per MB. This level value along with QP chosen by rate control is used to compute the QP modifier, + which in turn is applied on top of QP chosen by rate control. + If NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_DISABLED value in qpDeltaMap will be ignored.*/ uint32_t qpDeltaMapSize; /**< [in]: Specifies the size in bytes of qpDeltaMap surface allocated by client and pointed to by NV_ENC_PIC_PARAMS::qpDeltaMap. Surface (array) should be picWidthInMbs * picHeightInMbs */ uint32_t reservedBitFields; /**< [in]: Reserved bitfields and must be set to 0 */ uint16_t meHintRefPicDist[2]; /**< [in]: Specifies temporal distance for reference picture (NVENC_EXTERNAL_ME_HINT::refidx = 0) used during external ME with NV_ENC_INITALIZE_PARAMS::enablePTD = 1 . meHintRefPicDist[0] is for L0 hints and meHintRefPicDist[1] is for L1 hints. diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index 9b65276..2683f8e 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -26,6 +26,10 @@ #include #include "ffmpeg-mux/ffmpeg-mux.h" +#ifdef _WIN32 +#include "util/windows/win-version.h" +#endif + #include #define do_log(level, format, ...) \ @@ -109,13 +113,9 @@ static void *ffmpeg_mux_create(obs_data_t *settings, obs_output_t *output) } #ifdef _WIN32 -#ifdef _WIN64 -#define FFMPEG_MUX "ffmpeg-mux64.exe" +#define FFMPEG_MUX "obs-ffmpeg-mux.exe" #else -#define FFMPEG_MUX "ffmpeg-mux32.exe" -#endif -#else -#define FFMPEG_MUX "ffmpeg-mux" +#define FFMPEG_MUX "obs-ffmpeg-mux" #endif static inline bool capturing(struct ffmpeg_muxer *stream) @@ -237,7 +237,7 @@ static void build_command_line(struct ffmpeg_muxer *stream, struct dstr *cmd, num_tracks++; } - dstr_init_move_array(cmd, obs_module_file(FFMPEG_MUX)); + dstr_init_move_array(cmd, os_get_executable_path_ptr(FFMPEG_MUX)); dstr_insert_ch(cmd, 0, '\"'); dstr_cat(cmd, "\" \""); @@ -290,6 +290,16 @@ static bool ffmpeg_mux_start(void *data) struct dstr error_message; dstr_init_copy(&error_message, obs_module_text("UnableToWritePath")); +#ifdef _WIN32 + // special warning for Windows 10 users about Defender + struct win_version_info ver; + get_win_ver(&ver); + if (ver.major >= 10) { + dstr_cat(&error_message, "\n\n"); + dstr_cat(&error_message, + obs_module_text("WarnWindowsDefender")); + } +#endif dstr_replace(&error_message, "%1", path); obs_output_set_last_error(stream->output, error_message.array); @@ -321,7 +331,7 @@ static bool ffmpeg_mux_start(void *data) return true; } -static int deactivate(struct ffmpeg_muxer *stream) +static int deactivate(struct ffmpeg_muxer *stream, int code) { int ret = -1; @@ -335,8 +345,11 @@ static int deactivate(struct ffmpeg_muxer *stream) info("Output of file '%s' stopped", stream->path.array); } - if (stopping(stream)) + if (code) { + obs_output_signal_stop(stream->output, code); + } else if (stopping(stream)) { obs_output_end_data_capture(stream->output); + } os_atomic_set_bool(&stream->stopping, false); return ret; @@ -355,9 +368,23 @@ static void ffmpeg_mux_stop(void *data, uint64_t ts) static void signal_failure(struct ffmpeg_muxer *stream) { - int ret = deactivate(stream); + char error[1024]; + int ret; int code; + size_t len; + + len = os_process_pipe_read_err(stream->pipe, (uint8_t *)error, + sizeof(error) - 1); + + if (len > 0) { + error[len] = 0; + warn ("ffmpeg-mux: %s", error); + obs_output_set_last_error (stream->output, error); + } + + ret = deactivate(stream, 0); + switch (ret) { case FFM_UNSUPPORTED: code = OBS_OUTPUT_UNSUPPORTED; break; default: code = OBS_OUTPUT_ERROR; @@ -455,6 +482,12 @@ static void ffmpeg_mux_data(void *data, struct encoder_packet *packet) if (!active(stream)) return; + /* encoder failure */ + if (!packet) { + deactivate(stream, OBS_OUTPUT_ENCODE_ERROR); + return; + } + if (!stream->sent_headers) { if (!send_headers(stream)) return; @@ -464,7 +497,7 @@ static void ffmpeg_mux_data(void *data, struct encoder_packet *packet) if (stopping(stream)) { if (packet->sys_dts_usec >= stream->stop_ts) { - deactivate(stream); + deactivate(stream, 0); return; } } @@ -779,10 +812,13 @@ static void replay_buffer_save(struct ffmpeg_muxer *stream) replay_buffer_mux_thread, stream) == 0; } -static void deactivate_replay_buffer(struct ffmpeg_muxer *stream) +static void deactivate_replay_buffer(struct ffmpeg_muxer *stream, int code) { - if (stopping(stream)) + if (code) { + obs_output_signal_stop(stream->output, code); + } else if (stopping(stream)) { obs_output_end_data_capture(stream->output); + } os_atomic_set_bool(&stream->active, false); os_atomic_set_bool(&stream->sent_headers, false); @@ -798,9 +834,15 @@ static void replay_buffer_data(void *data, struct encoder_packet *packet) if (!active(stream)) return; + /* encoder failure */ + if (!packet) { + deactivate_replay_buffer(stream, OBS_OUTPUT_ENCODE_ERROR); + return; + } + if (stopping(stream)) { if (packet->sys_dts_usec >= stream->stop_ts) { - deactivate_replay_buffer(stream); + deactivate_replay_buffer(stream, 0); return; } } diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index 16cd32e..469dce4 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -60,7 +60,7 @@ struct nvenc_encoder { static const char *nvenc_getname(void *unused) { UNUSED_PARAMETER(unused); - return "NVENC H.264"; + return "NVIDIA NVENC H.264"; } static inline bool valid_format(enum video_format format) @@ -134,8 +134,6 @@ static bool nvenc_update(void *data, obs_data_t *settings) int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); const char *preset = obs_data_get_string(settings, "preset"); const char *profile = obs_data_get_string(settings, "profile"); - const char *level = obs_data_get_string(settings, "level"); - bool twopass = obs_data_get_bool(settings, "2pass"); int gpu = (int)obs_data_get_int(settings, "gpu"); bool cbr_override = obs_data_get_bool(settings, "cbr"); int bf = (int)obs_data_get_int(settings, "bf"); @@ -158,6 +156,13 @@ static bool nvenc_update(void *data, obs_data_t *settings) info.colorspace = voi->colorspace; info.range = voi->range; + bool twopass = false; + + if (astrcmpi(preset, "mq") == 0) { + twopass = true; + preset = "hq"; + } + nvenc_video_info(enc, &info); av_opt_set_int(enc->context->priv_data, "cbr", false, 0); av_opt_set(enc->context->priv_data, "profile", profile, 0); @@ -185,7 +190,7 @@ static bool nvenc_update(void *data, obs_data_t *settings) } - av_opt_set(enc->context->priv_data, "level", level, 0); + av_opt_set(enc->context->priv_data, "level", "auto", 0); av_opt_set_int(enc->context->priv_data, "2pass", twopass, 0); av_opt_set_int(enc->context->priv_data, "gpu", gpu, 0); @@ -216,14 +221,13 @@ static bool nvenc_update(void *data, obs_data_t *settings) "\tkeyint: %d\n" "\tpreset: %s\n" "\tprofile: %s\n" - "\tlevel: %s\n" "\twidth: %d\n" "\theight: %d\n" "\t2-pass: %s\n" "\tb-frames: %d\n" "\tGPU: %d\n", rc, bitrate, cqp, enc->context->gop_size, - preset, profile, level, + preset, profile, enc->context->width, enc->context->height, twopass ? "true" : "false", enc->context->max_b_frames, @@ -390,16 +394,16 @@ static bool nvenc_encode(void *data, struct encoder_frame *frame, return true; } -static void nvenc_defaults(obs_data_t *settings) +void nvenc_defaults(obs_data_t *settings) { obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "max_bitrate", 5000); obs_data_set_default_int(settings, "keyint_sec", 0); - obs_data_set_default_int(settings, "cqp", 23); + obs_data_set_default_int(settings, "cqp", 20); obs_data_set_default_string(settings, "rate_control", "CBR"); - obs_data_set_default_string(settings, "preset", "default"); - obs_data_set_default_string(settings, "profile", "main"); - obs_data_set_default_string(settings, "level", "auto"); - obs_data_set_default_bool(settings, "2pass", true); + obs_data_set_default_string(settings, "preset", "hq"); + obs_data_set_default_string(settings, "profile", "high"); + obs_data_set_default_bool(settings, "psycho_aq", true); obs_data_set_default_int(settings, "gpu", 0); obs_data_set_default_int(settings, "bf", 2); } @@ -409,11 +413,14 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, { const char *rc = obs_data_get_string(settings, "rate_control"); bool cqp = astrcmpi(rc, "CQP") == 0; + bool vbr = astrcmpi(rc, "VBR") == 0; bool lossless = astrcmpi(rc, "lossless") == 0; size_t count; p = obs_properties_get(ppts, "bitrate"); obs_property_set_visible(p, !cqp && !lossless); + p = obs_properties_get(ppts, "max_bitrate"); + obs_property_set_visible(p, vbr); p = obs_properties_get(ppts, "cqp"); obs_property_set_visible(p, cqp); @@ -421,17 +428,15 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, count = obs_property_list_item_count(p); for (size_t i = 0; i < count; i++) { - bool compatible = (i == 0 || i == 2); + bool compatible = (i == 0 || i == 3); obs_property_list_item_disable(p, i, lossless && !compatible); } return true; } -static obs_properties_t *nvenc_properties(void *unused) +obs_properties_t *nvenc_properties_internal(bool ffmpeg) { - UNUSED_PARAMETER(unused); - obs_properties_t *props = obs_properties_create(); obs_property_t *p; @@ -439,17 +444,22 @@ static obs_properties_t *nvenc_properties(void *unused) obs_module_text("RateControl"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, "CBR", "CBR"); - obs_property_list_add_string(p, "VBR", "VBR"); obs_property_list_add_string(p, "CQP", "CQP"); + obs_property_list_add_string(p, "VBR", "VBR"); obs_property_list_add_string(p, obs_module_text("Lossless"), "lossless"); obs_property_set_modified_callback(p, rate_control_modified); - obs_properties_add_int(props, "bitrate", + p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), 50, 300000, 50); + obs_property_int_set_suffix(p, " Kbps"); + p = obs_properties_add_int(props, "max_bitrate", + obs_module_text("MaxBitrate"), 50, 300000, 50); + obs_property_int_set_suffix(p, " Kbps"); - obs_properties_add_int(props, "cqp", "CQP", 0, 50, 1); + obs_properties_add_int(props, "cqp", obs_module_text("NVENC.CQLevel"), + 1, 30, 1); obs_properties_add_int(props, "keyint_sec", obs_module_text("KeyframeIntervalSec"), 0, 10, 1); @@ -460,10 +470,10 @@ static obs_properties_t *nvenc_properties(void *unused) #define add_preset(val) \ obs_property_list_add_string(p, obs_module_text("NVENC.Preset." val), \ val) - add_preset("default"); + add_preset("mq"); add_preset("hq"); + add_preset("default"); add_preset("hp"); - add_preset("bd"); add_preset("ll"); add_preset("llhq"); add_preset("llhp"); @@ -477,38 +487,20 @@ static obs_properties_t *nvenc_properties(void *unused) add_profile("high"); add_profile("main"); add_profile("baseline"); - add_profile("high444p"); - - p = obs_properties_add_list(props, "level", - obs_module_text("NVENC.Level"), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_STRING); - add_profile("auto"); - add_profile("1" ); - add_profile("1.0" ); - add_profile("1b" ); - add_profile("1.0b"); - add_profile("1.1" ); - add_profile("1.2" ); - add_profile("1.3" ); - add_profile("2" ); - add_profile("2.0" ); - add_profile("2.1" ); - add_profile("2.2" ); - add_profile("3" ); - add_profile("3.0" ); - add_profile("3.1" ); - add_profile("3.2" ); - add_profile("4" ); - add_profile("4.0" ); - add_profile("4.1" ); - add_profile("4.2" ); - add_profile("5" ); - add_profile("5.0" ); - add_profile("5.1" ); #undef add_profile - obs_properties_add_bool(props, "2pass", - obs_module_text("NVENC.Use2Pass")); + if (!ffmpeg) { + p = obs_properties_add_bool(props, "lookahead", + obs_module_text("NVENC.LookAhead")); + obs_property_set_long_description(p, + obs_module_text("NVENC.LookAhead.ToolTip")); + + p = obs_properties_add_bool(props, "psycho_aq", + obs_module_text("NVENC.PsychoVisualTuning")); + obs_property_set_long_description(p, + obs_module_text("NVENC.PsychoVisualTuning.ToolTip")); + } + obs_properties_add_int(props, "gpu", obs_module_text("GPU"), 0, 8, 1); obs_properties_add_int(props, "bf", obs_module_text("BFrames"), @@ -517,6 +509,18 @@ static obs_properties_t *nvenc_properties(void *unused) return props; } +obs_properties_t *nvenc_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(false); +} + +obs_properties_t *nvenc_properties_ffmpeg(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(true); +} + static bool nvenc_extra_data(void *data, uint8_t **extra_data, size_t *size) { struct nvenc_encoder *enc = data; @@ -544,7 +548,7 @@ struct obs_encoder_info nvenc_encoder_info = { .destroy = nvenc_destroy, .encode = nvenc_encode, .get_defaults = nvenc_defaults, - .get_properties = nvenc_properties, + .get_properties = nvenc_properties_ffmpeg, .get_extra_data = nvenc_extra_data, .get_sei_data = nvenc_sei_data, .get_video_info = nvenc_video_info diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 9acdef2..7f687ee 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -45,6 +45,8 @@ struct ffmpeg_cfg { int audio_encoder_id; const char *video_settings; const char *audio_settings; + int audio_mix_count; + int audio_tracks; enum AVPixelFormat format; enum AVColorRange color_range; enum AVColorSpace color_space; @@ -56,7 +58,7 @@ struct ffmpeg_cfg { struct ffmpeg_data { AVStream *video; - AVStream *audio; + AVStream **audio_streams; AVCodec *acodec; AVCodec *vcodec; AVFormatContext *output; @@ -68,18 +70,24 @@ struct ffmpeg_data { uint64_t start_timestamp; - int64_t total_samples; + int64_t total_samples[MAX_AUDIO_MIXES]; uint32_t audio_samplerate; enum audio_format audio_format; size_t audio_planes; size_t audio_size; - struct circlebuf excess_frames[MAX_AV_PLANES]; - uint8_t *samples[MAX_AV_PLANES]; - AVFrame *aframe; + int num_audio_streams; + + /* audio_tracks is a bitmask storing the indices of the mixes */ + int audio_tracks; + struct circlebuf excess_frames[MAX_AUDIO_MIXES][MAX_AV_PLANES]; + uint8_t *samples[MAX_AUDIO_MIXES][MAX_AV_PLANES]; + AVFrame *aframe[MAX_AUDIO_MIXES]; struct ffmpeg_cfg config; bool initialized; + + char *last_error; }; struct ffmpeg_output { @@ -108,6 +116,30 @@ struct ffmpeg_output { /* ------------------------------------------------------------------------- */ +static void ffmpeg_output_set_last_error(struct ffmpeg_data *data, + const char *error) +{ + if (data->last_error) + bfree(data->last_error); + + data->last_error = bstrdup(error); +} + +void ffmpeg_log_error(int log_level, struct ffmpeg_data *data, + const char *format, ...) +{ + va_list args; + char out[4096]; + + va_start(args, format); + vsnprintf(out, sizeof(out), format, args); + va_end(args); + + ffmpeg_output_set_last_error(data, out); + + blog(log_level, "%s", out); +} + static bool new_stream(struct ffmpeg_data *data, AVStream **stream, AVCodec **codec, enum AVCodecID id, const char *name) { @@ -116,14 +148,14 @@ static bool new_stream(struct ffmpeg_data *data, AVStream **stream, avcodec_find_encoder(id); if (!*codec) { - blog(LOG_WARNING, "Couldn't find encoder '%s'", + ffmpeg_log_error(LOG_WARNING, data, "Couldn't find encoder '%s'", avcodec_get_name(id)); return false; } *stream = avformat_new_stream(data->output, *codec); if (!*stream) { - blog(LOG_WARNING, "Couldn't create stream for encoder '%s'", + ffmpeg_log_error(LOG_WARNING, data, "Couldn't create stream for encoder '%s'", avcodec_get_name(id)); return false; } @@ -180,14 +212,14 @@ static bool open_video_codec(struct ffmpeg_data *data) ret = avcodec_open2(context, data->vcodec, NULL); if (ret < 0) { - blog(LOG_WARNING, "Failed to open video codec: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to open video codec: %s", av_err2str(ret)); return false; } data->vframe = av_frame_alloc(); if (!data->vframe) { - blog(LOG_WARNING, "Failed to allocate video frame"); + ffmpeg_log_error(LOG_WARNING, data, "Failed to allocate video frame"); return false; } @@ -199,7 +231,7 @@ static bool open_video_codec(struct ffmpeg_data *data) ret = av_frame_get_buffer(data->vframe, base_get_alignment()); if (ret < 0) { - blog(LOG_WARNING, "Failed to allocate vframe: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to allocate vframe: %s", av_err2str(ret)); return false; } @@ -217,7 +249,7 @@ static bool init_swscale(struct ffmpeg_data *data, AVCodecContext *context) SWS_BICUBIC, NULL, NULL, NULL); if (!data->swscale) { - blog(LOG_WARNING, "Could not initialize swscale"); + ffmpeg_log_error(LOG_WARNING, data, "Could not initialize swscale"); return false; } @@ -231,7 +263,7 @@ static bool create_video_stream(struct ffmpeg_data *data) struct obs_video_info ovi; if (!obs_get_video_info(&ovi)) { - blog(LOG_WARNING, "No active video"); + ffmpeg_log_error(LOG_WARNING, data, "No active video"); return false; } @@ -273,9 +305,9 @@ static bool create_video_stream(struct ffmpeg_data *data) return true; } -static bool open_audio_codec(struct ffmpeg_data *data) +static bool open_audio_codec(struct ffmpeg_data *data, int idx) { - AVCodecContext *context = data->audio->codec; + AVCodecContext *context = data->audio_streams[idx]->codec; char **opts = strlist_split(data->config.audio_settings, ' ', false); int ret; @@ -284,32 +316,32 @@ static bool open_audio_codec(struct ffmpeg_data *data) strlist_free(opts); } - data->aframe = av_frame_alloc(); - if (!data->aframe) { - blog(LOG_WARNING, "Failed to allocate audio frame"); + data->aframe[idx] = av_frame_alloc(); + if (!data->aframe[idx]) { + ffmpeg_log_error(LOG_WARNING, data, "Failed to allocate audio frame"); return false; } - data->aframe->format = context->sample_fmt; - data->aframe->channels = context->channels; - data->aframe->channel_layout = context->channel_layout; - data->aframe->sample_rate = context->sample_rate; + data->aframe[idx]->format = context->sample_fmt; + data->aframe[idx]->channels = context->channels; + data->aframe[idx]->channel_layout = context->channel_layout; + data->aframe[idx]->sample_rate = context->sample_rate; context->strict_std_compliance = -2; ret = avcodec_open2(context, data->acodec, NULL); if (ret < 0) { - blog(LOG_WARNING, "Failed to open audio codec: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to open audio codec: %s", av_err2str(ret)); return false; } data->frame_size = context->frame_size ? context->frame_size : 1024; - ret = av_samples_alloc(data->samples, NULL, context->channels, + ret = av_samples_alloc(data->samples[idx], NULL, context->channels, data->frame_size, context->sample_fmt, 0); if (ret < 0) { - blog(LOG_WARNING, "Failed to create audio buffer: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to create audio buffer: %s", av_err2str(ret)); return false; } @@ -317,27 +349,29 @@ static bool open_audio_codec(struct ffmpeg_data *data) return true; } -static bool create_audio_stream(struct ffmpeg_data *data) +static bool create_audio_stream(struct ffmpeg_data *data, int idx) { AVCodecContext *context; + AVStream *stream; struct obs_audio_info aoi; if (!obs_get_audio_info(&aoi)) { - blog(LOG_WARNING, "No active audio"); + ffmpeg_log_error(LOG_WARNING, data, "No active audio"); return false; } - if (!new_stream(data, &data->audio, &data->acodec, + if (!new_stream(data, &stream, &data->acodec, data->output->oformat->audio_codec, data->config.audio_encoder)) return false; - context = data->audio->codec; - context->bit_rate = data->config.audio_bitrate * 1000; - context->time_base = (AVRational){ 1, aoi.samples_per_sec }; - context->channels = get_audio_channels(aoi.speakers); - context->sample_rate = aoi.samples_per_sec; - context->channel_layout = + data->audio_streams[idx] = stream; + context = data->audio_streams[idx]->codec; + context->bit_rate = data->config.audio_bitrate * 1000; + context->time_base = (AVRational){ 1, aoi.samples_per_sec }; + context->channels = get_audio_channels(aoi.speakers); + context->sample_rate = aoi.samples_per_sec; + context->channel_layout = av_get_default_channel_layout(context->channels); //AVlib default channel layout for 5 channels is 5.0 ; fix for 4.1 @@ -347,7 +381,7 @@ static bool create_audio_stream(struct ffmpeg_data *data) context->sample_fmt = data->acodec->sample_fmts ? data->acodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; - data->audio->time_base = context->time_base; + data->audio_streams[idx]->time_base = context->time_base; data->audio_samplerate = aoi.samples_per_sec; data->audio_format = convert_ffmpeg_sample_format(context->sample_fmt); @@ -357,7 +391,7 @@ static bool create_audio_stream(struct ffmpeg_data *data) if (data->output->oformat->flags & AVFMT_GLOBALHEADER) context->flags |= CODEC_FLAG_GLOBAL_H; - return open_audio_codec(data); + return open_audio_codec(data, idx); } static inline bool init_streams(struct ffmpeg_data *data) @@ -368,9 +402,14 @@ static inline bool init_streams(struct ffmpeg_data *data) if (!create_video_stream(data)) return false; - if (format->audio_codec != AV_CODEC_ID_NONE) - if (!create_audio_stream(data)) - return false; + if (format->audio_codec != AV_CODEC_ID_NONE && data->num_audio_streams) { + data->audio_streams = calloc(1, + data->num_audio_streams * sizeof(void*)); + for (int i = 0; i < data->num_audio_streams; i++) { + if (!create_audio_stream(data, i)) + return false; + } + } return true; } @@ -383,7 +422,7 @@ static inline bool open_output_file(struct ffmpeg_data *data) AVDictionary *dict = NULL; if ((ret = av_dict_parse_string(&dict, data->config.muxer_settings, "=", " ", 0))) { - blog(LOG_WARNING, "Failed to parse muxer settings: %s\n%s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to parse muxer settings: %s\n%s", av_err2str(ret), data->config.muxer_settings); av_dict_free(&dict); @@ -406,8 +445,9 @@ static inline bool open_output_file(struct ffmpeg_data *data) ret = avio_open2(&data->output->pb, data->config.url, AVIO_FLAG_WRITE, NULL, &dict); if (ret < 0) { - blog(LOG_WARNING, "Couldn't open '%s', %s", - data->config.url, av_err2str(ret)); + ffmpeg_log_error(LOG_WARNING, data, + "Couldn't open '%s', %s", data->config.url, + av_err2str(ret)); av_dict_free(&dict); return false; } @@ -419,7 +459,7 @@ static inline bool open_output_file(struct ffmpeg_data *data) ret = avformat_write_header(data->output, &dict); if (ret < 0) { - blog(LOG_WARNING, "Error opening '%s': %s", + ffmpeg_log_error(LOG_WARNING, data, "Error opening '%s': %s", data->config.url, av_err2str(ret)); return false; } @@ -457,12 +497,17 @@ static void close_video(struct ffmpeg_data *data) static void close_audio(struct ffmpeg_data *data) { - for (size_t i = 0; i < MAX_AV_PLANES; i++) - circlebuf_free(&data->excess_frames[i]); + for (int idx = 0; idx < data->num_audio_streams; idx++) { + for (size_t i = 0; i < MAX_AV_PLANES; i++) + circlebuf_free(&data->excess_frames[idx][i]); - av_freep(&data->samples[0]); - avcodec_close(data->audio->codec); - av_frame_free(&data->aframe); + if (data->samples[idx][0]) + av_freep(&data->samples[idx][0]); + if (data->audio_streams[idx]) + avcodec_close(data->audio_streams[idx]->codec); + if (data->aframe[idx]) + av_frame_free(&data->aframe[idx]); + } } static void ffmpeg_data_free(struct ffmpeg_data *data) @@ -472,8 +517,11 @@ static void ffmpeg_data_free(struct ffmpeg_data *data) if (data->video) close_video(data); - if (data->audio) + if (data->audio_streams) { close_audio(data); + free(data->audio_streams); + data->audio_streams = NULL; + } if (data->output) { if ((data->output->oformat->flags & AVFMT_NOFILE) == 0) @@ -482,6 +530,9 @@ static void ffmpeg_data_free(struct ffmpeg_data *data) avformat_free_context(data->output); } + if (data->last_error) + bfree(data->last_error); + memset(data, 0, sizeof(struct ffmpeg_data)); } @@ -528,7 +579,8 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data, memset(data, 0, sizeof(struct ffmpeg_data)); data->config = *config; - + data->num_audio_streams = config->audio_mix_count; + data->audio_tracks = config->audio_tracks; if (!config->url || !*config->url) return false; @@ -543,19 +595,27 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data, is_rtmp ? NULL : data->config.format_mime_type); if (output_format == NULL) { - blog(LOG_WARNING, "Couldn't find matching output format with " - " parameters: name=%s, url=%s, mime=%s", - safe_str(is_rtmp ? - "flv" : data->config.format_name), - safe_str(data->config.url), - safe_str(is_rtmp ? - NULL : data->config.format_mime_type)); + ffmpeg_log_error(LOG_WARNING, data, + "Couldn't find matching output format with " + "parameters: name=%s, url=%s, mime=%s", + safe_str(is_rtmp ? + "flv" : data->config.format_name), + safe_str(data->config.url), + safe_str(is_rtmp ? + NULL : data->config.format_mime_type)); + goto fail; } avformat_alloc_output_context2(&data->output, output_format, NULL, NULL); + if (!data->output) { + ffmpeg_log_error(LOG_WARNING, data, + "Couldn't create avformat context"); + goto fail; + } + if (is_rtmp) { data->output->oformat->video_codec = AV_CODEC_ID_H264; data->output->oformat->audio_codec = AV_CODEC_ID_AAC; @@ -564,11 +624,6 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data, set_encoder_ids(data); } - if (!data->output) { - blog(LOG_WARNING, "Couldn't create avformat context"); - goto fail; - } - if (!init_streams(data)) goto fail; if (!open_output_file(data)) @@ -581,7 +636,6 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data, fail: blog(LOG_WARNING, "ffmpeg_data_init failed"); - ffmpeg_data_free(data); return false; } @@ -736,6 +790,7 @@ static void receive_video(void *param, struct video_data *frame) if (ret < 0) { blog(LOG_WARNING, "receive_video: Error encoding " "video: %s", av_err2str(ret)); + //FIXME: stop the encode with an error return; } @@ -761,12 +816,13 @@ static void receive_video(void *param, struct video_data *frame) if (ret != 0) { blog(LOG_WARNING, "receive_video: Error writing video: %s", av_err2str(ret)); + //FIXME: stop the encode with an error } data->total_frames++; } -static void encode_audio(struct ffmpeg_output *output, +static void encode_audio(struct ffmpeg_output *output, int idx, struct AVCodecContext *context, size_t block_size) { struct ffmpeg_data *data = &output->ff_data; @@ -775,24 +831,25 @@ static void encode_audio(struct ffmpeg_output *output, int ret, got_packet; size_t total_size = data->frame_size * block_size * context->channels; - data->aframe->nb_samples = data->frame_size; - data->aframe->pts = av_rescale_q(data->total_samples, + data->aframe[idx]->nb_samples = data->frame_size; + data->aframe[idx]->pts = av_rescale_q(data->total_samples[idx], (AVRational){1, context->sample_rate}, context->time_base); - ret = avcodec_fill_audio_frame(data->aframe, context->channels, - context->sample_fmt, data->samples[0], + ret = avcodec_fill_audio_frame(data->aframe[idx], context->channels, + context->sample_fmt, data->samples[idx][0], (int)total_size, 1); if (ret < 0) { blog(LOG_WARNING, "encode_audio: avcodec_fill_audio_frame " "failed: %s", av_err2str(ret)); + //FIXME: stop the encode with an error return; } - data->total_samples += data->frame_size; + data->total_samples[idx] += data->frame_size; #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) - ret = avcodec_send_frame(context, data->aframe); + ret = avcodec_send_frame(context, data->aframe[idx]); if (ret == 0) ret = avcodec_receive_packet(context, &packet); @@ -801,23 +858,26 @@ static void encode_audio(struct ffmpeg_output *output, if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) ret = 0; #else - ret = avcodec_encode_audio2(context, &packet, data->aframe, + ret = avcodec_encode_audio2(context, &packet, data->aframe[idx], &got_packet); #endif if (ret < 0) { blog(LOG_WARNING, "encode_audio: Error encoding audio: %s", av_err2str(ret)); + //FIXME: stop the encode with an error return; } if (!got_packet) return; - packet.pts = rescale_ts(packet.pts, context, data->audio->time_base); - packet.dts = rescale_ts(packet.dts, context, data->audio->time_base); + packet.pts = rescale_ts(packet.pts, context, + data->audio_streams[idx]->time_base); + packet.dts = rescale_ts(packet.dts, context, + data->audio_streams[idx]->time_base); packet.duration = (int)av_rescale_q(packet.duration, context->time_base, - data->audio->time_base); - packet.stream_index = data->audio->index; + data->audio_streams[idx]->time_base); + packet.stream_index = data->audio_streams[idx]->index; pthread_mutex_lock(&output->write_mutex); da_push_back(output->packets, &packet); @@ -853,18 +913,38 @@ static bool prepare_audio(struct ffmpeg_data *data, return true; } -static void receive_audio(void *param, struct audio_data *frame) +/* Given a bitmask for the selected tracks and the mix index, + * this returns the stream index which will be passed to the muxer. */ +static int get_track_order(int track_config, size_t mix_index) +{ + int position = 0; + for (size_t i = 0; i < mix_index; i++) { + if (track_config & 1 << i) + position++; + } + return position; +} + +static void receive_audio(void *param, size_t mix_idx, struct audio_data *frame) { struct ffmpeg_output *output = param; struct ffmpeg_data *data = &output->ff_data; size_t frame_size_bytes; struct audio_data in; + int track_order; // codec doesn't support audio or none configured - if (!data->audio) + if (!data->audio_streams) return; - AVCodecContext *context = data->audio->codec; + /* check that the track was selected */ + if ((data->audio_tracks & (1 << mix_idx)) == 0) + return; + + /* get track order (first selected, etc ...) */ + track_order = get_track_order(data->audio_tracks, mix_idx); + + AVCodecContext *context = data->audio_streams[track_order]->codec; if (!data->start_timestamp) return; @@ -877,15 +957,16 @@ static void receive_audio(void *param, struct audio_data *frame) frame_size_bytes = (size_t)data->frame_size * data->audio_size; for (size_t i = 0; i < data->audio_planes; i++) - circlebuf_push_back(&data->excess_frames[i], in.data[i], - in.frames * data->audio_size); + circlebuf_push_back(&data->excess_frames[track_order][i], + in.data[i], in.frames * data->audio_size); - while (data->excess_frames[0].size >= frame_size_bytes) { + while (data->excess_frames[track_order][0].size >= frame_size_bytes) { for (size_t i = 0; i < data->audio_planes; i++) - circlebuf_pop_front(&data->excess_frames[i], - data->samples[i], frame_size_bytes); + circlebuf_pop_front(&data->excess_frames[track_order][i], + data->samples[track_order][i], + frame_size_bytes); - encode_audio(output, context, data->audio_size); + encode_audio(output, track_order, context, data->audio_size); } } @@ -901,7 +982,7 @@ static uint64_t get_packet_sys_dts(struct ffmpeg_output *output, time_base = data->video->time_base; start_ts = output->video_start_ts; } else { - time_base = data->audio->time_base; + time_base = data->audio_streams[0]->time_base; start_ts = output->audio_start_ts; } @@ -944,8 +1025,9 @@ static int process_packet(struct ffmpeg_output *output) ret = av_interleaved_write_frame(output->ff_data.output, &packet); if (ret < 0) { av_free_packet(&packet); - blog(LOG_WARNING, "receive_audio: Error writing packet: %s", - av_err2str(ret)); + ffmpeg_log_error(LOG_WARNING, &output->ff_data, + "receive_audio: Error writing packet: %s", + av_err2str(ret)); return ret; } @@ -990,6 +1072,18 @@ static inline const char *get_string_or_null(obs_data_t *settings, return value; } +static int get_audio_mix_count(int audio_mix_mask) +{ + int mix_count = 0; + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + if ((audio_mix_mask & (1 << i)) != 0) { + mix_count++; + } + } + + return mix_count; +} + static bool try_connect(struct ffmpeg_output *output) { video_t *video = obs_output_video(output->output); @@ -1025,6 +1119,8 @@ static bool try_connect(struct ffmpeg_output *output) config.height = (int)obs_output_get_height(output->output); config.format = obs_to_ffmpeg_video_format( video_output_get_format(video)); + config.audio_tracks = (int)obs_output_get_mixers(output->output); + config.audio_mix_count = get_audio_mix_count(config.audio_tracks); if (format_is_yuv(voi->format)) { config.color_range = voi->range == VIDEO_RANGE_FULL ? @@ -1049,8 +1145,14 @@ static bool try_connect(struct ffmpeg_output *output) success = ffmpeg_data_init(&output->ff_data, &config); obs_data_release(settings); - if (!success) + if (!success) { + if (output->ff_data.last_error) { + obs_output_set_last_error(output->output, + output->ff_data.last_error); + } + ffmpeg_data_free(&output->ff_data); return false; + } struct audio_convert_info aci = { .format = output->ff_data.audio_format @@ -1063,8 +1165,9 @@ static bool try_connect(struct ffmpeg_output *output) ret = pthread_create(&output->write_thread, NULL, write_thread, output); if (ret != 0) { - blog(LOG_WARNING, "ffmpeg_output_start: failed to create write " - "thread."); + ffmpeg_log_error(LOG_WARNING, &output->ff_data, + "ffmpeg_output_start: failed to create write " + "thread."); ffmpeg_output_full_stop(output); return false; } @@ -1157,13 +1260,15 @@ static uint64_t ffmpeg_output_total_bytes(void *data) struct obs_output_info ffmpeg_output = { .id = "ffmpeg_output", - .flags = OBS_OUTPUT_AUDIO | OBS_OUTPUT_VIDEO, + .flags = OBS_OUTPUT_AUDIO | + OBS_OUTPUT_VIDEO | + OBS_OUTPUT_MULTI_TRACK, .get_name = ffmpeg_output_getname, .create = ffmpeg_output_create, .destroy = ffmpeg_output_destroy, .start = ffmpeg_output_start, .stop = ffmpeg_output_stop, .raw_video = receive_video, - .raw_audio = receive_audio, + .raw_audio2 = 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 730ad6d..b1fe369 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-source.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-source.c @@ -155,6 +155,10 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data) obs_properties_add_bool(props, "restart_on_activate", obs_module_text("RestartWhenActivated")); + obs_properties_add_int_slider(props, "buffering_mb", + obs_module_text("BufferingMB"), + 1, 16, 1); + obs_properties_add_text(props, "input", obs_module_text("Input"), OBS_TEXT_DEFAULT); diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c new file mode 100644 index 0000000..28b4d6f --- /dev/null +++ b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c @@ -0,0 +1,539 @@ +/****************************************************************************** + Copyright (C) 2016 by Hugh Bailey + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 27, 100) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "obs-ffmpeg-formats.h" + +#define do_log(level, format, ...) \ + blog(level, "[FFMPEG VAAPI encoder: '%s'] " format, \ + obs_encoder_get_name(enc->encoder), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) + +struct vaapi_encoder { + obs_encoder_t *encoder; + + AVBufferRef *vadevice_ref; + AVBufferRef *vaframes_ref; + + AVCodec * vaapi; + AVCodecContext *context; + + AVFrame *vframe; + + DARRAY(uint8_t) buffer; + + uint8_t *header; + size_t header_size; + + uint8_t *sei; + size_t sei_size; + + int height; + bool first_packet; + bool initialized; +}; + +static const char *vaapi_getname(void *unused) +{ + UNUSED_PARAMETER(unused); + return "FFMPEG VAAPI"; +} + +static inline bool valid_format(enum video_format format) +{ + return format == VIDEO_FORMAT_NV12; +} + +static void vaapi_video_info(void *data, struct video_scale_info *info) +{ + struct vaapi_encoder *enc = data; + enum video_format pref_format; + + pref_format = obs_encoder_get_preferred_video_format(enc->encoder); + + if (!valid_format(pref_format)) { + pref_format = valid_format(info->format) ? info->format + : VIDEO_FORMAT_NV12; + } + + info->format = pref_format; +} + +static bool vaapi_init_codec(struct vaapi_encoder *enc, const char *path) +{ + int ret; + + ret = av_hwdevice_ctx_create(&enc->vadevice_ref, AV_HWDEVICE_TYPE_VAAPI, + path, NULL, 0); + if (ret < 0) { + warn("Failed to create VAAPI device context: %s", + av_err2str(ret)); + return false; + } + + enc->vaframes_ref = av_hwframe_ctx_alloc(enc->vadevice_ref); + if (!enc->vaframes_ref) { + warn("Failed to alloc HW frames context"); + return false; + } + + AVHWFramesContext *frames_ctx = + (AVHWFramesContext *)enc->vaframes_ref->data; + frames_ctx->format = AV_PIX_FMT_VAAPI; + frames_ctx->sw_format = AV_PIX_FMT_NV12; + frames_ctx->width = enc->context->width; + frames_ctx->height = enc->context->height; + frames_ctx->initial_pool_size = 20; + + ret = av_hwframe_ctx_init(enc->vaframes_ref); + if (ret < 0) { + warn("Failed to init HW frames context: %s", av_err2str(ret)); + return false; + } + + /* 2. Create software frame and picture */ + enc->vframe = av_frame_alloc(); + if (!enc->vframe) { + warn("Failed to allocate video frame"); + return false; + } + + enc->vframe->format = enc->context->pix_fmt; + enc->vframe->width = enc->context->width; + enc->vframe->height = enc->context->height; + enc->vframe->colorspace = enc->context->colorspace; + enc->vframe->color_range = enc->context->color_range; + + ret = av_frame_get_buffer(enc->vframe, base_get_alignment()); + if (ret < 0) { + warn("Failed to allocate vframe: %s", av_err2str(ret)); + return false; + } + + /* 3. set up codec */ + enc->context->pix_fmt = AV_PIX_FMT_VAAPI; + enc->context->hw_frames_ctx = av_buffer_ref(enc->vaframes_ref); + + ret = avcodec_open2(enc->context, enc->vaapi, NULL); + if (ret < 0) { + warn("Failed to open VAAPI codec: %s", av_err2str(ret)); + return false; + } + + enc->initialized = true; + return true; +} + +static bool vaapi_update(void *data, obs_data_t *settings) +{ + struct vaapi_encoder *enc = data; + + const char *device = obs_data_get_string(settings, "vaapi_device"); + + int profile = (int)obs_data_get_int(settings, "profile"); + int bf = (int)obs_data_get_int(settings, "bf"); + + int level = (int)obs_data_get_int(settings, "level"); + int bitrate = (int)obs_data_get_int(settings, "bitrate"); + int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); + + int qp = (int)obs_data_get_int(settings, "qp"); + int quality = (int)obs_data_get_int(settings, "quality"); + + av_opt_set_int(enc->context->priv_data, "qp", qp, 0); + av_opt_set_int(enc->context->priv_data, "quality", quality, 0); + + video_t * video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + struct video_scale_info info; + + info.format = voi->format; + info.colorspace = voi->colorspace; + info.range = voi->range; + + vaapi_video_info(enc, &info); + + enc->context->profile = profile; + enc->context->max_b_frames = bf; + enc->context->level = level; + enc->context->bit_rate = bitrate * 1000; + enc->context->rc_max_rate = bitrate * 1000; + + enc->context->width = obs_encoder_get_width(enc->encoder); + enc->context->height = obs_encoder_get_height(enc->encoder); + + enc->context->time_base = (AVRational){voi->fps_den, voi->fps_num}; + enc->context->pix_fmt = obs_to_ffmpeg_video_format(info.format); + enc->context->colorspace = info.colorspace == VIDEO_CS_709 + ? AVCOL_SPC_BT709 + : AVCOL_SPC_BT470BG; + enc->context->color_range = info.range == VIDEO_RANGE_FULL + ? AVCOL_RANGE_JPEG + : AVCOL_RANGE_MPEG; + + if (keyint_sec > 0) { + enc->context->gop_size = + keyint_sec * voi->fps_num / voi->fps_den; + } else { + enc->context->gop_size = 120; + } + + enc->height = enc->context->height; + + info("settings:\n" + "\tdevice: %s\n" + "\tqp: %d\n" + "\tquality: %d\n" + "\tprofile: %d\n" + "\tlevel: %d\n" + "\tbitrate: %d\n" + "\tkeyint: %d\n" + "\twidth: %d\n" + "\theight: %d\n" + "\tb-frames: %d\n", + device, qp, quality, profile, level, bitrate, + enc->context->gop_size, enc->context->width, + enc->context->height, enc->context->max_b_frames); + + return vaapi_init_codec(enc, device); +} + +static void vaapi_destroy(void *data) +{ + struct vaapi_encoder *enc = data; + + if (enc->initialized) { + AVPacket pkt = {0}; + int r_pkt = 1; + + while (r_pkt) { +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + if (avcodec_receive_packet(enc->context, &pkt) < 0) + break; +#else + if (avcodec_encode_video2(enc->context, &pkt, NULL, + &r_pkt) < 0) + break; +#endif + + if (r_pkt) + av_packet_unref(&pkt); + } + } + + avcodec_close(enc->context); + av_frame_unref(enc->vframe); + av_frame_free(&enc->vframe); + av_buffer_unref(&enc->vaframes_ref); + av_buffer_unref(&enc->vadevice_ref); + da_free(enc->buffer); + bfree(enc->header); + bfree(enc->sei); + + bfree(enc); +} + +static void *vaapi_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + struct vaapi_encoder *enc; + avcodec_register_all(); + + enc = bzalloc(sizeof(*enc)); + enc->encoder = encoder; + + int vaapi_codec = (int)obs_data_get_int(settings, "vaapi_codec"); + + if (vaapi_codec == AV_CODEC_ID_H264) { + enc->vaapi = avcodec_find_encoder_by_name("h264_vaapi"); + } + + enc->first_packet = true; + + blog(LOG_INFO, "---------------------------------"); + + if (!enc->vaapi) { + warn("Couldn't find encoder"); + goto fail; + } + + enc->context = avcodec_alloc_context3(enc->vaapi); + if (!enc->context) { + warn("Failed to create codec context"); + goto fail; + } + + if (!vaapi_update(enc, settings)) + goto fail; + + return enc; + +fail: + vaapi_destroy(enc); + return NULL; +} + +static inline void copy_data(AVFrame *pic, const struct encoder_frame *frame, + int height, enum AVPixelFormat format) +{ + int h_chroma_shift, v_chroma_shift; + av_pix_fmt_get_chroma_sub_sample( + format, &h_chroma_shift, &v_chroma_shift); + for (int plane = 0; plane < MAX_AV_PLANES; plane++) { + if (!frame->data[plane]) + continue; + + int frame_rowsize = (int)frame->linesize[plane]; + int pic_rowsize = pic->linesize[plane]; + int bytes = frame_rowsize < pic_rowsize ? frame_rowsize + : pic_rowsize; + int plane_height = height >> (plane ? v_chroma_shift : 0); + + for (int y = 0; y < plane_height; y++) { + int pos_frame = y * frame_rowsize; + int pos_pic = y * pic_rowsize; + + memcpy(pic->data[plane] + pos_pic, + frame->data[plane] + pos_frame, bytes); + } + } +} + +static bool vaapi_encode(void *data, struct encoder_frame *frame, + struct encoder_packet *packet, bool *received_packet) +{ + struct vaapi_encoder *enc = data; + AVFrame * hwframe = NULL; + AVPacket av_pkt; + int got_packet; + int ret; + + hwframe = av_frame_alloc(); + if (!hwframe) { + warn("vaapi_encode: failed to allocate hw frame"); + return false; + } + + ret = av_hwframe_get_buffer(enc->vaframes_ref, hwframe, 0); + if (ret < 0) { + warn("vaapi_encode: failed to get buffer for hw frame: %s", + av_err2str(ret)); + goto fail; + } + + copy_data(enc->vframe, frame, enc->height, enc->context->pix_fmt); + + enc->vframe->pts = frame->pts; + hwframe->pts = frame->pts; + hwframe->width = enc->vframe->width; + hwframe->height = enc->vframe->height; + + ret = av_hwframe_transfer_data(hwframe, enc->vframe, 0); + if (ret < 0) { + warn("vaapi_encode: failed to upload hw frame: %s", + av_err2str(ret)); + goto fail; + } + + ret = av_frame_copy_props(hwframe, enc->vframe); + if (ret < 0) { + warn("vaapi_encode: failed to copy props to hw frame: %s", + av_err2str(ret)); + goto fail; + } + + av_init_packet(&av_pkt); + +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101) + ret = avcodec_send_frame(enc->context, hwframe); + if (ret == 0) + ret = avcodec_receive_packet(enc->context, &av_pkt); + + got_packet = (ret == 0); + + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + ret = 0; +#else + ret = avcodec_encode_video2( + enc->context, &av_pkt, hwframe, &got_packet); +#endif + if (ret < 0) { + warn("vaapi_encode: Error encoding: %s", av_err2str(ret)); + goto fail; + } + + if (got_packet && av_pkt.size) { + if (enc->first_packet) { + uint8_t *new_packet; + size_t size; + + enc->first_packet = false; + obs_extract_avc_headers(av_pkt.data, av_pkt.size, + &new_packet, &size, &enc->header, + &enc->header_size, &enc->sei, + &enc->sei_size); + + da_copy_array(enc->buffer, new_packet, size); + bfree(new_packet); + } else { + da_copy_array(enc->buffer, av_pkt.data, av_pkt.size); + } + + packet->pts = av_pkt.pts; + packet->dts = av_pkt.dts; + packet->data = enc->buffer.array; + packet->size = enc->buffer.num; + packet->type = OBS_ENCODER_VIDEO; + packet->keyframe = obs_avc_keyframe(packet->data, packet->size); + *received_packet = true; + } else { + *received_packet = false; + } + + av_packet_unref(&av_pkt); + av_frame_free(&hwframe); + return true; + +fail: + av_frame_free(&hwframe); + return false; +} + +static void set_visible(obs_properties_t *ppts, const char *name, bool visible) +{ + obs_property_t *p = obs_properties_get(ppts, name); + obs_property_set_visible(p, visible); +} + +static void vaapi_defaults(obs_data_t *settings) +{ + obs_data_set_default_string( + settings, "vaapi_device", "/dev/dri/renderD128"); + obs_data_set_default_int(settings, "vaapi_codec", AV_CODEC_ID_H264); + obs_data_set_default_int(settings, "profile", + FF_PROFILE_H264_CONSTRAINED_BASELINE); + obs_data_set_default_int(settings, "level", 40); + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "keyint_sec", 0); + obs_data_set_default_int(settings, "bf", 0); + obs_data_set_default_int(settings, "qp", 20); + obs_data_set_default_int(settings, "quality", 0); + obs_data_set_default_int(settings, "rendermode", 0); +} + +static obs_properties_t *vaapi_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + + obs_properties_t *props = obs_properties_create(); + obs_property_t * list; + + list = obs_properties_add_list(props, "vaapi_device", "VAAPI Device", + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + char path[32] = "/dev/dri/renderD1"; + for (int i = 28;; i++) { + sprintf(path, "/dev/dri/renderD1%d", i); + if (access(path, F_OK) == 0) { + char card[128] = "Card: "; + sprintf(card, "Card%d: %s", i - 28, path); + obs_property_list_add_string(list, card, path); + } else { + break; + } + } + + list = obs_properties_add_list(props, "vaapi_codec", "VAAPI Codec", + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int(list, "H.264 (default)", AV_CODEC_ID_H264); + + list = obs_properties_add_list(props, "level", "Level", + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, "480p30 (3.0)", 30); + obs_property_list_add_int(list, "720p30/480p60 (3.1)", 31); + obs_property_list_add_int( + list, "Compatibility mode (4.0 default)", 40); + obs_property_list_add_int(list, "720p60/1080p30 (4.1)", 41); + obs_property_list_add_int(list, "1080p60 (4.2)", 42); + + obs_property_t *p; + p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), 0, + 300000, 50); + obs_property_int_set_suffix(p, " Kbps"); + + obs_properties_add_int(props, "keyint_sec", + obs_module_text("Keyframe Interval (seconds)"), 0, 20, + 1); + + return props; +} + +static bool vaapi_extra_data(void *data, uint8_t **extra_data, size_t *size) +{ + struct vaapi_encoder *enc = data; + + *extra_data = enc->header; + *size = enc->header_size; + return true; +} + +static bool vaapi_sei_data(void *data, uint8_t **extra_data, size_t *size) +{ + struct vaapi_encoder *enc = data; + + *extra_data = enc->sei; + *size = enc->sei_size; + return true; +} + +struct obs_encoder_info vaapi_encoder_info = { + .id = "ffmpeg_vaapi", + .type = OBS_ENCODER_VIDEO, + .codec = "h264", + .get_name = vaapi_getname, + .create = vaapi_create, + .destroy = vaapi_destroy, + .encode = vaapi_encode, + .get_defaults = vaapi_defaults, + .get_properties = vaapi_properties, + .get_extra_data = vaapi_extra_data, + .get_sei_data = vaapi_sei_data, + .get_video_info = vaapi_video_info +}; + +#endif diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c index 3ccef9b..808e5a0 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg.c @@ -2,12 +2,23 @@ #include #include #include +#include #include #include #include +#ifdef _WIN32 +#include +#include +#include +#endif + OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-ffmpeg", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "FFmpeg based sources/outputs/encoders"; +} extern struct obs_source_info ffmpeg_source; extern struct obs_output_info ffmpeg_output; @@ -17,6 +28,14 @@ extern struct obs_encoder_info aac_encoder_info; extern struct obs_encoder_info opus_encoder_info; extern struct obs_encoder_info nvenc_encoder_info; +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 27, 100) +#define LIBAVUTIL_VAAPI_AVAILABLE +#endif + +#ifdef LIBAVUTIL_VAAPI_AVAILABLE +extern struct obs_encoder_info vaapi_encoder_info; +#endif + static DARRAY(struct log_context { void *context; char str[4096]; @@ -121,6 +140,130 @@ cleanup: static const char *nvenc_check_name = "nvenc_check"; +#ifdef _WIN32 +static const wchar_t *blacklisted_adapters[] = { + L"720M", + L"730M", + L"740M", + L"745M", + L"820M", + L"830M", + L"840M", + L"845M", + L"920M", + L"930M", + L"940M", + L"945M", + L"1030", + L"MX110", + L"MX130", + L"MX150", + L"MX230", + L"MX250", + L"M520", + L"M500", + L"P500", + L"K620M" +}; + +static const size_t num_blacklisted = + sizeof(blacklisted_adapters) / sizeof(blacklisted_adapters[0]); + +static bool is_adapter(const wchar_t *name, const wchar_t *adapter) +{ + const wchar_t *find = wstrstri(name, adapter); + if (!find) { + return false; + } + + /* check before string for potential numeric mismatch */ + if (find > name && iswdigit(find[-1]) && iswdigit(find[0])) { + return false; + } + + /* check after string for potential numeric mismatch */ + size_t len = wcslen(adapter); + if (iswdigit(find[len - 1]) && iswdigit(find[len])) { + return false; + } + + return true; +} + +static bool is_blacklisted(const wchar_t *name) +{ + for (size_t i = 0; i < num_blacklisted; i++) { + const wchar_t *blacklisted_adapter = blacklisted_adapters[i]; + if (is_adapter(name, blacklisted_adapter)) { + return true; + } + } + + return false; +} + +typedef HRESULT (WINAPI *create_dxgi_proc)(const IID *, IDXGIFactory1 **); + +static bool nvenc_device_available(void) +{ + static HMODULE dxgi = NULL; + static create_dxgi_proc create = NULL; + IDXGIFactory1 *factory; + IDXGIAdapter1 *adapter; + bool available = false; + HRESULT hr; + UINT i = 0; + + if (!dxgi) { + dxgi = GetModuleHandleW(L"dxgi"); + if (!dxgi) { + dxgi = LoadLibraryW(L"dxgi"); + if (!dxgi) { + return true; + } + } + } + + if (!create) { + create = (create_dxgi_proc)GetProcAddress(dxgi, + "CreateDXGIFactory1"); + if (!create) { + return true; + } + } + + hr = create(&IID_IDXGIFactory1, &factory); + if (FAILED(hr)) { + return true; + } + + while (factory->lpVtbl->EnumAdapters1(factory, i++, &adapter) == S_OK) { + DXGI_ADAPTER_DESC desc; + + hr = adapter->lpVtbl->GetDesc(adapter, &desc); + adapter->lpVtbl->Release(adapter); + + if (FAILED(hr)) { + continue; + } + + if (wstrstri(desc.Description, L"nvidia") && + !is_blacklisted(desc.Description)) { + available = true; + goto finish; + } + } + +finish: + factory->lpVtbl->Release(factory); + return available; +} +#endif + +#ifdef _WIN32 +extern bool load_nvenc_lib(void); +#endif + static bool nvenc_supported(void) { av_register_all(); @@ -136,10 +279,12 @@ static bool nvenc_supported(void) } #if defined(_WIN32) - if (sizeof(void*) == 8) { - lib = os_dlopen("nvEncodeAPI64.dll"); - } else { - lib = os_dlopen("nvEncodeAPI.dll"); + if (!nvenc_device_available()) { + goto cleanup; + } + if (load_nvenc_lib()) { + success = true; + goto finish; } #else lib = os_dlopen("libnvidia-encode.so.1"); @@ -152,12 +297,28 @@ static bool nvenc_supported(void) cleanup: if (lib) os_dlclose(lib); +#if defined(_WIN32) +finish: +#endif profile_end(nvenc_check_name); return success; } #endif +#ifdef LIBAVUTIL_VAAPI_AVAILABLE +static bool vaapi_supported(void) +{ + AVCodec *vaenc = avcodec_find_encoder_by_name("h264_vaapi"); + return !!vaenc; +} +#endif + +#ifdef _WIN32 +extern void jim_nvenc_load(void); +extern void jim_nvenc_unload(void); +#endif + bool obs_module_load(void) { da_init(active_log_contexts); @@ -174,8 +335,19 @@ bool obs_module_load(void) #ifndef __APPLE__ if (nvenc_supported()) { blog(LOG_INFO, "NVENC supported"); +#ifdef _WIN32 + if (get_win_ver_int() > 0x0601) { + jim_nvenc_load(); + } +#endif obs_register_encoder(&nvenc_encoder_info); } +#if !defined(_WIN32) && defined(LIBAVUTIL_VAAPI_AVAILABLE) + if (vaapi_supported()) { + blog(LOG_INFO, "FFMPEG VAAPI supported"); + obs_register_encoder(&vaapi_encoder_info); + } +#endif #endif return true; } @@ -197,4 +369,8 @@ void obs_module_unload(void) da_free(active_log_contexts); da_free(cached_log_contexts); + +#ifdef _WIN32 + jim_nvenc_unload(); +#endif } diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index ec4289c..d6cf13c 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -1,13 +1,23 @@ project(obs-filters) -find_package(Libspeexdsp QUIET) -if(LIBSPEEXDSP_FOUND) - set(obs-filters_LIBSPEEXDSP_SOURCES - noise-suppress-filter.c) - set(obs-filters_LIBSPEEXDSP_LIBRARIES - ${LIBSPEEXDSP_LIBRARIES}) +option(DISABLE_SPEEXDSP "Disable building of the SpeexDSP-based Noise Suppression filter" OFF) + +if(DISABLE_SPEEXDSP) + message(STATUS "SpeexDSP support disabled") + set(LIBSPEEXDSP_FOUND FALSE) else() - message(STATUS "Speexdsp library not found, speexdsp filters disabled") + find_package(Libspeexdsp QUIET) + + if(NOT LIBSPEEXDSP_FOUND) + message(STATUS "SpeexDSP support not found") + set(LIBSPEEXDSP_FOUND FALSE) + else() + message(STATUS "SpeexDSP supported") + set(obs-filters_LIBSPEEXDSP_SOURCES + noise-suppress-filter.c) + set(obs-filters_LIBSPEEXDSP_LIBRARIES + ${LIBSPEEXDSP_LIBRARIES}) + endif() endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/obs-filters-config.h.in" @@ -38,7 +48,11 @@ set(obs-filters_SOURCES gain-filter.c noise-gate-filter.c mask-filter.c - compressor-filter.c) + invert-audio-polarity.c + compressor-filter.c + limiter-filter.c + expander-filter.c + luma-key-filter.c) add_library(obs-filters MODULE ${obs-filters_SOURCES} diff --git a/plugins/obs-filters/chroma-key-filter.c b/plugins/obs-filters/chroma-key-filter.c index 728135e..3fa9790 100644 --- a/plugins/obs-filters/chroma-key-filter.c +++ b/plugins/obs-filters/chroma-key-filter.c @@ -35,7 +35,6 @@ struct chroma_key_filter_data { gs_eparam_t *pixel_size_param; gs_eparam_t *chroma_param; - gs_eparam_t *key_rgb_param; gs_eparam_t *similarity_param; gs_eparam_t *smoothness_param; gs_eparam_t *spill_param; @@ -45,7 +44,6 @@ struct chroma_key_filter_data { float brightness; float gamma; - struct vec4 key_rgb; struct vec2 chroma; float similarity; float smoothness; @@ -97,6 +95,7 @@ static inline void chroma_settings_update( SETTING_KEY_COLOR); const char *key_type = obs_data_get_string(settings, SETTING_COLOR_TYPE); + struct vec4 key_rgb; struct vec4 key_color_v4; struct matrix4 yuv_mat_m4; @@ -107,10 +106,10 @@ static inline void chroma_settings_update( else if (strcmp(key_type, "magenta") == 0) key_color = 0xFF00FF; - vec4_from_rgba(&filter->key_rgb, key_color | 0xFF000000); + vec4_from_rgba(&key_rgb, key_color | 0xFF000000); memcpy(&yuv_mat_m4, yuv_mat, sizeof(yuv_mat)); - vec4_transform(&key_color_v4, &filter->key_rgb, &yuv_mat_m4); + vec4_transform(&key_color_v4, &key_rgb, &yuv_mat_m4); vec2_set(&filter->chroma, key_color_v4.y, key_color_v4.z); filter->similarity = (float)similarity / 1000.0f; @@ -161,8 +160,6 @@ static void *chroma_key_create(obs_data_t *settings, obs_source_t *context) filter->effect, "gamma"); filter->chroma_param = gs_effect_get_param_by_name( filter->effect, "chroma_key"); - filter->key_rgb_param = gs_effect_get_param_by_name( - filter->effect, "key_rgb"); filter->pixel_size_param = gs_effect_get_param_by_name( filter->effect, "pixel_size"); filter->similarity_param = gs_effect_get_param_by_name( @@ -205,7 +202,6 @@ static void chroma_key_render(void *data, gs_effect_t *effect) gs_effect_set_float(filter->brightness_param, filter->brightness); gs_effect_set_float(filter->gamma_param, filter->gamma); gs_effect_set_vec2(filter->chroma_param, &filter->chroma); - gs_effect_set_vec4(filter->key_rgb_param, &filter->key_rgb); gs_effect_set_vec2(filter->pixel_size_param, &pixel_size); gs_effect_set_float(filter->similarity_param, filter->similarity); gs_effect_set_float(filter->smoothness_param, filter->smoothness); @@ -251,7 +247,8 @@ static obs_properties_t *chroma_key_properties(void *data) obs_properties_add_int_slider(props, SETTING_SPILL, TEXT_SPILL, 1, 1000, 1); - obs_properties_add_int(props, SETTING_OPACITY, TEXT_OPACITY, 0, 100, 1); + obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, + 0, 100, 1); obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST, -1.0, 1.0, 0.01); obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, diff --git a/plugins/obs-filters/color-key-filter.c b/plugins/obs-filters/color-key-filter.c index 1b1c310..17a64f2 100644 --- a/plugins/obs-filters/color-key-filter.c +++ b/plugins/obs-filters/color-key-filter.c @@ -218,7 +218,8 @@ static obs_properties_t *color_key_properties(void *data) obs_properties_add_int_slider(props, SETTING_SMOOTHNESS, TEXT_SMOOTHNESS, 1, 1000, 1); - obs_properties_add_int(props, SETTING_OPACITY, TEXT_OPACITY, 0, 100, 1); + obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, + 0, 100, 1); obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST, -1.0, 1.0, 0.01); obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, diff --git a/plugins/obs-filters/data/chroma_key_filter.effect b/plugins/obs-filters/data/chroma_key_filter.effect index 8a75968..c7d41f3 100644 --- a/plugins/obs-filters/data/chroma_key_filter.effect +++ b/plugins/obs-filters/data/chroma_key_filter.effect @@ -12,7 +12,6 @@ uniform float brightness; uniform float gamma; uniform float2 chroma_key; -uniform float4 key_rgb; uniform float2 pixel_size; uniform float similarity; uniform float smoothness; @@ -55,17 +54,15 @@ float4 SampleTexture(float2 uv) float GetBoxFilteredChromaDist(float3 rgb, float2 texCoord) { - float distVal = GetChromaDist(rgb); - distVal += GetChromaDist(SampleTexture(texCoord-pixel_size).rgb); - distVal += GetChromaDist(SampleTexture(texCoord-float2(pixel_size.x, 0.0)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord-float2(pixel_size.x, -pixel_size.y)).rgb); - - distVal += GetChromaDist(SampleTexture(texCoord-float2(0.0, pixel_size.y)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord+float2(0.0, pixel_size.y)).rgb); - - distVal += GetChromaDist(SampleTexture(texCoord+float2(pixel_size.x, -pixel_size.y)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord+float2(pixel_size.x, 0.0)).rgb); - distVal += GetChromaDist(SampleTexture(texCoord+pixel_size).rgb); + float2 h_pixel_size = pixel_size / 2.0; + float2 point_0 = float2(pixel_size.x, h_pixel_size.y); + float2 point_1 = float2(h_pixel_size.x, -pixel_size.y); + float distVal = GetChromaDist(SampleTexture(texCoord-point_0).rgb); + distVal += GetChromaDist(SampleTexture(texCoord+point_0).rgb); + distVal += GetChromaDist(SampleTexture(texCoord-point_1).rgb); + distVal += GetChromaDist(SampleTexture(texCoord+point_1).rgb); + distVal *= 2.0; + distVal += GetChromaDist(rgb); return distVal / 9.0; } @@ -76,6 +73,7 @@ float4 ProcessChromaKey(float4 rgba, VertData v_in) float fullMask = pow(saturate(baseMask / smoothness), 1.5); float spillVal = pow(saturate(baseMask / spill), 1.5); + rgba.rgba *= color; rgba.a *= fullMask; float desat = (rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722); @@ -86,7 +84,7 @@ float4 ProcessChromaKey(float4 rgba, VertData v_in) float4 PSChromaKeyRGBA(VertData v_in) : TARGET { - float4 rgba = image.Sample(textureSampler, v_in.uv) * color; + float4 rgba = image.Sample(textureSampler, v_in.uv); return ProcessChromaKey(rgba, v_in); } diff --git a/plugins/obs-filters/data/color_key_filter.effect b/plugins/obs-filters/data/color_key_filter.effect index 599d088..1af6b9d 100644 --- a/plugins/obs-filters/data/color_key_filter.effect +++ b/plugins/obs-filters/data/color_key_filter.effect @@ -39,17 +39,9 @@ float GetColorDist(float3 rgb) return distance(key_color.rgb, rgb); } -float4 SampleYUVToRGB(float2 uv) -{ - float4 yuv = image.Sample(textureSampler, uv); - yuv.xyz = clamp(yuv.xyz, color_range_min, color_range_max); - return saturate(mul(float4(yuv.xyz, 1.0), color_matrix)); -} - float4 ProcessColorKey(float4 rgba, VertData v_in) { float colorDist = GetColorDist(rgba.rgb); - float baseMask = colorDist - similarity; rgba.a *= saturate(max(colorDist - similarity, 0.0) / smoothness); return CalcColor(rgba); diff --git a/plugins/obs-filters/data/locale/bg-BG.ini b/plugins/obs-filters/data/locale/bg-BG.ini new file mode 100644 index 0000000..c65da8a --- /dev/null +++ b/plugins/obs-filters/data/locale/bg-BG.ini @@ -0,0 +1,4 @@ +Limiter="Ограничител" +Limiter.Threshold="Праг на отсичане (dB)" +Limiter.ReleaseTime="Време за отговор (мс)" + diff --git a/plugins/obs-filters/data/locale/ca-ES.ini b/plugins/obs-filters/data/locale/ca-ES.ini index 455357a..f3e0047 100644 --- a/plugins/obs-filters/data/locale/ca-ES.ini +++ b/plugins/obs-filters/data/locale/ca-ES.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Retard de processament" UndistortCenter="No distorsionis el centre de la imatge en escalar des d'una ultrapanoràmica" NoiseGate="Porta de soroll" NoiseSuppress="Supressió de soroll" +InvertPolarity="Inverteix la polaritat" Gain="Guany" DelayMs="Retard (en mil·lisegons)" Type="Tipus" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Temps d'alliberar (mil·lisegons)" Gain.GainDB="Guany (dB)" StretchImage="Expandir imatge (descarta la relació d'aspecte d'imatge)" Resolution="Resolució" +Base.Canvas="Resolució base (Llenç)" None="Cap" ScaleFiltering="Escala de filtratge" ScaleFiltering.Point="Punt" ScaleFiltering.Bilinear="Bilineal" ScaleFiltering.Bicubic="Bicúbic" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Àrea" NoiseSuppress.SuppressLevel="Nivell de supressió (dB)" Saturation="Saturació" HueShift="Cavi de tonalitat" @@ -74,4 +77,25 @@ Compressor.AttackTime="Atac (ms)" Compressor.ReleaseTime="Llançament (ms)" Compressor.OutputGain="Guany de sortida (dB)" Compressor.SidechainSource="Font d'atenuació/reducció" +Limiter="Límit" +Limiter.Threshold="Llindar (dB)" +Limiter.ReleaseTime="Llançament (ms)" +Expander="Expansor" +Expander.Ratio="Ràtio (X:1)" +Expander.Threshold="Llindar (dB)" +Expander.AttackTime="Atac (ms)" +Expander.ReleaseTime="Llançament (ms)" +Expander.OutputGain="Guany de sortida (dB)" +Expander.Detector="Detecció" +Expander.RMS="RMS" +Expander.Peak="Límit" +Expander.None="Cap" +Expander.Presets="Predefinits" +Expander.Presets.Expander="Expansor" +Expander.Presets.Gate="Porta" +LumaKeyFilter="Clau Luma" +Luma.LumaMax="Màx. Luma" +Luma.LumaMin="Mín. Luma" +Luma.LumaMaxSmooth="Suavitzat màx. Luma" +Luma.LumaMinSmooth="Suavitzat mín. Luma" diff --git a/plugins/obs-filters/data/locale/cs-CZ.ini b/plugins/obs-filters/data/locale/cs-CZ.ini index abbcaeb..6af9711 100644 --- a/plugins/obs-filters/data/locale/cs-CZ.ini +++ b/plugins/obs-filters/data/locale/cs-CZ.ini @@ -12,6 +12,7 @@ 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" +InvertPolarity="Obrátit polaritu" Gain="Zisk" DelayMs="Zpoždění (ms)" Type="Typ" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Čas uvolnění (ms)" Gain.GainDB="Zisk (dB)" StretchImage="Roztáhnout obrázek (ignorovat poměr stran)" Resolution="Rozlišení" +Base.Canvas="Základní rozlišení" None="Žádné" ScaleFiltering="Filtrování rozsahu" ScaleFiltering.Point="Bod" ScaleFiltering.Bilinear="Bilineární" ScaleFiltering.Bicubic="Bikubický" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Oblast" NoiseSuppress.SuppressLevel="Úroveň potlačení (dB)" Saturation="Saturace" HueShift="Posun odstínu" @@ -74,4 +77,25 @@ Compressor.AttackTime="Stažení (v ms)" Compressor.ReleaseTime="Uvolnění (v ms)" Compressor.OutputGain="Síla výstupu (v dB)" Compressor.SidechainSource="Zdroj pro side-chain/ducking" +Limiter="Omezovač" +Limiter.Threshold="Práh (v dB)" +Limiter.ReleaseTime="Uvolnění (v ms)" +Expander="Expander" +Expander.Ratio="Poměr (X:1)" +Expander.Threshold="Práh (v dB)" +Expander.AttackTime="Stažení (v ms)" +Expander.ReleaseTime="Uvolnění (v ms)" +Expander.OutputGain="Síla výstupu (v dB)" +Expander.Detector="Detekce" +Expander.RMS="RMS" +Expander.Peak="Špičky" +Expander.None="Žádná" +Expander.Presets="Předvolby" +Expander.Presets.Expander="Expander" +Expander.Presets.Gate="Brána" +LumaKeyFilter="Luma klíč" +Luma.LumaMax="Luma maximum" +Luma.LumaMin="Luma minimum" +Luma.LumaMaxSmooth="Luma maximální vyhlazení" +Luma.LumaMinSmooth="Luma minimální vyhlazení" diff --git a/plugins/obs-filters/data/locale/da-DK.ini b/plugins/obs-filters/data/locale/da-DK.ini index e9a125b..ab73964 100644 --- a/plugins/obs-filters/data/locale/da-DK.ini +++ b/plugins/obs-filters/data/locale/da-DK.ini @@ -1,10 +1,10 @@ ColorFilter="Farvekorrektion" ColorGradeFilter="Anvend LUT" -MaskFilter="Billede maske/blanding" -AsyncDelayFilter="Video forsinkelse (asynkron)" +MaskFilter="Billedmaskering/-blanding" +AsyncDelayFilter="Videoforsinkelse (asynkron)" CropFilter="Beskæring/Polstring" ScrollFilter="Rul" -ChromaKeyFilter="Chroma nøgle" +ChromaKeyFilter="Chroma-nøgle" ColorKeyFilter="Farvenøgle" SharpnessFilter="Skarphed" ScaleFilter="Skalering/Formatforhold" @@ -12,14 +12,15 @@ GPUDelayFilter="Renderingsforsinkelse" UndistortCenter="Fjern forvrængning af billedets midte ved skalering fra ultrabred" NoiseGate="Noise Gate" NoiseSuppress="Støjundertrykkelse" +InvertPolarity="Invertér polaritet" Gain="Forstærkning" DelayMs="Forsinkelse (millisekunder)" Type="Type" -MaskBlendType.MaskColor="Alpha maske (farvekanal)" -MaskBlendType.MaskAlpha="Alpha maske (Alpha kanal)" -MaskBlendType.BlendMultiply="Blend (formere)" -MaskBlendType.BlendAddition="Blanding (tilføjelse)" -MaskBlendType.BlendSubtraction="Blanding (subtraktion)" +MaskBlendType.MaskColor="Alpha-maske (farvekanal)" +MaskBlendType.MaskAlpha="Alpha-maske (Alpha-kanal)" +MaskBlendType.BlendMultiply="Bland (formere)" +MaskBlendType.BlendAddition="Bland (tilføjelse)" +MaskBlendType.BlendSubtraction="Bland (subtraktion)" Path="Sti" Color="Farve" Opacity="Gennemsigtighed" @@ -28,11 +29,11 @@ Brightness="Lysstyrke" Gamma="Gamma" BrowsePath.Images="Alle billedfiler" BrowsePath.AllFiles="Alle filer" -KeyColorType="Nøglefarve type" -KeyColor="Nøglefarven" -Similarity="Lighed (1-1000)" -Smoothness="Glathed (1-1000)" -ColorSpillReduction="Nøglefarve udslipsreduktion (1-1000)" +KeyColorType="Nøglefarvetype" +KeyColor="Nøglefarve" +Similarity="Lighed (1-1.000)" +Smoothness="Glathed (1-1.000)" +ColorSpillReduction="Nøglefarve udslipsreduktion (1-1.000)" Crop.Left="Venstre" Crop.Right="Højre" Crop.Top="Top" @@ -44,25 +45,27 @@ ScrollFilter.SpeedX="Horisontal hastighed" ScrollFilter.SpeedY="Vertikal hastighed" ScrollFilter.LimitWidth="Begræns bredde" ScrollFilter.LimitHeight="Begræns højde" -CustomColor="Brugerdefineret farve" +CustomColor="Tilpasset farve" Red="Rød" Green="Grøn" Blue="Blå" Magenta="Magenta" -NoiseGate.OpenThreshold="Åbne-tærskel (dB)" -NoiseGate.CloseThreshold="Lukke-tærskel (dB)" -NoiseGate.AttackTime="Effektueringstid (millisek.)" +NoiseGate.OpenThreshold="Åbningstærskel (dB)" +NoiseGate.CloseThreshold="Lukningstærskel (dB)" +NoiseGate.AttackTime="Responstid (millisek.)" NoiseGate.HoldTime="Holdetid (millisek.)" NoiseGate.ReleaseTime="Frigivelsestid (millisek.)" Gain.GainDB="Forstærkning (dB)" -StretchImage="Stræk billedet (ignorer størrelsesforhold)" +StretchImage="Stræk billede (ignorér størrelsesforhold)" Resolution="Opløsning" +Base.Canvas="Grundopløsning (lærred)" None="Ingen" ScaleFiltering="Skaleringsfilter" ScaleFiltering.Point="Punkt" ScaleFiltering.Bilinear="Bilineær" ScaleFiltering.Bicubic="Bikubisk" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Område" NoiseSuppress.SuppressLevel="Undertrykkelsesniveau (dB)" Saturation="Mætning" HueShift="Nuanceskift" @@ -70,8 +73,29 @@ 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)" -Compressor.SidechainSource="Sidechain/Ducking-kilde" +Compressor.AttackTime="Responstid (ms)" +Compressor.ReleaseTime="Frigivelse (ms)" +Compressor.OutputGain="Outputforstærkning (dB)" +Compressor.SidechainSource="Sidechain-/Ducking-kilde" +Limiter="Begrænser" +Limiter.Threshold="Grænse (dB)" +Limiter.ReleaseTime="Frigivelse (ms)" +Expander="Expander" +Expander.Ratio="Forhold (X:1)" +Expander.Threshold="Grænse (dB)" +Expander.AttackTime="Responstid (ms)" +Expander.ReleaseTime="Frigivelse (ms)" +Expander.OutputGain="Outputforstærkning (dB)" +Expander.Detector="Detektering" +Expander.RMS="RMS" +Expander.Peak="Spids" +Expander.None="Ingen" +Expander.Presets="Forvalg" +Expander.Presets.Expander="Expander" +Expander.Presets.Gate="Gate" +LumaKeyFilter="Luma Key" +Luma.LumaMax="Luma maks." +Luma.LumaMin="Luma min." +Luma.LumaMaxSmooth="Luma maks. glidende" +Luma.LumaMinSmooth="Luma min. glidende" diff --git a/plugins/obs-filters/data/locale/de-DE.ini b/plugins/obs-filters/data/locale/de-DE.ini index d1ae53b..449e752 100644 --- a/plugins/obs-filters/data/locale/de-DE.ini +++ b/plugins/obs-filters/data/locale/de-DE.ini @@ -1,25 +1,26 @@ ColorFilter="Farbkorrektur" ColorGradeFilter="LUT anwenden" -MaskFilter="Bild Maske/Blend" +MaskFilter="Bildmaske/-Vermischung" AsyncDelayFilter="Videoverzögerung (Asynchron)" CropFilter="Zuschneiden/Pad" ScrollFilter="Bewegung" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" -SharpnessFilter="Schärfen" +SharpnessFilter="Schärfung" ScaleFilter="Skalierung/Seitenverhältnis" GPUDelayFilter="Renderverzögerung" -UndistortCenter="Entzerre Mitte des Bildes bei der Skalierung von Ultraweitwinkel" +UndistortCenter="Mitte des Bildes bei der Skalierung von Ultraweitwinkel entzerren" NoiseGate="Noise Gate" NoiseSuppress="Rauschunterdrückung" +InvertPolarity="Audiopolarität umkehren" Gain="Gain" DelayMs="Verzögerung (Millisekunden)" Type="Art" MaskBlendType.MaskColor="Alphamaske (Farbkanal)" MaskBlendType.MaskAlpha="Alphamaske (Alphakanal)" MaskBlendType.BlendMultiply="Blend (Multiplizieren)" -MaskBlendType.BlendAddition="Blend (Addition)" -MaskBlendType.BlendSubtraction="Blend (Subtraktion)" +MaskBlendType.BlendAddition="Blend (Addieren)" +MaskBlendType.BlendSubtraction="Blend (Subtrahieren)" Path="Pfad" Color="Farbe" Opacity="Deckkraft" @@ -28,11 +29,11 @@ Brightness="Helligkeit" Gamma="Gamma" BrowsePath.Images="Alle Bilddateien" BrowsePath.AllFiles="Alle Dateien" -KeyColorType="Key-Farbe-Typ" +KeyColorType="Key-Farbtyp" KeyColor="Key-Farbe" -Similarity="Ähnlichkeit (1-1000)" -Smoothness="Glätte (1-1000)" -ColorSpillReduction="Key-Farbe Spill Reduktion (1-1000)" +Similarity="Ähnlichkeit (1 — 1000)" +Smoothness="Glätte (1 — 1000)" +ColorSpillReduction="Key-Farbe-Spill-Reduzierung (1 — 1000)" Crop.Left="Links" Crop.Right="Rechts" Crop.Top="Oben" @@ -57,21 +58,44 @@ NoiseGate.ReleaseTime="Release-Zeit (Millisekunden)" Gain.GainDB="Gain (dB)" StretchImage="Bild strecken (Bildseitenverhältnis verwerfen)" Resolution="Auflösung" +Base.Canvas="Basis-(Leinwand-)Auflösung" None="Keine" ScaleFiltering="Skalierungsfilterung" ScaleFiltering.Point="Point" ScaleFiltering.Bilinear="Bilinear" ScaleFiltering.Bicubic="Bicubic" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Bereich" NoiseSuppress.SuppressLevel="Unterdrückungspegel (dB)" Saturation="Sättigung" HueShift="Farbtonverschiebung" Amount="Betrag" Compressor="Kompressor" Compressor.Ratio="Verhältnis (X:1)" -Compressor.Threshold="Schwelle (dB)" +Compressor.Threshold="Schwellenwert (dB)" Compressor.AttackTime="Angriff (ms)" -Compressor.ReleaseTime="Freigabe (ms)" +Compressor.ReleaseTime="Abfallzeit (ms)" Compressor.OutputGain="Ausgangspegel (dB)" -Compressor.SidechainSource="Sidechain/Ducking Quelle" +Compressor.SidechainSource="Sidechain-/Ducking-Quelle" +Limiter="Begrenzer" +Limiter.Threshold="Schwellenwert (dB)" +Limiter.ReleaseTime="Abfallzeit (ms)" +Expander="Expander" +Expander.Ratio="Verhältnis (X:1)" +Expander.Threshold="Schwellenwert (dB)" +Expander.AttackTime="Angriff (ms)" +Expander.ReleaseTime="Abfallzeit (ms)" +Expander.OutputGain="Ausgangspegel (dB)" +Expander.Detector="Erkennung" +Expander.RMS="RMS" +Expander.Peak="Spitze" +Expander.None="Keine" +Expander.Presets="Voreinstellungen" +Expander.Presets.Expander="Expander" +Expander.Presets.Gate="Gate" +LumaKeyFilter="Luma-Key" +Luma.LumaMax="Max. Luma" +Luma.LumaMin="Min. Luma" +Luma.LumaMaxSmooth="Max. Luma-Glätte" +Luma.LumaMinSmooth="Min. Luma-Glätte" diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index 241cbee..cf49d70 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Render Delay" UndistortCenter="Undistort center of image when scaling from ultrawide" NoiseGate="Noise Gate" NoiseSuppress="Noise Suppression" +InvertPolarity="Invert Polarity" Gain="Gain" DelayMs="Delay (milliseconds)" Type="Type" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Release Time (milliseconds)" Gain.GainDB="Gain (dB)" StretchImage="Stretch Image (discard image aspect ratio)" Resolution="Resolution" +Base.Canvas="Base (Canvas) Resolution" None="None" ScaleFiltering="Scale Filtering" ScaleFiltering.Point="Point" ScaleFiltering.Bilinear="Bilinear" ScaleFiltering.Bicubic="Bicubic" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Area" NoiseSuppress.SuppressLevel="Suppression Level (dB)" Saturation="Saturation" HueShift="Hue Shift" @@ -74,3 +77,24 @@ Compressor.AttackTime="Attack (ms)" Compressor.ReleaseTime="Release (ms)" Compressor.OutputGain="Output Gain (dB)" Compressor.SidechainSource="Sidechain/Ducking Source" +Limiter="Limiter" +Limiter.Threshold="Threshold (dB)" +Limiter.ReleaseTime="Release (ms)" +Expander="Expander" +Expander.Ratio="Ratio (X:1)" +Expander.Threshold="Threshold (dB)" +Expander.AttackTime="Attack (ms)" +Expander.ReleaseTime="Release (ms)" +Expander.OutputGain="Output Gain (dB)" +Expander.Detector="Detection" +Expander.RMS="RMS" +Expander.Peak="Peak" +Expander.None="None" +Expander.Presets="Presets" +Expander.Presets.Expander="Expander" +Expander.Presets.Gate="Gate" +LumaKeyFilter="Luma Key" +Luma.LumaMax="Luma Max" +Luma.LumaMin="Luma Min" +Luma.LumaMaxSmooth="Luma Max Smooth" +Luma.LumaMinSmooth="Luma Min Smooth" diff --git a/plugins/obs-filters/data/locale/es-ES.ini b/plugins/obs-filters/data/locale/es-ES.ini index 25a6492..5e1336b 100644 --- a/plugins/obs-filters/data/locale/es-ES.ini +++ b/plugins/obs-filters/data/locale/es-ES.ini @@ -1,17 +1,18 @@ ColorFilter="Corrección de color" ColorGradeFilter="Aplicar LUT" MaskFilter="Imagen máscara/mezcla" -AsyncDelayFilter="Retardo de Video (asincróno)" +AsyncDelayFilter="Retardo de Vídeo (asíncrono)" CropFilter="Recortar/Acolchar" -ScrollFilter="desplazamiento" +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" +UndistortCenter="No distorsionar el centro de la imagen al escalar desde una ultrapanorámica" NoiseGate="Puerta anti-ruidos" NoiseSuppress="Eliminación de ruido" +InvertPolarity="Invertir polaridad" Gain="Ganancia" DelayMs="Retardo (milisegundos)" Type="Tipo" @@ -41,7 +42,7 @@ Crop.Width="Ancho" Crop.Height="Alto" Crop.Relative="Relativo" ScrollFilter.SpeedX="Velocidad Horizontal" -ScrollFilter.SpeedY="VElocidad Vertical" +ScrollFilter.SpeedY="Velocidad Vertical" ScrollFilter.LimitWidth="Limitar el ancho" ScrollFilter.LimitHeight="Limitar la altura" CustomColor="Color Personalizado" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Tiempo (en milisegundos) de liberacion" Gain.GainDB="Ganancia (dB)" StretchImage="Expandir imagen (descartar relación de aspecto de imagen)" Resolution="Resolución" +Base.Canvas="Resolución base (Lienzo)" None="Ninguno" ScaleFiltering="Escala de filtrado" ScaleFiltering.Point="Punto" ScaleFiltering.Bilinear="Bilineal" ScaleFiltering.Bicubic="Bicúbico" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Área" NoiseSuppress.SuppressLevel="Nivel de eliminación de ruido (dB)" Saturation="Saturación" HueShift="Cambio de tonalidad" @@ -74,4 +77,25 @@ Compressor.AttackTime="Ataque (ms)" Compressor.ReleaseTime="Liberación (ms)" Compressor.OutputGain="Ganancia de salida (dB)" Compressor.SidechainSource="Fuente de atenuación/reducción" +Limiter="Limitador" +Limiter.Threshold="Umbral (dB)" +Limiter.ReleaseTime="Soltar (ms)" +Expander="Expansor" +Expander.Ratio="Relación (X:1)" +Expander.Threshold="Umbral (dB)" +Expander.AttackTime="Atacar (ms)" +Expander.ReleaseTime="Soltar (ms)" +Expander.OutputGain="Ganancia de salida (dB)" +Expander.Detector="Detección" +Expander.RMS="RMS" +Expander.Peak="Pico" +Expander.None="Ninguna" +Expander.Presets="Pre-ajustes" +Expander.Presets.Expander="Expansor" +Expander.Presets.Gate="Puerta" +LumaKeyFilter="Clave Luma" +Luma.LumaMax="Máx. Luma" +Luma.LumaMin="Min. Luma" +Luma.LumaMaxSmooth="Suavizado Máximo Luma" +Luma.LumaMinSmooth="Suavizado Mínimo Luma" diff --git a/plugins/obs-filters/data/locale/eu-ES.ini b/plugins/obs-filters/data/locale/eu-ES.ini index d9e7d5c..851669e 100644 --- a/plugins/obs-filters/data/locale/eu-ES.ini +++ b/plugins/obs-filters/data/locale/eu-ES.ini @@ -6,12 +6,13 @@ CropFilter="Moztu/Bete" ScrollFilter="Korritu" ChromaKeyFilter="Kroma" ColorKeyFilter="Kolore gakoa" -SharpnessFilter="Enfokea" +SharpnessFilter="Fokatzea" ScaleFilter="Eskala/Aspektu-erlazioa" GPUDelayFilter="Errendatzearen atzerapena" UndistortCenter="Ez distortsionatu irudiaren erdigunea ultra zabala eskalatzean" NoiseGate="Zarata atalasea" NoiseSuppress="Zarata kendu" +InvertPolarity="Polaritatea alderantzikatu" Gain="Irabazia" DelayMs="Atzerapena (milisegundo)" Type="Mota" @@ -49,20 +50,22 @@ Red="Gorria" Green="Berdea" Blue="Urdina" Magenta="Magenta" -NoiseGate.OpenThreshold="Irekiera muga (dB)" -NoiseGate.CloseThreshold="Itxiera muga (dB)" +NoiseGate.OpenThreshold="Irekiera atalasea (dB)" +NoiseGate.CloseThreshold="Itxiera atalasea (dB)" NoiseGate.AttackTime="Eraso denbora (milisegundo)" NoiseGate.HoldTime="Euste denbora (milisegundo)" NoiseGate.ReleaseTime="Askatze denbora (milisegundo)" Gain.GainDB="Irabazia (dB)" StretchImage="Luzatu irudia (baztertu irudiaren aspektu-erlazioa)" Resolution="Bereizmena" +Base.Canvas="Oinarriaren (oihalaren) bereizmena" None="Ezer ez" ScaleFiltering="Iragazketa-eskala" ScaleFiltering.Point="Puntua" ScaleFiltering.Bilinear="Bilineala" ScaleFiltering.Bicubic="Bikubikoa" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Area" NoiseSuppress.SuppressLevel="Kenketaren maila (dB)" Saturation="Margoasetasuna" HueShift="Nabardura Aldaketa" @@ -74,4 +77,25 @@ Compressor.AttackTime="Erasoa (ms)" Compressor.ReleaseTime="Askapena (ms)" Compressor.OutputGain="Irteerako irabazia (dB)" Compressor.SidechainSource="Sidechain/Ducking iturburua" +Limiter="Mugatzailea" +Limiter.Threshold="Atalasea (dB)" +Limiter.ReleaseTime="Askatze denbora (ms)" +Expander="Deskonprimagailua" +Expander.Ratio="Erlazioa (X:1)" +Expander.Threshold="Atalasea (dB)" +Expander.AttackTime="Erasoa (ms)" +Expander.ReleaseTime="Askapena (ms)" +Expander.OutputGain="Irteerako irabazia (dB)" +Expander.Detector="Detekzioa" +Expander.RMS="RMS" +Expander.Peak="Gailurra" +Expander.None="Bat ere ez" +Expander.Presets="Aurrez ezarritakoak" +Expander.Presets.Expander="Deskonprimagailua" +Expander.Presets.Gate="Atea" +LumaKeyFilter="Luma gakoa" +Luma.LumaMax="Luma max" +Luma.LumaMin="Luma min" +Luma.LumaMaxSmooth="Luma leuntasun max" +Luma.LumaMinSmooth="Luma leuntasun min" diff --git a/plugins/obs-filters/data/locale/fa-IR.ini b/plugins/obs-filters/data/locale/fa-IR.ini new file mode 100644 index 0000000..9453392 --- /dev/null +++ b/plugins/obs-filters/data/locale/fa-IR.ini @@ -0,0 +1,18 @@ +ColorFilter="اصلاح رنگ" +ColorGradeFilter="درخواست LUT" +MaskFilter="تصویر ماسک/مخلوط" +ChromaKeyFilter="صحنه کلیدی(کروماکی)" +ColorKeyFilter="رنگ کلیدی" +SharpnessFilter="تیزکردن" +BrowsePath.AllFiles="همه‌ی فایل ها" +KeyColorType="نوع رنگ کلیدی" +Crop.Left="چپ" +Crop.Right="راست" +Crop.Top="بالا" +Crop.Bottom="پایین" +Crop.Width="عرض" +Crop.Height="ارتفاع" +Red="قرمز" +Green="سبز" +ScaleFiltering.Area="ناحیه" + diff --git a/plugins/obs-filters/data/locale/fi-FI.ini b/plugins/obs-filters/data/locale/fi-FI.ini index e456d6d..7ebad95 100644 --- a/plugins/obs-filters/data/locale/fi-FI.ini +++ b/plugins/obs-filters/data/locale/fi-FI.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Renderoinnin viive" UndistortCenter="Poista vääristymä keskeltä kuvaa skaalattaessa ultra-leveästä" NoiseGate="Noise Gate" NoiseSuppress="Melunvaimennus" +InvertPolarity="Käännä Audio-napaisuus" Gain="Vahvistus" DelayMs="Viive (millisekuntia)" Type="Tyyppi" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Vapautumisaika (millisekuntia)" Gain.GainDB="Vahvistus (dB)" StretchImage="Venytä kuvaa (Ohita kuvasuhde)" Resolution="Resoluutio" +Base.Canvas="Piirtoalueen (kanvaasin) resoluutio" None="Ei mitään" ScaleFiltering="Skaalauksen suodatus" ScaleFiltering.Point="Piste" ScaleFiltering.Bilinear="Bilinear" ScaleFiltering.Bicubic="Bicubic" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Alue" NoiseSuppress.SuppressLevel="Vaimennustaso (dB)" Saturation="Värikylläisyys" HueShift="Värisävy" @@ -74,4 +77,25 @@ Compressor.AttackTime="Attack-aika (ms)" Compressor.ReleaseTime="Vapautumisaika (ms)" Compressor.OutputGain="Signaalin vahvistus (dB)" Compressor.SidechainSource="Lähteen väistäminen" +Limiter="Rajoitin" +Limiter.Threshold="Kynnysarvo (dB)" +Limiter.ReleaseTime="Vapautumisaika (ms)" +Expander="Laajentaja" +Expander.Ratio="Suhde (X:1)" +Expander.Threshold="Kynnysarvo (dB)" +Expander.AttackTime="Attack-aika (ms)" +Expander.ReleaseTime="Vapautumisaika (ms)" +Expander.OutputGain="Signaalin vahvistus (dB)" +Expander.Detector="Havaitseminen" +Expander.RMS="RMS" +Expander.Peak="Huippu" +Expander.None="Ei mitään" +Expander.Presets="Esiasetukset" +Expander.Presets.Expander="Laajentaja" +Expander.Presets.Gate="Gate" +LumaKeyFilter="Luma-avain" +Luma.LumaMax="Luma-maksimi" +Luma.LumaMin="Luma-minimi" +Luma.LumaMaxSmooth="Luma-maksimin pehmennys" +Luma.LumaMinSmooth="Luma-minimin pehmennys" diff --git a/plugins/obs-filters/data/locale/fr-FR.ini b/plugins/obs-filters/data/locale/fr-FR.ini index 38855f7..608d8aa 100644 --- a/plugins/obs-filters/data/locale/fr-FR.ini +++ b/plugins/obs-filters/data/locale/fr-FR.ini @@ -1,17 +1,18 @@ ColorFilter="Corrections colorimétrique" -ColorGradeFilter="Appliquer LUT" +ColorGradeFilter="Appliquer un LUT" MaskFilter="Masque d'image/mélange" -AsyncDelayFilter="Retard vidéo (async.)" +AsyncDelayFilter="Retard vidéo (asynchrone)" CropFilter="Rogner / Encadrer" ScrollFilter="Défilement" -ChromaKeyFilter="Clé chromatique" -ColorKeyFilter="Couleur d'incrustation" +ChromaKeyFilter="Incrustation par chrominance (Chroma Key)" +ColorKeyFilter="Incrustation par couleur" SharpnessFilter="Accentuer" -ScaleFilter="Mise à l’échelle / Ratio d'affichage" +ScaleFilter="Mise à l’échelle / Rapport d'affichage" GPUDelayFilter="Délai de rendu" UndistortCenter="Ne pas déformer le centre de l'image lors d'une mise à l'échelle ultra large" NoiseGate="Noise Gate" NoiseSuppress="Suppression du bruit" +InvertPolarity="Inverser la polarité" Gain="Gain" DelayMs="Retard (en millisecondes)" Type="Type " @@ -26,7 +27,7 @@ Opacity="Opacité" Contrast="Contraste" Brightness="Luminosité" Gamma="Gamma" -BrowsePath.Images="Tous les fichiers images" +BrowsePath.Images="Tous les fichiers Image" BrowsePath.AllFiles="Tous les fichiers" KeyColorType="Type de couleur-clé" KeyColor="Couleur-clé" @@ -53,16 +54,18 @@ NoiseGate.OpenThreshold="Seuil d'ouverture (dB)" NoiseGate.CloseThreshold="Seuil de fermeture (dB)" NoiseGate.AttackTime="Temps d'attaque (millisecondes)" NoiseGate.HoldTime="Temps de maintien (millisecondes)" -NoiseGate.ReleaseTime="Temps d'arrêt (millisecondes)" +NoiseGate.ReleaseTime="Temps de relâche (ou \"release\" en millisecondes)" Gain.GainDB="Gain (dB)" StretchImage="Étirer l'Image (ignorer ses proportions)" Resolution="Résolution" +Base.Canvas="Résolution de base (canevas)" None="Aucune" ScaleFiltering="Échelle de filtrage" ScaleFiltering.Point="Point" ScaleFiltering.Bilinear="Bilinéaire" ScaleFiltering.Bicubic="Bicubique" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Zone" NoiseSuppress.SuppressLevel="Seuil de suppression (en dB)" Saturation="Saturation" HueShift="Décalage de teinte" @@ -71,7 +74,28 @@ Compressor="Compresseur" Compressor.Ratio="Ratio (X:1)" Compressor.Threshold="Seuil (dB)" Compressor.AttackTime="Attaque (ms)" -Compressor.ReleaseTime="Libération (ms)" -Compressor.OutputGain="Sortie Gain (dB)" -Compressor.SidechainSource="Source Sidechain/Ducking" +Compressor.ReleaseTime="Relâchement (ou \"release\" en ms)" +Compressor.OutputGain="Gain en Sortie (dB)" +Compressor.SidechainSource="Source pour la Sidechain/Ducking" +Limiter="Limiteur" +Limiter.Threshold="Seuil (dB)" +Limiter.ReleaseTime="Relâchement (\"release\" en ms)" +Expander="Expandeur" +Expander.Ratio="Ratio (X:1)" +Expander.Threshold="Seuil (dB)" +Expander.AttackTime="Attaque (ms)" +Expander.ReleaseTime="Relâchement (\"release\" en ms)" +Expander.OutputGain="Gain en sortie (dB)" +Expander.Detector="Détection" +Expander.RMS="RMS" +Expander.Peak="Crête" +Expander.None="Aucune" +Expander.Presets="Pré-réglages" +Expander.Presets.Expander="Expandeur" +Expander.Presets.Gate="Gate" +LumaKeyFilter="Incrustation par luminance (Luma Key)" +Luma.LumaMax="Luminance max." +Luma.LumaMin="Luminance min." +Luma.LumaMaxSmooth="Adoucissement luminance max." +Luma.LumaMinSmooth="Adoucissement luminance min." diff --git a/plugins/obs-filters/data/locale/hu-HU.ini b/plugins/obs-filters/data/locale/hu-HU.ini index 32e9b79..6b4478e 100644 --- a/plugins/obs-filters/data/locale/hu-HU.ini +++ b/plugins/obs-filters/data/locale/hu-HU.ini @@ -12,6 +12,7 @@ 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" +InvertPolarity="Polaritás megfordítása" Gain="Erősítés" DelayMs="Késleltetés (ezredmásodperc)" Type="Típus" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Felengedés ideje (ezredmásodperc)" Gain.GainDB="Erősítés (dB)" StretchImage="Kép nyújtása (képarány elvetésével)" Resolution="Felbontás" +Base.Canvas="Alap (Vászon) felbontás" None="Nincs" ScaleFiltering="Skála-szűrés" ScaleFiltering.Point="Pont" ScaleFiltering.Bilinear="Bilineáris" ScaleFiltering.Bicubic="Kettős köbös" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Terület" NoiseSuppress.SuppressLevel="Csökkentési szint (dB)" Saturation="Telítettség" HueShift="Színezet váltása" @@ -74,4 +77,25 @@ Compressor.AttackTime="Aktiválás (ms)" Compressor.ReleaseTime="Felengedés (ms)" Compressor.OutputGain="Kimeneti erősítés (dB)" Compressor.SidechainSource="Oldallánc/Buktatott forrás" +Limiter="Limiter" +Limiter.Threshold="Küszöb (dB)" +Limiter.ReleaseTime="Kiadás (ms)" +Expander="Expander" +Expander.Ratio="Arány (X:1)" +Expander.Threshold="Küszöb (dB)" +Expander.AttackTime="Aktiválás (ms)" +Expander.ReleaseTime="Felengedés (ms)" +Expander.OutputGain="Kimeneti erősítés (dB)" +Expander.Detector="Észlelés" +Expander.RMS="RMS" +Expander.Peak="Csúcs" +Expander.None="Nincs" +Expander.Presets="Készletek" +Expander.Presets.Expander="Expander" +Expander.Presets.Gate="Kapu" +LumaKeyFilter="Luma kulcs" +Luma.LumaMax="Luma maximum" +Luma.LumaMin="Luma minimum" +Luma.LumaMaxSmooth="Luma maximum simított" +Luma.LumaMinSmooth="Luma minimum simított" diff --git a/plugins/obs-filters/data/locale/it-IT.ini b/plugins/obs-filters/data/locale/it-IT.ini index 1c83914..542a9c0 100644 --- a/plugins/obs-filters/data/locale/it-IT.ini +++ b/plugins/obs-filters/data/locale/it-IT.ini @@ -1,77 +1,101 @@ -ColorFilter="Correzione colore" +ColorFilter="Correzione del colore" ColorGradeFilter="Applica LUT" -MaskFilter="Immagine maschera/miscela" +MaskFilter="Maschera/miscela l'immagine" AsyncDelayFilter="Ritardo video (Asincrono)" -CropFilter="Crop/Pad" +CropFilter="Ritaglia/aggiungi una cornice" ScrollFilter="Scorrimento" -ChromaKeyFilter="Chroma Key" -ColorKeyFilter="Chiave Colore" +ChromaKeyFilter="Chiave chroma" +ColorKeyFilter="Chiave colore" SharpnessFilter="Nitidizza" -ScaleFilter="Ridimensionamento/Aspect Ratio" -GPUDelayFilter="Ritardo di rendering" -UndistortCenter="Rimuovi distorsione del centro immagine quando si scala da un rapporto molto alto" +ScaleFilter="Ridimensionamento/proporzioni" +GPUDelayFilter="Ritardo del rendering" +UndistortCenter="Rimuovi la distorsione del centro immagine quando si scala da un rapporto dello schermo ultrawide" NoiseGate="Sensibilità dell'ingresso" -NoiseSuppress="Soppressione rumore" -Gain="Incremento" -DelayMs="Ritardo (millisecondi)" +NoiseSuppress="Riduzione del rumore" +InvertPolarity="Inverti la polarità" +Gain="Guadagno" +DelayMs="Ritardo (in millisecondi)" Type="Tipo" MaskBlendType.MaskColor="Maschera alfa (canale di colore)" -MaskBlendType.MaskAlpha="Maschera alfa (Canale alpha)" +MaskBlendType.MaskAlpha="Maschera alfa (canale alfa)" MaskBlendType.BlendMultiply="Miscela (moltiplica)" -MaskBlendType.BlendAddition="Miscela (aggiunta)" -MaskBlendType.BlendSubtraction="Miscela (sottrazione)" +MaskBlendType.BlendAddition="Miscela (additiva)" +MaskBlendType.BlendSubtraction="Miscela (sottrattiva)" Path="Percorso" Color="Colore" Opacity="Opacità" Contrast="Contrasto" Brightness="Luminosità" Gamma="Gamma" -BrowsePath.Images="Tutti i file di immagine" +BrowsePath.Images="Tutti i file immagine" BrowsePath.AllFiles="Tutti i file" -KeyColorType="Tipo di Color Key" -KeyColor="Key Color" +KeyColorType="Tipo di chiave colore" +KeyColor="Chiave colore" Similarity="Somiglianza (1-1000)" -Smoothness="Scorrevolezza (1-1000)" -ColorSpillReduction="Key Color Spill Reduction (1-1000)" +Smoothness="Finezza (1-1000)" +ColorSpillReduction="Riduzione della sbavatura della chiave colore (1-1000)" Crop.Left="A sinistra" Crop.Right="A destra" Crop.Top="In alto" -Crop.Bottom="In Basso" +Crop.Bottom="In basso" Crop.Width="Larghezza" Crop.Height="Altezza" Crop.Relative="Relativo" ScrollFilter.SpeedX="Velocità orizzontale" ScrollFilter.SpeedY="Velocità verticale" -ScrollFilter.LimitWidth="Limite larghezza" -ScrollFilter.LimitHeight="Limite altezza" -CustomColor="Personalizza colore" +ScrollFilter.LimitWidth="Limite della larghezza" +ScrollFilter.LimitHeight="Limite dell'altezza" +CustomColor="Colore personalizzato" Red="Rosso" Green="Verde" Blue="Blu" Magenta="Magenta" -NoiseGate.OpenThreshold="Apri soglia (dB)" -NoiseGate.CloseThreshold="Chiudo soglia (dB)" -NoiseGate.AttackTime="Tempo d'inizio (millisecondi)" -NoiseGate.HoldTime="Tempo d'attesa (millisecondi)" -NoiseGate.ReleaseTime="Tempo di rilascio (millisecondi)" -Gain.GainDB="Incremento (dB)" -StretchImage="Stendi immagine (scarta proporzioni immagine)" +NoiseGate.OpenThreshold="Soglia di apertura (in dB)" +NoiseGate.CloseThreshold="Soglia di chiusura (in dB)" +NoiseGate.AttackTime="Tempo di attivazione (in millisecondi)" +NoiseGate.HoldTime="Tempo di attesa (in millisecondi)" +NoiseGate.ReleaseTime="Tempo di rilascio (in millisecondi)" +Gain.GainDB="Guadagno (in dB)" +StretchImage="Adatta l'immagine allo schermo (ignorando le proporzioni)" Resolution="Risoluzione" -None="Nessuno" +Base.Canvas="Risoluzione di base (inquadratura)" +None="Nessuna" ScaleFiltering="Scala di filtraggio" ScaleFiltering.Point="Punto" ScaleFiltering.Bilinear="Bilineare" ScaleFiltering.Bicubic="Bicubico" ScaleFiltering.Lanczos="Lanczos" -NoiseSuppress.SuppressLevel="Livello di soppressione (dB)" +ScaleFiltering.Area="Zona" +NoiseSuppress.SuppressLevel="Livello di riduzione (in 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)" -Compressor.SidechainSource="Sidechain/Ducking Sorgente" +Compressor.Ratio="Rapporto/proporzioni (X:1)" +Compressor.Threshold="Soglia (in dB)" +Compressor.AttackTime="Attivazione (in ms)" +Compressor.ReleaseTime="Rilascio (in ms)" +Compressor.OutputGain="Guadagno in uscita (in dB)" +Compressor.SidechainSource="Fonte per sidechain/ducking" +Limiter="Limitatore" +Limiter.Threshold="Soglia (in dB)" +Limiter.ReleaseTime="Rilascio (in ms)" +Expander="Espansore" +Expander.Ratio="Rapporto/proporzioni (X:1)" +Expander.Threshold="Soglia (in dB)" +Expander.AttackTime="Attivazione (in ms)" +Expander.ReleaseTime="Rilascio (in ms)" +Expander.OutputGain="Guadagno in uscita (in dB)" +Expander.Detector="Tipo di rilevamento" +Expander.RMS="RMS" +Expander.Peak="Picco" +Expander.None="Nessuno" +Expander.Presets="Preset" +Expander.Presets.Expander="Espansore" +Expander.Presets.Gate="Sensibilità" +LumaKeyFilter="Chiave Luma" +Luma.LumaMax="Luma massima" +Luma.LumaMin="Luma minima" +Luma.LumaMaxSmooth="Sfumatura massima di Luma" +Luma.LumaMinSmooth="Sfumatura minima di Luma" diff --git a/plugins/obs-filters/data/locale/ja-JP.ini b/plugins/obs-filters/data/locale/ja-JP.ini index 1dd1194..ae19476 100644 --- a/plugins/obs-filters/data/locale/ja-JP.ini +++ b/plugins/obs-filters/data/locale/ja-JP.ini @@ -12,6 +12,7 @@ GPUDelayFilter="レンダリング遅延" UndistortCenter="超広角からスケーリングするときに画像の中心を歪めない" NoiseGate="ノイズゲート" NoiseSuppress="ノイズ抑制" +InvertPolarity="極性を反転する" Gain="ゲイン" DelayMs="遅延時間 (ミリ秒)" Type="種別" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="解除時間 (ミリ秒)" Gain.GainDB="ゲイン (dB)" StretchImage="画像を拡大 (アスペクト比を破棄)" Resolution="解像度" +Base.Canvas="基本 (キャンバス) 解像度" None="なし" ScaleFiltering="スケールフィルタ" ScaleFiltering.Point="ポイント" ScaleFiltering.Bilinear="バイリニア" ScaleFiltering.Bicubic="バイキュービック" ScaleFiltering.Lanczos="ランチョス" +ScaleFiltering.Area="エリア" NoiseSuppress.SuppressLevel="抑制レベル (dB)" Saturation="彩度" HueShift="色相シフト" @@ -74,4 +77,25 @@ Compressor.AttackTime="アタックタイム (ms)" Compressor.ReleaseTime="リリースタイム (ms)" Compressor.OutputGain="出力ゲイン (dB)" Compressor.SidechainSource="サイドチェーン/ダッキングソース" +Limiter="リミッター" +Limiter.Threshold="閾値 (dB)" +Limiter.ReleaseTime="リリースタイム (ms)" +Expander="エキスパンダー" +Expander.Ratio="比率 (X:1)" +Expander.Threshold="閾値 (dB)" +Expander.AttackTime="アタックタイム (ms)" +Expander.ReleaseTime="リリースタイム (ms)" +Expander.OutputGain="出力ゲイン (dB)" +Expander.Detector="検出" +Expander.RMS="RMS" +Expander.Peak="ピーク" +Expander.None="未設定" +Expander.Presets="プリセット" +Expander.Presets.Expander="エキスパンダー" +Expander.Presets.Gate="ゲート" +LumaKeyFilter="ルマキー" +Luma.LumaMax="最大輝度" +Luma.LumaMin="最小輝度" +Luma.LumaMaxSmooth="ルマ最大スムーズ" +Luma.LumaMinSmooth="ルマ最小スムーズ" diff --git a/plugins/obs-filters/data/locale/ka-GE.ini b/plugins/obs-filters/data/locale/ka-GE.ini index 7547556..b8a465c 100644 --- a/plugins/obs-filters/data/locale/ka-GE.ini +++ b/plugins/obs-filters/data/locale/ka-GE.ini @@ -12,6 +12,7 @@ GPUDelayFilter="დაყოვნება დამუშავებისა UndistortCenter="ზეფართო სურათის შუაგულის გამრუდების არიდება, ზომების შეცვლისას" NoiseGate="ხმაურის შეზღუდვა" NoiseSuppress="ხმაურის დახშობა" +InvertPolarity="შებრუნებული პოლარობა" Gain="სიგნალის გაძლიერება" DelayMs="დაყოვნება (მილიწამი)" Type="სახეობა" @@ -32,7 +33,7 @@ KeyColorType="საკვანძო ფერის სახე" KeyColor="საკვანძო ფერი" Similarity="მსგავსება (1-1000)" Smoothness="სიგლუვე (1-1000)" -ColorSpillReduction="საკვანძო ფერთა გაბნევის შემცირება (1-1000)" +ColorSpillReduction="საკვანძო ფერის გაბნევის შემცირება (1-1000)" Crop.Left="მარცხენა" Crop.Right="მარჯვენა" Crop.Top="ზედა" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="შემცირების (Release) ხანგრ Gain.GainDB="გაძლიერება (dB)" StretchImage="სურათის გაწელვა (გვერდების თანაფარდობის უგულებელყოფა)" Resolution="გაფართოება" +Base.Canvas="ძირითადი (ფონის) გაფართოება" None="არცერთი" ScaleFiltering="მასშტაბირების ფილტრი" ScaleFiltering.Point="წერტილოვანი" ScaleFiltering.Bilinear="ორხაზოვანი" ScaleFiltering.Bicubic="ბიკუბური" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="სივრცე" NoiseSuppress.SuppressLevel="დახშობის ხარისხი (dB)" Saturation="გაჯერებულობა" HueShift="შეფერილობის შეცვლა" @@ -74,4 +77,25 @@ Compressor.AttackTime="მომატება (მწ)" Compressor.ReleaseTime="შემცირება (მწ)" Compressor.OutputGain="გამომავალი სიგნალის გაძლიერება (dB)" Compressor.SidechainSource="Sidechain/ხმის დონის დადაბლების წყარო" +Limiter="შემზღუდველი" +Limiter.Threshold="ზღურბლი (dB)" +Limiter.ReleaseTime="შემცირება (მწ)" +Expander="გამშლელი" +Expander.Ratio="ფარდობა (X:1)" +Expander.Threshold="ზღურბლი (dB)" +Expander.AttackTime="მომატება (მწ)" +Expander.ReleaseTime="შემცირება (მწ)" +Expander.OutputGain="გამომავალი სიგნალის გაძლიერება (dB)" +Expander.Detector="დადგენა" +Expander.RMS="საშ. კვადრატული (RMS)" +Expander.Peak="ხანმოკლე" +Expander.None="არცერთი" +Expander.Presets="მზა პარამეტრები" +Expander.Presets.Expander="გამშლელი" +Expander.Presets.Gate="ჩამკეტი" +LumaKeyFilter="კაშკაშა არეების ჩანაცვლება (Luma Key)" +Luma.LumaMax="სიკაშკაშის ზედა ზღვარი" +Luma.LumaMin="სიკაშკაშის ქვედა ზღვარი" +Luma.LumaMaxSmooth="სიკაშკაშის ზედა ზღვრის სიგლუვე" +Luma.LumaMinSmooth="სიკაშკაშის ქვედა ზღვრის სიგლუვე" diff --git a/plugins/obs-filters/data/locale/ko-KR.ini b/plugins/obs-filters/data/locale/ko-KR.ini index e03702b..0241c1c 100644 --- a/plugins/obs-filters/data/locale/ko-KR.ini +++ b/plugins/obs-filters/data/locale/ko-KR.ini @@ -12,6 +12,7 @@ GPUDelayFilter="렌더링 지연" UndistortCenter="울트라와이드에서 크기조정 시 이미지 중앙의 왜곡을 수정" NoiseGate="노이즈 게이트" NoiseSuppress="소음 억제" +InvertPolarity="극성 반전" Gain="증폭" DelayMs="지연 (밀리초)" Type="형식" @@ -53,16 +54,18 @@ NoiseGate.OpenThreshold="개방 역치값 (dB)" NoiseGate.CloseThreshold="폐쇄 역치값 (dB)" NoiseGate.AttackTime="개방 준비 시간 (밀리세컨드)" NoiseGate.HoldTime="개방 유지 시간 (밀리세컨드)" -NoiseGate.ReleaseTime="폐쇄 준비 시간 (밀리세컨드)" +NoiseGate.ReleaseTime="폐쇄 준비 시간 (밀리세컨드)" Gain.GainDB="증폭 (dB)" StretchImage="이미지 늘리기 (이미지 가로 세로 비율 포기)" Resolution="해상도" +Base.Canvas="기본 (캔버스) 해상도" None="없음" ScaleFiltering="비율 필터링" ScaleFiltering.Point="점" ScaleFiltering.Bilinear="이중선형" ScaleFiltering.Bicubic="쌍삼차" ScaleFiltering.Lanczos="란초스" +ScaleFiltering.Area="영역" NoiseSuppress.SuppressLevel="억제 세기 (dB)" Saturation="채도" HueShift="색조 변화" @@ -74,4 +77,25 @@ Compressor.AttackTime="신호 감지 후 반응까지 걸리는 시간 (ms)" Compressor.ReleaseTime="신호 세기가 감퇴 이후 증폭이 회복하는 시간 (ms)" Compressor.OutputGain="출력 증폭 (dB)" Compressor.SidechainSource="사이드체인/더킹 소스" +Limiter="음성 제한" +Limiter.Threshold="임계값 (dB)" +Limiter.ReleaseTime="해제 (ms)" +Expander="확장기" +Expander.Ratio="비율 (X:1)" +Expander.Threshold="임계값 (dB)" +Expander.AttackTime="반응 시간 (ms)" +Expander.ReleaseTime="해제 (ms)" +Expander.OutputGain="출력 증폭 (dB)" +Expander.Detector="측정 감지" +Expander.RMS="RMS" +Expander.Peak="최고조" +Expander.None="없음" +Expander.Presets="사전 설정" +Expander.Presets.Expander="확장기" +Expander.Presets.Gate="게이트" +LumaKeyFilter="루마 키" +Luma.LumaMax="루마 최대값" +Luma.LumaMin="루마 최소값" +Luma.LumaMaxSmooth="루마 스무스 최대값" +Luma.LumaMinSmooth="루마 스무스 최소값" diff --git a/plugins/obs-filters/data/locale/nb-NO.ini b/plugins/obs-filters/data/locale/nb-NO.ini index b7c7979..22d2f8f 100644 --- a/plugins/obs-filters/data/locale/nb-NO.ini +++ b/plugins/obs-filters/data/locale/nb-NO.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Rendringsforsinkelse" UndistortCenter="Fjern forstyrring av bildets midtområde, når det skaleres ned fra ultrabredhet" NoiseGate="Støyterskel" NoiseSuppress="Lyddemping" +InvertPolarity="Inverter polaritet" Gain="Forsterkning" DelayMs="Forsinkelse (millisekunder)" Type="Type" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Løslatelsestid (millisekunder)" Gain.GainDB="Forsterkning (dB)" StretchImage="Strekk bilde (ignorer bildets sideforhold)" Resolution="Oppløsning" +Base.Canvas="Grunnoppløsning (lerret)" None="Ingen" ScaleFiltering="Skala Filtrering" ScaleFiltering.Point="Punkt" ScaleFiltering.Bilinear="Bilineær" ScaleFiltering.Bicubic="Bikubisk" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Område" NoiseSuppress.SuppressLevel="Dempelse Nivå (dB)" Saturation="Metning" HueShift="Fargetone Skifte" @@ -74,4 +77,25 @@ Compressor.AttackTime="Angrep (ms)" Compressor.ReleaseTime="Slipp (ms)" Compressor.OutputGain="Utdataforsterkning (dB)" Compressor.SidechainSource="Lydduppe-kilde" +Limiter="Begrenser" +Limiter.Threshold="Terskel (dB)" +Limiter.ReleaseTime="Slipp (ms)" +Expander="Utvider" +Expander.Ratio="Forhold (X:1)" +Expander.Threshold="Terskel (dB)" +Expander.AttackTime="Responstid (ms)" +Expander.ReleaseTime="Slipp (ms)" +Expander.OutputGain="Utdataforsterkning (dB)" +Expander.Detector="Identifisering" +Expander.RMS="RMS" +Expander.Peak="Topp" +Expander.None="Ingen" +Expander.Presets="Forhåndsinnstillinger" +Expander.Presets.Expander="Utvider" +Expander.Presets.Gate="Port" +LumaKeyFilter="Luma Key" +Luma.LumaMax="Luma maks." +Luma.LumaMin="Luma min." +Luma.LumaMaxSmooth="Luma maks. glidende" +Luma.LumaMinSmooth="Luma min. glidende" diff --git a/plugins/obs-filters/data/locale/nl-NL.ini b/plugins/obs-filters/data/locale/nl-NL.ini index 386a24b..4773aad 100644 --- a/plugins/obs-filters/data/locale/nl-NL.ini +++ b/plugins/obs-filters/data/locale/nl-NL.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Rendervertraging" UndistortCenter="Verbeter beeldverhouding in het midden van bij schalen vanaf ultrawide" NoiseGate="Noise Gate" NoiseSuppress="Ruisonderdrukking" +InvertPolarity="Polariteit omkeren" Gain="Gain" DelayMs="Vertraging (milliseconden)" Type="Type" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Release-tijd (milliseconden)" Gain.GainDB="Gain (dB)" StretchImage="Afbeelding uitrekken (negeer beeldverhouding van de afbeelding)" Resolution="Resolutie" +Base.Canvas="Basisresolutie (Canvas)" None="Geen" ScaleFiltering="Schaal-filter" ScaleFiltering.Point="Point" ScaleFiltering.Bilinear="Bilinear" ScaleFiltering.Bicubic="Bicubic" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Gebied" NoiseSuppress.SuppressLevel="Onderdrukkingsniveau (dB)" Saturation="Verzadiging" HueShift="Tintverschuiving" @@ -74,4 +77,20 @@ Compressor.AttackTime="Attack (ms)" Compressor.ReleaseTime="Release (ms)" Compressor.OutputGain="Uitvoergain (dB)" Compressor.SidechainSource="Sidechain/Ducking Bron" +Limiter="Begrenzer" +Limiter.Threshold="Drempel (dB)" +Limiter.ReleaseTime="Vrijgave (ms)" +Expander="Uitbreiding" +Expander.Ratio="Verhouding (X:1)" +Expander.Threshold="Drempel (dB)" +Expander.AttackTime="Aanval (ms)" +Expander.ReleaseTime="Vrijgave (ms)" +Expander.OutputGain="Uitvoer versterking (dB)" +Expander.Detector="Detectie" +Expander.RMS="RMS" +Expander.Peak="Piek" +Expander.None="Geen" +Expander.Presets="Vooraf ingestelde instellingen" +Expander.Presets.Expander="Uitbreiding" +Expander.Presets.Gate="Hek" diff --git a/plugins/obs-filters/data/locale/pl-PL.ini b/plugins/obs-filters/data/locale/pl-PL.ini index 05fcefb..f491c19 100644 --- a/plugins/obs-filters/data/locale/pl-PL.ini +++ b/plugins/obs-filters/data/locale/pl-PL.ini @@ -12,6 +12,7 @@ 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" +InvertPolarity="Odwrócenie polaryzacji" Gain="Poziom" DelayMs="Opóźnienie (milisekundy)" Type="Typ" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Czas zwolnienia (milisekundy)" Gain.GainDB="Poziom (dB)" StretchImage="Rozciągnięcie obrazu (ignoruj proporcje)" Resolution="Rozdzielczość" +Base.Canvas="Rozdzielczość bazowa (obraz)" None="Brak" ScaleFiltering="Filtrowanie" ScaleFiltering.Point="Punktowe" ScaleFiltering.Bilinear="Dwuliniowe" ScaleFiltering.Bicubic="Dwusześcienne" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Obszar" NoiseSuppress.SuppressLevel="Poziom tłumienia (dB)" Saturation="Nasycenie" HueShift="Przesunięcie barwy" @@ -74,4 +77,25 @@ Compressor.AttackTime="Atak (ms)" Compressor.ReleaseTime="Odpuszczenie (ms)" Compressor.OutputGain="Zysk na wyjściu (dB)" Compressor.SidechainSource="Źródło poboczne" +Limiter="Limiter" +Limiter.Threshold="Próg odcięcia (dB)" +Limiter.ReleaseTime="Czas reakcji (ms)" +Expander="Expander" +Expander.Ratio="Stosunek (X:1)" +Expander.Threshold="Próg (dB)" +Expander.AttackTime="Aktywacja (ms)" +Expander.ReleaseTime="Odpuszczenie (ms)" +Expander.OutputGain="Zysk na wyjściu (dB)" +Expander.Detector="Wykrywanie" +Expander.RMS="RMS" +Expander.Peak="Punkt szczytowy" +Expander.None="Brak" +Expander.Presets="Predefiniowane" +Expander.Presets.Expander="Expander" +Expander.Presets.Gate="Brama" +LumaKeyFilter="Kluczowanie luma key" +Luma.LumaMax="Luma Max" +Luma.LumaMin="Luma Min" +Luma.LumaMaxSmooth="Luma wygładzanie max" +Luma.LumaMinSmooth="Luma wygładzanie min" diff --git a/plugins/obs-filters/data/locale/pt-BR.ini b/plugins/obs-filters/data/locale/pt-BR.ini index 886dd2a..8cbee2a 100644 --- a/plugins/obs-filters/data/locale/pt-BR.ini +++ b/plugins/obs-filters/data/locale/pt-BR.ini @@ -12,6 +12,7 @@ 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" +InvertPolarity="Inverter Polaridade" Gain="Ganho" DelayMs="Atraso (milissegundos)" Type="Tipo" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Tempo de liberação (milissegundos)" Gain.GainDB="Ganho (dB)" StretchImage="Esticar a Imagem (descartar aspecto da imagem)" Resolution="Resolução" +Base.Canvas="Resolução Base (Tela)" None="Nenhum" ScaleFiltering="Filtragem de escala" ScaleFiltering.Point="Ponto" ScaleFiltering.Bilinear="Bilinear" ScaleFiltering.Bicubic="Bicúbico" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Área" NoiseSuppress.SuppressLevel="Nível de redução (dB)" Saturation="Saturação" HueShift="Alteração de matiz" @@ -74,4 +77,25 @@ Compressor.AttackTime="Ataque (ms)" Compressor.ReleaseTime="Liberação (ms)" Compressor.OutputGain="Ganho na saída (dB)" Compressor.SidechainSource="Fonte de Cadeia Lateral/Oscilação de Áudio" +Limiter="Limitador" +Limiter.Threshold="Limiar (dB)" +Limiter.ReleaseTime="Liberação (ms)" +Expander="Expansor" +Expander.Ratio="Razão (X:1)" +Expander.Threshold="Limiar (dB)" +Expander.AttackTime="Ataque (ms)" +Expander.ReleaseTime="Liberação (ms)" +Expander.OutputGain="Ganho na Saída (dB)" +Expander.Detector="Detecção" +Expander.RMS="RMS" +Expander.Peak="Pico" +Expander.None="Nenhuma" +Expander.Presets="Predefinições" +Expander.Presets.Expander="Expansor" +Expander.Presets.Gate="Portão" +LumaKeyFilter="Luma Key" +Luma.LumaMax="Luminância Máxima" +Luma.LumaMin="Luminância Mínima" +Luma.LumaMaxSmooth="Suavização da Luminância Máxima" +Luma.LumaMinSmooth="Suavização da Luminância Mínima" diff --git a/plugins/obs-filters/data/locale/pt-PT.ini b/plugins/obs-filters/data/locale/pt-PT.ini index 8a2f817..8c5b5cb 100644 --- a/plugins/obs-filters/data/locale/pt-PT.ini +++ b/plugins/obs-filters/data/locale/pt-PT.ini @@ -1,4 +1,5 @@ ColorFilter="Correção de cor" +ColorGradeFilter="Aplicar LUT" MaskFilter="Máscara/mistura de imagem" AsyncDelayFilter="Atraso de vídeo (Async)" ScrollFilter="Percorre" @@ -6,6 +7,7 @@ ChromaKeyFilter="Chroma Key" ColorKeyFilter="Color Key" SharpnessFilter="Nitidez" NoiseGate="Filtro de ruído" +InvertPolarity="Inverter Polaridade" Gain="Ganho" DelayMs="Atraso (milissegundos)" Type="Topo" @@ -50,4 +52,32 @@ NoiseGate.HoldTime="Tempo de bloqueio (milissegundos)" NoiseGate.ReleaseTime="Tempo de libertação (milissegundos)" Gain.GainDB="Ganho (dB)" StretchImage="Esticar a imagem (relação de aspeto de imagem de descarte)" +Resolution="Resolução" +None="Nenhum" +NoiseSuppress.SuppressLevel="Nível de Supressão (dB)" +Saturation="Saturação" +Amount="Montante" +Compressor="Compressor" +Compressor.Ratio="Relação (X:1)" +Compressor.Threshold="Limiar (dB)" +Compressor.AttackTime="Ataque (ms)" +Compressor.ReleaseTime="Liberação (ms)" +Compressor.OutputGain="Ganho de saída (dB)" +Compressor.SidechainSource="Sidechain/Ducking Source" +Limiter="Limitador" +Limiter.Threshold="Limiar (dB)" +Limiter.ReleaseTime="Release (ms)" +Expander="Expansor" +Expander.Ratio="Relação (X:1)" +Expander.Threshold="Limiar (dB)" +Expander.AttackTime="Ataque (ms)" +Expander.ReleaseTime="Liberação (ms)" +Expander.OutputGain="Ganho de saída (dB)" +Expander.Detector="Deteção" +Expander.RMS="RMS" +Expander.Peak="Pico" +Expander.None="Nenhum" +Expander.Presets="Predefinições" +Expander.Presets.Expander="Expansor" +Expander.Presets.Gate="Portão" diff --git a/plugins/obs-filters/data/locale/ro-RO.ini b/plugins/obs-filters/data/locale/ro-RO.ini index ee175a8..f677080 100644 --- a/plugins/obs-filters/data/locale/ro-RO.ini +++ b/plugins/obs-filters/data/locale/ro-RO.ini @@ -7,6 +7,7 @@ ColorKeyFilter="Culoare cheie" SharpnessFilter="Accentuare" ScaleFilter="Scalare/Rație Aspect" NoiseGate="Poartă de zgomot" +InvertPolarity="Inversează polaritatea" Gain="Amplificare" DelayMs="Întârziere (milisecunde)" Type="Tip" @@ -26,7 +27,7 @@ BrowsePath.AllFiles="Toate fișierele" KeyColorType="Tipul culorii cheie" KeyColor="Culoare cheie" Similarity="Similaritate (1-100)" -Smoothness="Netezire (1-1000)" +Smoothness="Netezime (1-1000)" ColorSpillReduction="Reducere pentru devărsarea culorii cheie (1-1000)" Crop.Left="Stânga" Crop.Right="Dreapta" @@ -52,9 +53,28 @@ NoiseGate.ReleaseTime="Timp de eliberare (milisecunde)" Gain.GainDB="Amplificare (dB)" StretchImage="Întinde imaginea (renunță la raportul de aspect al imaginii)" Resolution="Rezoluție" +Base.Canvas="Rezoluție (planșă) de bază" None="Fără" ScaleFiltering.Bilinear="Biliniar" ScaleFiltering.Bicubic="Bicubic" ScaleFiltering.Lanczos="Lanczos" Saturation="Saturație" +Compressor="Compresor" +Compressor.Ratio="Raport (X:1)" +Compressor.Threshold="Prag (dB)" +Compressor.AttackTime="Atacare (ms)" +Compressor.ReleaseTime="Eliberare (ms)" +Compressor.OutputGain="Amplificare pentru ieșire (dB)" +Limiter="Limitor" +Limiter.Threshold="Prag (dB)" +Limiter.ReleaseTime="Eliberare (ms)" +Expander.Ratio="Raport (X:1)" +Expander.Threshold="Prag (dB)" +Expander.OutputGain="Amplificare pentru ieșire (dB)" +Expander.Detector="Detectare" +Expander.RMS="RMS" +Expander.Peak="Valoare de vârf" +Expander.None="Niciuna" +Expander.Presets="Presetări" +Expander.Presets.Expander="Expandor" diff --git a/plugins/obs-filters/data/locale/ru-RU.ini b/plugins/obs-filters/data/locale/ru-RU.ini index 1fe92ef..e56c70d 100644 --- a/plugins/obs-filters/data/locale/ru-RU.ini +++ b/plugins/obs-filters/data/locale/ru-RU.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Задержка отображения" UndistortCenter="Не искривлять центр изображения при масштабировании Ultrawide разрешения" NoiseGate="Пропускной уровень шума" NoiseSuppress="Шумоподавление" +InvertPolarity="Инвертировать полярность" Gain="Усиление" DelayMs="Задержка (миллисекунд)" Type="Тип" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Длительность затухания (миллис Gain.GainDB="Усиление (дБ)" StretchImage="Растянуть изображение (игнорировать пропорции изображения)" Resolution="Разрешение" +Base.Canvas="Базовое (основное) разрешение" None="Нет" ScaleFiltering="Фильтр масштабирования" ScaleFiltering.Point="Точечный" ScaleFiltering.Bilinear="Билинейный" ScaleFiltering.Bicubic="Бикубический" ScaleFiltering.Lanczos="Метод Ланцоша" +ScaleFiltering.Area="Область" NoiseSuppress.SuppressLevel="Уровень подавления (дБ)" Saturation="Насыщенность" HueShift="Сдвиг оттенка" @@ -74,4 +77,25 @@ Compressor.AttackTime="Атака (мс)" Compressor.ReleaseTime="Спад (мс)" Compressor.OutputGain="Выходное усиление (дБ)" Compressor.SidechainSource="Источник приглушения/сайдчейн-компрессии" +Limiter="Лимитер" +Limiter.Threshold="Порог срабатывания (дБ)" +Limiter.ReleaseTime="Восстановление (мс)" +Expander="Экспандер" +Expander.Ratio="Соотношение (X:1)" +Expander.Threshold="Порог срабатывания (дБ)" +Expander.AttackTime="Атака (мс)" +Expander.ReleaseTime="Восстановление (мс)" +Expander.OutputGain="Выходное усиление (дБ)" +Expander.Detector="Обнаружение" +Expander.RMS="RMS" +Expander.Peak="Пик" +Expander.None="Нет" +Expander.Presets="Предустановки" +Expander.Presets.Expander="Экспандер" +Expander.Presets.Gate="Гейт" +LumaKeyFilter="Яркостный ключ" +Luma.LumaMax="Макс. яркость" +Luma.LumaMin="Мин. яркость" +Luma.LumaMaxSmooth="Сглаживание макс. яркости" +Luma.LumaMinSmooth="Сглаживание мин. яркости" diff --git a/plugins/obs-filters/data/locale/sk-SK.ini b/plugins/obs-filters/data/locale/sk-SK.ini index d9ddbdf..161f4db 100644 --- a/plugins/obs-filters/data/locale/sk-SK.ini +++ b/plugins/obs-filters/data/locale/sk-SK.ini @@ -1,15 +1,26 @@ ColorFilter="Korekcia farieb" ColorGradeFilter="Použiť LUT" +MaskFilter="Maska/miešanie obrazu" AsyncDelayFilter="Oneskorenie videa (Async)" CropFilter="Orezať/odsadiť" +ScrollFilter="Rolovanie" ChromaKeyFilter="Chroma Key" ColorKeyFilter="Farebný kľúč" SharpnessFilter="Zaostriť" ScaleFilter="Škálovanie/pomer strán" +GPUDelayFilter="Oneskorenie vykresľovania" +UndistortCenter="Odstrániť skreslenie pri škálovaní zo širokouhlého obrazu" +NoiseGate="Hluková brána" NoiseSuppress="Potlačenie šumu" +InvertPolarity="Invertovať polaritu" Gain="Zosilnenie" DelayMs="Oneskorenie (v milisekundách)" Type="Typ" +MaskBlendType.MaskColor="Maska priehľadnosti (kanál farieb)" +MaskBlendType.MaskAlpha="Maska priehľadnosti (kanál priehľadnosti)" +MaskBlendType.BlendMultiply="Zmiešať (násobenie)" +MaskBlendType.BlendAddition="Zmiešať (sčítanie)" +MaskBlendType.BlendSubtraction="Zmiešať (odčítanie)" Path="Cesta" Color="Farba" Opacity="Priehľadnosť" @@ -21,6 +32,8 @@ BrowsePath.AllFiles="Všetky súbory" KeyColorType="Typ kľúčovej farby" KeyColor="Kľúčová farba" Similarity="Podobnosť (1-1000)" +Smoothness="Hladkosť (1-1000)" +ColorSpillReduction="Redukcia color spill-u (1-1000)" Crop.Left="Vľavo" Crop.Right="Vpravo" Crop.Top="Hore" @@ -28,6 +41,8 @@ Crop.Bottom="Dole" Crop.Width="Šírka" Crop.Height="Výška" Crop.Relative="Relatívne" +ScrollFilter.SpeedX="Horizontálna rýchlosť" +ScrollFilter.SpeedY="Vertikálna rýchlosť" ScrollFilter.LimitWidth="Obmedziť šírku" ScrollFilter.LimitHeight="Obmedziť výšku" CustomColor="Vlastná farba" @@ -37,16 +52,27 @@ Blue="Modrá" Magenta="Purpurová" NoiseGate.OpenThreshold="Hladina otvorenia (dB)" NoiseGate.CloseThreshold="Hladina zatvorenia (dB)" +NoiseGate.AttackTime="Čas nástupu (milisekundy)" +NoiseGate.HoldTime="Čas podržania (milisekundy)" +NoiseGate.ReleaseTime="Čas uvoľnenia (milisekundy)" +Gain.GainDB="Zisk (dB)" +StretchImage="Roztiahnutie obrázka (pomer strán obrazu zahodiť)" Resolution="Rozlíšenie" +Base.Canvas="Základné (Plátnové) rozlíšenie" None="Žiadne" ScaleFiltering="Filtrovanie rozsahu" ScaleFiltering.Point="Bodové" ScaleFiltering.Bilinear="Bilineárne" ScaleFiltering.Bicubic="Bikubické" ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="Úroveň potlačenia (dB)" Saturation="Sýtosť" +HueShift="Posun odtieňa" Amount="Množstvo" Compressor="Kompresor" Compressor.Ratio="Pomer (X:1)" Compressor.Threshold="Prah (dB)" +Compressor.AttackTime="Nástup (ms)" +Compressor.ReleaseTime="Uvoľnenie (ms)" +Compressor.OutputGain="Zosilnenie výstupu (dB)" diff --git a/plugins/obs-filters/data/locale/sr-CS.ini b/plugins/obs-filters/data/locale/sr-CS.ini index 181a708..39cbc03 100644 --- a/plugins/obs-filters/data/locale/sr-CS.ini +++ b/plugins/obs-filters/data/locale/sr-CS.ini @@ -1,16 +1,20 @@ ColorFilter="Promena boja" +ColorGradeFilter="Primeni LUT" MaskFilter="Slika maske i stapanja" -AsyncDelayFilter="Video pauza (asinhrono)" +AsyncDelayFilter="Video kašnjenje (asinhrono)" CropFilter="Odsecanje/okvir" ScrollFilter="Pomeranje" ChromaKeyFilter="Ključ providnosti" ColorKeyFilter="Ključ boje" SharpnessFilter="Izoštravanje" ScaleFilter="Uvećanje/Odnos" +GPUDelayFilter="Kašnjenje u radu" +UndistortCenter="Ukloni distorziju centra slike tokom skaliranja sa ultra široke slike na normalnu" NoiseGate="Kapija šuma" NoiseSuppress="Suzbijanje šuma" +InvertPolarity="Invertuj polaritet" Gain="Pojačanje" -DelayMs="Pauza (milisekunde)" +DelayMs="Kašnjenje (milisekunde)" Type="Vrsta" MaskBlendType.MaskColor="Maska prozirnosti (kanal boje)" MaskBlendType.MaskAlpha="Maska prozirnosti (prozirni kanal)" @@ -54,6 +58,7 @@ NoiseGate.ReleaseTime="Vreme otpuštanja (milisekunde)" Gain.GainDB="Pojačanje (dB)" StretchImage="Istegni sliku (zanemari odnos visine i širine slike)" Resolution="Rezolucija" +Base.Canvas="Osnovna rezolucija" None="Nijedno" ScaleFiltering="Filter uvećanja" ScaleFiltering.Point="Tačka" @@ -61,4 +66,30 @@ ScaleFiltering.Bilinear="Bilinearno" ScaleFiltering.Bicubic="Bikubično" ScaleFiltering.Lanczos="Lankoz" NoiseSuppress.SuppressLevel="Nivo suzbijanja (dB)" +Saturation="Zasićenje" +HueShift="Promena nijanse" +Amount="Količina" +Compressor="Kompresor" +Compressor.Ratio="Razmera (X:1)" +Compressor.Threshold="Prag (dB)" +Compressor.AttackTime="Napad (ms)" +Compressor.ReleaseTime="Vreme otpuštanja (ms)" +Compressor.OutputGain="Pojačanje izlaza (dB)" +Compressor.SidechainSource="Sidechain/Ducking Izvor" +Limiter="Limiter" +Limiter.Threshold="Prag (dB)" +Limiter.ReleaseTime="Vreme otpuštanja (ms)" +Expander="Ekspander" +Expander.Ratio="Razmera (X:1)" +Expander.Threshold="Prag (dB)" +Expander.AttackTime="Napad (ms)" +Expander.ReleaseTime="Vreme otpuštanja (ms)" +Expander.OutputGain="Pojačanje izlaza (dB)" +Expander.Detector="Detekcija" +Expander.RMS="RMS" +Expander.Peak="Vrhunac (ekspander reaguje na kratke vrhunce)" +Expander.None="Bez" +Expander.Presets="Predefinisana podešavanja" +Expander.Presets.Expander="Ekspander" +Expander.Presets.Gate="Prolaz (eng. Gate)" diff --git a/plugins/obs-filters/data/locale/sr-SP.ini b/plugins/obs-filters/data/locale/sr-SP.ini index 0aa7561..c8ac575 100644 --- a/plugins/obs-filters/data/locale/sr-SP.ini +++ b/plugins/obs-filters/data/locale/sr-SP.ini @@ -1,16 +1,20 @@ ColorFilter="Промена боја" +ColorGradeFilter="Примени LUT" MaskFilter="Слика маске и стапања" -AsyncDelayFilter="Видео пауза (асинхроно)" +AsyncDelayFilter="Видео кашњење (асинхроно)" CropFilter="Одсецање/оквир" ScrollFilter="Померање" ChromaKeyFilter="Кључ провидности" ColorKeyFilter="Кључ боје" SharpnessFilter="Изоштравање" ScaleFilter="Увећање/однос" +GPUDelayFilter="Кашњење у раду" +UndistortCenter="Уклони дисторзију центра слике током скалирања са ултра широке слике на нормалну" NoiseGate="Капија шума" NoiseSuppress="Сузбијање шума" +InvertPolarity="Инвертуј поларитет" Gain="Појачање" -DelayMs="Пауза (милисекунде)" +DelayMs="Кашњење (милисекунде)" Type="Врста" MaskBlendType.MaskColor="Маска прозирности (канал боје)" MaskBlendType.MaskAlpha="Маска прозирности (прозирни канал)" @@ -54,6 +58,7 @@ NoiseGate.ReleaseTime="Време отпуштања (милисекунде)" Gain.GainDB="Појачање (dB)" StretchImage="Истегни слику (занемари однос висине и ширине слике)" Resolution="Резолуција" +Base.Canvas="Основна резолуција" None="Ниједно" ScaleFiltering="Филтер увећања" ScaleFiltering.Point="Тачка" @@ -61,4 +66,30 @@ ScaleFiltering.Bilinear="Билинеарно" ScaleFiltering.Bicubic="Бикубично" ScaleFiltering.Lanczos="Ланкоз" NoiseSuppress.SuppressLevel="Ниво сузбијања (dB)" +Saturation="Засићење" +HueShift="Промена нијансе" +Amount="Количина" +Compressor="Компресор" +Compressor.Ratio="Размера (X:1)" +Compressor.Threshold="Праг (dB)" +Compressor.AttackTime="Напад (ms)" +Compressor.ReleaseTime="Време отпуштања (ms)" +Compressor.OutputGain="Појачање излаза (dB)" +Compressor.SidechainSource="Sidechain/Ducking Извор" +Limiter="Лимитер" +Limiter.Threshold="Праг (dB)" +Limiter.ReleaseTime="Време отпуштања (ms)" +Expander="Експандер" +Expander.Ratio="Размера (X:1)" +Expander.Threshold="Праг (dB)" +Expander.AttackTime="Напад (ms)" +Expander.ReleaseTime="Време отпуштања (ms)" +Expander.OutputGain="Појачање излаза (dB)" +Expander.Detector="Детекција" +Expander.RMS="RMS" +Expander.Peak="Врхунац (експандер реагује на кратке врхунце)" +Expander.None="Без" +Expander.Presets="Предефинисана подешавања" +Expander.Presets.Expander="Експандер" +Expander.Presets.Gate="Пролаз (енг. Gate)" diff --git a/plugins/obs-filters/data/locale/sv-SE.ini b/plugins/obs-filters/data/locale/sv-SE.ini index 6562a77..0c8b2d5 100644 --- a/plugins/obs-filters/data/locale/sv-SE.ini +++ b/plugins/obs-filters/data/locale/sv-SE.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Renderingsfördröjning" UndistortCenter="Återställ bildens centrum vid skalning från ultrawide" NoiseGate="Brusblockering" NoiseSuppress="Brusreducering" +InvertPolarity="Invertera polaritet" Gain="Förstärkning" DelayMs="Fördröjning (millisekunder)" Type="Typ" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Släpptid (millisekunder)" Gain.GainDB="Förstärkning (dB)" StretchImage="Sträck bild (ignorera bildförhållandet)" Resolution="Upplösning" +Base.Canvas="Grundupplösning (kanvas)" None="Ingen" ScaleFiltering="Skalningsfiltrering" ScaleFiltering.Point="Punkt" ScaleFiltering.Bilinear="Bilinjär" ScaleFiltering.Bicubic="Bikubisk" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="Område" NoiseSuppress.SuppressLevel="Brusreduceringsnivå (dB)" Saturation="Mättnad" HueShift="Nyansväxling" @@ -74,4 +77,25 @@ Compressor.AttackTime="Attack (ms)" Compressor.ReleaseTime="Frigör (ms)" Compressor.OutputGain="Utmatningsförstärkning (dB)" Compressor.SidechainSource="Sidechain/Ducking-källa" +Limiter="Begränsare" +Limiter.Threshold="Tröskel (dB)" +Limiter.ReleaseTime="Frigör (ms)" +Expander="Utvidgare" +Expander.Ratio="Förhållande (X:1)" +Expander.Threshold="Tröskel (dB)" +Expander.AttackTime="Attack (ms)" +Expander.ReleaseTime="Frigör (ms)" +Expander.OutputGain="Utmatningsförstärkning (dB)" +Expander.Detector="Identifiering" +Expander.RMS="RMS" +Expander.Peak="Maxpunkt" +Expander.None="Ingen" +Expander.Presets="Förinställningar" +Expander.Presets.Expander="Utvidgare" +Expander.Presets.Gate="Port" +LumaKeyFilter="Ljusstyrkefilter" +Luma.LumaMax="Maximal ljusstyrka" +Luma.LumaMin="Minimal ljusstyrka" +Luma.LumaMaxSmooth="Max. utjämning av ljusstyrka" +Luma.LumaMinSmooth="Min. utjämning av ljusstyrka" diff --git a/plugins/obs-filters/data/locale/tr-TR.ini b/plugins/obs-filters/data/locale/tr-TR.ini index ee2a738..a79f5fd 100644 --- a/plugins/obs-filters/data/locale/tr-TR.ini +++ b/plugins/obs-filters/data/locale/tr-TR.ini @@ -12,6 +12,7 @@ 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" +InvertPolarity="Polariteyi ters çevir" Gain="Kazanç" DelayMs="Gecikme (milisaniye)" Type="Türü" @@ -57,6 +58,7 @@ NoiseGate.ReleaseTime="Bırakma Süresi (milisaniye)" Gain.GainDB="Kazanç (dB)" StretchImage="Görüntüyü Uzat (görüntü en-boy oranını görmezden gel)" Resolution="Çözünürlük" +Base.Canvas="Temel (Tuval) Çözünürlüğü" None="Hiçbiri" ScaleFiltering="Ölçek Filtreleme" ScaleFiltering.Point="Nokta" @@ -74,4 +76,18 @@ Compressor.AttackTime="Atak (ms)" Compressor.ReleaseTime="Bırakma (ms)" Compressor.OutputGain="Çıkış Kazancı (dB)" Compressor.SidechainSource="Yan-Zincir/Alçaltma Kaynağı" +Limiter="Sınırlayıcı" +Limiter.Threshold="Eşik (dB)" +Limiter.ReleaseTime="Bırakma süresi (ms)" +Expander="Genişletici" +Expander.Ratio="Oran (X:1)" +Expander.Threshold="Eşik (dB)" +Expander.AttackTime="Tepki Süresi (ms)" +Expander.ReleaseTime="Bırakma Süresi (ms)" +Expander.OutputGain="Çıkış Kazancı (dB)" +Expander.Detector="Algılama" +Expander.None="Hiçbiri" +Expander.Presets="Hazır Ayarlar" +Expander.Presets.Expander="Genişletici" +Expander.Presets.Gate="Geçit" diff --git a/plugins/obs-filters/data/locale/uk-UA.ini b/plugins/obs-filters/data/locale/uk-UA.ini index 3df87f8..e028e64 100644 --- a/plugins/obs-filters/data/locale/uk-UA.ini +++ b/plugins/obs-filters/data/locale/uk-UA.ini @@ -12,6 +12,7 @@ GPUDelayFilter="Затримка візуалізації" UndistortCenter="Зменшити викривлення у центрі, якщо масштабувати з надширокоформатного" NoiseGate="Пороговий шумопонижувач" NoiseSuppress="Подавлення шуму" +InvertPolarity="Інверсія сигналу" Gain="Підсилення" DelayMs="Затримка (мілісекунд)" Type="Тип" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="Тривалість спаду сигналу (мілі Gain.GainDB="Підсилення (дБ)" StretchImage="Розтягнути зображення (ігнорувати пропорції зображення)" Resolution="Новий розмір" +Base.Canvas="Роздільна здатність (Полотно)" None="Не вказано" ScaleFiltering="Фільтр масштабування" ScaleFiltering.Point="Ступінчастий" ScaleFiltering.Bilinear="Білінійний" ScaleFiltering.Bicubic="Бікубічний" ScaleFiltering.Lanczos="Ланцош" +ScaleFiltering.Area="Усереднення площ" NoiseSuppress.SuppressLevel="Рівень подавлення (дБ)" Saturation="Насиченість" HueShift="Відтінок" @@ -74,4 +77,25 @@ Compressor.AttackTime="Атака (мс)" Compressor.ReleaseTime="Затухання (мс)" Compressor.OutputGain="Підсилення виводу (dB)" Compressor.SidechainSource="Джерело Коригування/Приглушення" +Limiter="Обмежувач амплітуди" +Limiter.Threshold="Поріг (дБ)" +Limiter.ReleaseTime="Затухання (мс)" +Expander="Експандер" +Expander.Ratio="Відношення (X:1)" +Expander.Threshold="Поріг (дБ)" +Expander.AttackTime="Атака (мс)" +Expander.ReleaseTime="Затухання (мс)" +Expander.OutputGain="Підсилення виводу (dB)" +Expander.Detector="Детектор" +Expander.RMS="Середньоквадратичний (RMS)" +Expander.Peak="Піковий" +Expander.None="Немає (миттєві значення)" +Expander.Presets="Шаблони" +Expander.Presets.Expander="Експандер" +Expander.Presets.Gate="Пороговий" +LumaKeyFilter="Фільтр яскравості" +Luma.LumaMax="Максимум яскравості" +Luma.LumaMin="Мінімум яскравості" +Luma.LumaMaxSmooth="Максимум яскравості припуск" +Luma.LumaMinSmooth="Мінімум яскравості припуск" diff --git a/plugins/obs-filters/data/locale/zh-CN.ini b/plugins/obs-filters/data/locale/zh-CN.ini index cd4daad..463045e 100644 --- a/plugins/obs-filters/data/locale/zh-CN.ini +++ b/plugins/obs-filters/data/locale/zh-CN.ini @@ -12,6 +12,7 @@ GPUDelayFilter="渲染延迟" UndistortCenter="当从超宽扩展时, 让图片中心不失真" NoiseGate="噪音阈值" NoiseSuppress="噪声抑制" +InvertPolarity="反转极性" Gain="增益" DelayMs="延迟(毫秒)" Type="类型" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="释放时间(毫秒)" Gain.GainDB="增益 (dB)" StretchImage="伸展图像 (丢弃图像纵横比)" Resolution="分辨率" +Base.Canvas="基础 (Canvas) 分辨率" None="无" ScaleFiltering="尺度滤波" ScaleFiltering.Point="点" ScaleFiltering.Bilinear="双线性算法" ScaleFiltering.Bicubic="双立方算法" ScaleFiltering.Lanczos="兰索斯算法" +ScaleFiltering.Area="区域" NoiseSuppress.SuppressLevel="抑制程度 (dB)" Saturation="饱和度" HueShift="色调偏移" @@ -70,8 +73,29 @@ Amount="数值" Compressor="压缩器" Compressor.Ratio="比率 (X:1)" Compressor.Threshold="阈值 (dB)" -Compressor.AttackTime="攻击 (ms)" +Compressor.AttackTime="起始时间(毫秒)" Compressor.ReleaseTime="释放 (ms)" Compressor.OutputGain="输出增益 (dB)" Compressor.SidechainSource="避免来源" +Limiter="限幅" +Limiter.Threshold="阈值 (dB)" +Limiter.ReleaseTime="释放 (ms)" +Expander="扩展效果" +Expander.Ratio="比率 (X:1)" +Expander.Threshold="阈值 (dB)" +Expander.AttackTime="起始时间(毫秒)" +Expander.ReleaseTime="释放时间(毫秒)" +Expander.OutputGain="输出增益 (dB)" +Expander.Detector="检测" +Expander.RMS="均方根" +Expander.Peak="峰值" +Expander.None="无" +Expander.Presets="预设" +Expander.Presets.Expander="扩展效果" +Expander.Presets.Gate="门限" +LumaKeyFilter="亮度键" +Luma.LumaMax="最大亮度" +Luma.LumaMin="最小亮度" +Luma.LumaMaxSmooth="最大亮度平滑" +Luma.LumaMinSmooth="最小亮度平滑" diff --git a/plugins/obs-filters/data/locale/zh-TW.ini b/plugins/obs-filters/data/locale/zh-TW.ini index 0925595..b9a83c3 100644 --- a/plugins/obs-filters/data/locale/zh-TW.ini +++ b/plugins/obs-filters/data/locale/zh-TW.ini @@ -12,6 +12,7 @@ GPUDelayFilter="繪製延遲" UndistortCenter="從超寬影像縮放時彌補影像中心的畸變" NoiseGate="噪音閾" NoiseSuppress="雜訊抑制" +InvertPolarity="反轉極性" Gain="增益" DelayMs="延遲 (毫秒)" Type="類型" @@ -57,12 +58,14 @@ NoiseGate.ReleaseTime="釋音時間 (Release time)(毫秒)" Gain.GainDB="增益 (dB)" StretchImage="伸展圖像 (無視圖像比例)" Resolution="解析度" +Base.Canvas="來源(畫布)解析度" None="無" ScaleFiltering="縮放濾鏡" ScaleFiltering.Point="點" ScaleFiltering.Bilinear="雙線性插值" ScaleFiltering.Bicubic="雙三次插值" ScaleFiltering.Lanczos="Lanczos" +ScaleFiltering.Area="範圍" NoiseSuppress.SuppressLevel="抑制標準 (dB)" Saturation="飽合度" HueShift="色調偏移" @@ -74,4 +77,25 @@ Compressor.AttackTime="起始時間 (ms)" Compressor.ReleaseTime="釋放時間 (ms)" Compressor.OutputGain="輸出增益 (dB)" Compressor.SidechainSource="側鏈/回避源" +Limiter="限制器" +Limiter.Threshold="臨界值 (dB)" +Limiter.ReleaseTime="釋放時間 (ms)" +Expander="展開特效" +Expander.Ratio="比例 (X:1)" +Expander.Threshold="閾值 (dB)" +Expander.AttackTime="起始時間 (ms)" +Expander.ReleaseTime="釋放時間 (ms)" +Expander.OutputGain="輸出增益 (dB)" +Expander.Detector="偵測" +Expander.RMS="方均根" +Expander.Peak="峰值" +Expander.None="無" +Expander.Presets="預先設定" +Expander.Presets.Expander="展開特效" +Expander.Presets.Gate="開門特效" +LumaKeyFilter="亮度鍵" +Luma.LumaMax="亮度最大值" +Luma.LumaMin="亮度最小值" +Luma.LumaMaxSmooth="亮度最大平滑度" +Luma.LumaMinSmooth="亮度最小平滑度" diff --git a/plugins/obs-filters/data/luma_key_filter.effect b/plugins/obs-filters/data/luma_key_filter.effect new file mode 100644 index 0000000..77cd40d --- /dev/null +++ b/plugins/obs-filters/data/luma_key_filter.effect @@ -0,0 +1,51 @@ +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float lumaMax; +uniform float lumaMin; +uniform float lumaMaxSmooth; +uniform float lumaMinSmooth; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData VSDefault(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; +} + +float4 PSALumaKeyRGBA(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float4 lumaCoef = float4(0.2989, 0.5870, 0.1140, 0.0); + + float luminance = dot(rgba, lumaCoef); + + float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); + float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); + + float amask = clo * chi; + + return float4(rgba.rgb, amask); +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSALumaKeyRGBA(v_in); + } +} diff --git a/plugins/obs-filters/data/sharpness.effect b/plugins/obs-filters/data/sharpness.effect index 54a023e..ea88e17 100644 --- a/plugins/obs-filters/data/sharpness.effect +++ b/plugins/obs-filters/data/sharpness.effect @@ -5,7 +5,6 @@ uniform float4x4 ViewProj; uniform texture2d image; uniform texture2d target; -uniform float4 color = {1.0, 1.0, 1.0, 1.0}; uniform float sharpness; uniform float texture_width; @@ -24,7 +23,6 @@ struct VertInOut { struct VertOut { float4 pos : POSITION; - float4 col : COLOR; float2 uv : TEXCOORD0; float4 t1 : TEXCOORD1; float4 t2 : TEXCOORD2; @@ -36,7 +34,6 @@ VertOut VSDefault(VertInOut vert_in) VertOut vert_out; vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); vert_out.uv = vert_in.uv; - vert_out.col = color; float2 ps = float2(1.0/texture_width, 1.0/texture_height); float dx = ps.x; @@ -52,23 +49,23 @@ float4 PSDrawBare(VertOut vert_in) : TARGET { float4 E = image.Sample(def_sampler, vert_in.uv); - float4 colorx = 8*E; + float4 color = 8*E; float4 B = image.Sample(def_sampler, vert_in.t1.yw); float4 D = image.Sample(def_sampler, vert_in.t2.xw); float4 F = image.Sample(def_sampler, vert_in.t2.zw); float4 H = image.Sample(def_sampler, vert_in.t3.yw); - colorx -= image.Sample(def_sampler, vert_in.t1.xw); - colorx -= B; - colorx -= image.Sample(def_sampler, vert_in.t1.zw); - colorx -= D; - colorx -= F; - colorx -= image.Sample(def_sampler, vert_in.t3.xw); - colorx -= H; - colorx -= image.Sample(def_sampler, vert_in.t3.zw); + color -= image.Sample(def_sampler, vert_in.t1.xw); + color -= B; + color -= image.Sample(def_sampler, vert_in.t1.zw); + color -= D; + color -= F; + color -= image.Sample(def_sampler, vert_in.t3.xw); + color -= H; + color -= image.Sample(def_sampler, vert_in.t3.zw); - colorx = ((E!=F && E!=D) || (E!=B && E!=H)) ? saturate(E + colorx*sharpness) : E; + color = ((E!=F && E!=D) || (E!=B && E!=H)) ? saturate(E + color*sharpness) : E; - return colorx; + return color; } technique Draw diff --git a/plugins/obs-filters/expander-filter.c b/plugins/obs-filters/expander-filter.c new file mode 100644 index 0000000..dc4e821 --- /dev/null +++ b/plugins/obs-filters/expander-filter.c @@ -0,0 +1,419 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +/* -------------------------------------------------------- */ + +#define do_log(level, format, ...) \ + blog(level, "[expander: '%s'] " format, \ + obs_source_get_name(cd->context), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) + +#ifdef _DEBUG +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) +#else +#define debug(format, ...) +#endif + +/* -------------------------------------------------------- */ + +#define S_RATIO "ratio" +#define S_THRESHOLD "threshold" +#define S_ATTACK_TIME "attack_time" +#define S_RELEASE_TIME "release_time" +#define S_OUTPUT_GAIN "output_gain" +#define S_DETECTOR "detector" +#define S_PRESETS "presets" + +#define MT_ obs_module_text +#define TEXT_RATIO MT_("expander.Ratio") +#define TEXT_THRESHOLD MT_("expander.Threshold") +#define TEXT_ATTACK_TIME MT_("expander.AttackTime") +#define TEXT_RELEASE_TIME MT_("expander.ReleaseTime") +#define TEXT_OUTPUT_GAIN MT_("expander.OutputGain") +#define TEXT_DETECTOR MT_("expander.Detector") +#define TEXT_PEAK MT_("expander.Peak") +#define TEXT_RMS MT_("expander.RMS") +#define TEXT_NONE MT_("expander.None") +#define TEXT_PRESETS MT_("expander.Presets") +#define TEXT_PRESETS_EXP MT_("expander.Presets.Expander") +#define TEXT_PRESETS_GATE MT_("expander.Presets.Gate") + +#define MIN_RATIO 1.0f +#define MAX_RATIO 20.0f +#define MIN_THRESHOLD_DB -60.0f +#define MAX_THRESHOLD_DB 0.0f +#define MIN_OUTPUT_GAIN_DB -32.0f +#define MAX_OUTPUT_GAIN_DB 32.0f +#define MIN_ATK_RLS_MS 1 +#define MAX_RLS_MS 1000 +#define MAX_ATK_MS 100 +#define DEFAULT_AUDIO_BUF_MS 10 + +#define MS_IN_S 1000 +#define MS_IN_S_F ((float)MS_IN_S) + +/* -------------------------------------------------------- */ + +struct expander_data { + obs_source_t *context; + float *envelope_buf[MAX_AUDIO_CHANNELS]; + size_t envelope_buf_len; + + float ratio; + float threshold; + float attack_gain; + float release_gain; + float output_gain; + + size_t num_channels; + size_t sample_rate; + float envelope[MAX_AUDIO_CHANNELS]; + float slope; + int detector; + float runave[MAX_AUDIO_CHANNELS]; + bool is_gate; + float *runaverage[MAX_AUDIO_CHANNELS]; + size_t runaverage_len; + float *gaindB[MAX_AUDIO_CHANNELS]; + size_t gaindB_len; + float gaindB_buf[MAX_AUDIO_CHANNELS]; + float *env_in; + size_t env_in_len; +}; + +enum { + RMS_DETECT, + RMS_STILLWELL_DETECT, + PEAK_DETECT, + NO_DETECT, +}; +/* -------------------------------------------------------- */ + +static void resize_env_buffer(struct expander_data *cd, size_t len) +{ + cd->envelope_buf_len = len; + for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) + cd->envelope_buf[i] = brealloc(cd->envelope_buf[i], + cd->envelope_buf_len * sizeof(float)); +} + +static void resize_runaverage_buffer(struct expander_data *cd, size_t len) +{ + cd->runaverage_len = len; + for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) + cd->runaverage[i] = brealloc(cd->runaverage[i], + cd->runaverage_len * sizeof(float)); +} + +static void resize_env_in_buffer(struct expander_data *cd, size_t len) +{ + cd->env_in_len = len; + cd->env_in = brealloc(cd->env_in, cd->env_in_len * sizeof(float)); +} + +static void resize_gaindB_buffer(struct expander_data *cd, size_t len) +{ + cd->gaindB_len = len; + for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) + cd->gaindB[i] = brealloc(cd->gaindB[i], + cd->gaindB_len * sizeof(float)); +} + +static inline float gain_coefficient(uint32_t sample_rate, float time) +{ + return expf(-1.0f / (sample_rate * time)); +} + +static const char *expander_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("Expander"); +} + +static void expander_defaults(obs_data_t *s) +{ + const char *presets = obs_data_get_string(s, S_PRESETS); + bool is_expander_preset = true; + if (strcmp(presets, "gate") == 0) + is_expander_preset = false; + obs_data_set_default_string(s, S_PRESETS, is_expander_preset ? + "expander" : "gate"); + obs_data_set_default_double(s, S_RATIO, is_expander_preset ? + 2.0 : 10.0); + obs_data_set_default_double(s, S_THRESHOLD, -40.0f); + obs_data_set_default_int(s, S_ATTACK_TIME, 10); + obs_data_set_default_int(s, S_RELEASE_TIME, is_expander_preset ? + 50 : 125); + obs_data_set_default_double(s, S_OUTPUT_GAIN, 0.0); + obs_data_set_default_string(s, S_DETECTOR, "RMS"); +} + +static void expander_update(void *data, obs_data_t *s) +{ + struct expander_data *cd = data; + const char *presets = obs_data_get_string(s, S_PRESETS); + if (strcmp(presets, "expander") == 0 && cd->is_gate) { + obs_data_clear(s); + obs_data_set_string(s, S_PRESETS, "expander"); + expander_defaults(s); + cd->is_gate = false; + } + if (strcmp(presets, "gate") == 0 && !cd->is_gate) { + obs_data_clear(s); + obs_data_set_string(s, S_PRESETS, "gate"); + expander_defaults(s); + cd->is_gate = true; + } + + const uint32_t sample_rate = + audio_output_get_sample_rate(obs_get_audio()); + const size_t num_channels = + audio_output_get_channels(obs_get_audio()); + const float attack_time_ms = + (float)obs_data_get_int(s, S_ATTACK_TIME); + const float release_time_ms = + (float)obs_data_get_int(s, S_RELEASE_TIME); + const float output_gain_db = + (float)obs_data_get_double(s, S_OUTPUT_GAIN); + + cd->ratio = (float)obs_data_get_double(s, S_RATIO); + + cd->threshold = (float)obs_data_get_double(s, S_THRESHOLD); + cd->attack_gain = gain_coefficient(sample_rate, + attack_time_ms / MS_IN_S_F); + cd->release_gain = gain_coefficient(sample_rate, + release_time_ms / MS_IN_S_F); + cd->output_gain = db_to_mul(output_gain_db); + cd->num_channels = num_channels; + cd->sample_rate = sample_rate; + cd->slope = 1.0f - cd->ratio; + + const char *detect_mode = obs_data_get_string(s, S_DETECTOR); + if (strcmp(detect_mode, "RMS") == 0) + cd->detector = RMS_DETECT; + if (strcmp(detect_mode, "peak") == 0) + cd->detector = PEAK_DETECT; + + size_t sample_len = sample_rate * DEFAULT_AUDIO_BUF_MS / MS_IN_S; + if (cd->envelope_buf_len == 0) + resize_env_buffer(cd, sample_len); + if (cd->runaverage_len == 0) + resize_runaverage_buffer(cd, sample_len); + if (cd->env_in_len == 0) + resize_env_in_buffer(cd, sample_len); + if (cd->gaindB_len == 0) + resize_gaindB_buffer(cd, sample_len); +} + +static void *expander_create(obs_data_t *settings, obs_source_t *filter) +{ + struct expander_data *cd = bzalloc(sizeof(struct expander_data)); + cd->context = filter; + for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) { + cd->runave[i] = 0; + cd->envelope[i] = 0; + cd->gaindB_buf[i] = 0; + } + cd->is_gate = false; + const char *presets = obs_data_get_string(settings, S_PRESETS); + if (strcmp(presets, "gate") == 0) + cd->is_gate = true; + + expander_update(cd, settings); + return cd; +} + +static void expander_destroy(void *data) +{ + struct expander_data *cd = data; + + for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) { + bfree(cd->envelope_buf[i]); + bfree(cd->runaverage[i]); + bfree(cd->gaindB[i]); + } + bfree(cd->env_in); + bfree(cd); +} + +// detection stage +static void analyze_envelope(struct expander_data *cd, + float **samples, const uint32_t num_samples) +{ + if (cd->envelope_buf_len < num_samples) + resize_env_buffer(cd, num_samples); + if (cd->runaverage_len < num_samples) + resize_runaverage_buffer(cd, num_samples); + if (cd->env_in_len < num_samples) + resize_env_in_buffer(cd, num_samples); + + // 10 ms RMS window + const float rmscoef = exp2f(-100.0f / cd->sample_rate); + + for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) { + memset(cd->envelope_buf[i], 0, + num_samples * sizeof(cd->envelope_buf[i][0])); + memset(cd->runaverage[i], 0, + num_samples * sizeof(cd->runaverage[i][0])); + } + memset(cd->env_in, 0, num_samples * sizeof(cd->env_in[0])); + + for (size_t chan = 0; chan < cd->num_channels; ++chan) { + if (!samples[chan]) + continue; + + float *envelope_buf = cd->envelope_buf[chan]; + float *runave = cd->runaverage[chan]; + float *env_in = cd->env_in; + + if (cd->detector == RMS_DETECT) { + runave[0] = rmscoef * cd->runave[chan] + + (1 - rmscoef) * powf(samples[chan][0], 2.0f); + env_in[0] = sqrtf(fmaxf(runave[0], 0)); + for (uint32_t i = 1; i < num_samples; ++i) { + runave[i] = rmscoef * runave[i - 1] + + (1 - rmscoef) * + powf(samples[chan][i], 2.0f); + env_in[i] = sqrtf(runave[i]); + } + } else if (cd->detector == PEAK_DETECT) { + for (uint32_t i = 0; i < num_samples; ++i) { + runave[i] = powf(samples[chan][i], 2); + env_in[i] = fabsf(samples[chan][i]); + } + } + + cd->runave[chan] = runave[num_samples-1]; + for (uint32_t i = 0; i < num_samples; ++i) + envelope_buf[i] = fmaxf(envelope_buf[i], env_in[i]); + cd->envelope[chan] = cd->envelope_buf[chan][num_samples - 1]; + } +} + +// gain stage and ballistics in dB domain +static inline void process_expansion(struct expander_data *cd, + float **samples, uint32_t num_samples) +{ + const float attack_gain = cd->attack_gain; + const float release_gain = cd->release_gain; + + if (cd->gaindB_len < num_samples) + resize_gaindB_buffer(cd, num_samples); + for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) + memset(cd->gaindB[i], 0, num_samples * sizeof(cd->gaindB[i][0])); + + for (size_t chan = 0; chan < cd->num_channels; chan++) { + for (size_t i = 0; i < num_samples; ++i) { + // gain stage of expansion + float env_db = mul_to_db(cd->envelope_buf[chan][i]); + float gain = cd->threshold - env_db > 0.0f ? + fmaxf(cd->slope * + (cd->threshold - env_db), -60.0f) : + 0.0f; + // ballistics (attack/release) + if (i > 0) { + if (gain > cd->gaindB[chan][i - 1]) + cd->gaindB[chan][i] = attack_gain * + cd->gaindB[chan][i - 1] + + (1.0f - attack_gain) * gain; + else + cd->gaindB[chan][i] = release_gain * + cd->gaindB[chan][i - 1] + + (1.0f - release_gain) * gain; + } else { + if (gain > cd->gaindB_buf[chan]) + cd->gaindB[chan][i] = attack_gain * + cd->gaindB_buf[chan] + + (1.0f - attack_gain) * gain; + else + cd->gaindB[chan][i] = release_gain * + cd->gaindB_buf[chan] + + (1.0f - release_gain) * gain; + } + + gain = db_to_mul(fminf(0, cd->gaindB[chan][i])); + if (samples[chan]) + samples[chan][i] *= gain * cd->output_gain; + } + cd->gaindB_buf[chan] = cd->gaindB[chan][num_samples - 1]; + } +} + +static struct obs_audio_data *expander_filter_audio(void *data, + struct obs_audio_data *audio) +{ + struct expander_data *cd = data; + + const uint32_t num_samples = audio->frames; + if (num_samples == 0) + return audio; + + float **samples = (float**)audio->data; + + analyze_envelope(cd, samples, num_samples); + process_expansion(cd, samples, num_samples); + return audio; +} + +static bool presets_changed(obs_properties_t *props, obs_property_t *prop, + obs_data_t *settings) +{ + UNUSED_PARAMETER(props); + UNUSED_PARAMETER(prop); + UNUSED_PARAMETER(settings); + return true; +} + +static obs_properties_t *expander_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_property_t *presets = obs_properties_add_list(props, S_PRESETS, + TEXT_PRESETS, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(presets, TEXT_PRESETS_EXP, "expander"); + obs_property_list_add_string(presets, TEXT_PRESETS_GATE, "gate"); + obs_property_set_modified_callback(presets, presets_changed); + obs_properties_add_float_slider(props, S_RATIO, + TEXT_RATIO, MIN_RATIO, MAX_RATIO, 0.1); + obs_properties_add_float_slider(props, S_THRESHOLD, + TEXT_THRESHOLD, MIN_THRESHOLD_DB, MAX_THRESHOLD_DB, + 0.1); + obs_properties_add_int_slider(props, S_ATTACK_TIME, + TEXT_ATTACK_TIME, MIN_ATK_RLS_MS, MAX_ATK_MS, 1); + obs_properties_add_int_slider(props, S_RELEASE_TIME, + TEXT_RELEASE_TIME, MIN_ATK_RLS_MS, MAX_RLS_MS, 1); + obs_properties_add_float_slider(props, S_OUTPUT_GAIN, + TEXT_OUTPUT_GAIN, MIN_OUTPUT_GAIN_DB, + MAX_OUTPUT_GAIN_DB, 0.1); + obs_property_t *detect = obs_properties_add_list(props, S_DETECTOR, + TEXT_DETECTOR, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(detect, TEXT_RMS, "RMS"); + obs_property_list_add_string(detect, TEXT_PEAK, "peak"); + + UNUSED_PARAMETER(data); + return props; +} + +struct obs_source_info expander_filter = { + .id = "expander_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_AUDIO, + .get_name = expander_name, + .create = expander_create, + .destroy = expander_destroy, + .update = expander_update, + .filter_audio = expander_filter_audio, + .get_defaults = expander_defaults, + .get_properties = expander_properties, +}; diff --git a/plugins/obs-filters/invert-audio-polarity.c b/plugins/obs-filters/invert-audio-polarity.c new file mode 100644 index 0000000..8c63508 --- /dev/null +++ b/plugins/obs-filters/invert-audio-polarity.c @@ -0,0 +1,49 @@ +#include + +static const char *invert_polarity_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("InvertPolarity"); +} + +static void invert_polarity_destroy(void *data) +{ + UNUSED_PARAMETER(data); +} + +static void *invert_polarity_create(obs_data_t *settings, obs_source_t *filter) +{ + UNUSED_PARAMETER(settings); + return filter; +} + +static struct obs_audio_data *invert_polarity_filter_audio(void *unused, + struct obs_audio_data *audio) +{ + float **adata = (float**)audio->data; + + for (size_t c = 0; c < MAX_AV_PLANES; c++) { + register float *channel_data = adata[c]; + register float *channel_end = channel_data + audio->frames; + + if (!channel_data) + break; + + while (channel_data < channel_end) { + *(channel_data++) *= -1.0f; + } + } + + UNUSED_PARAMETER(unused); + return audio; +} + +struct obs_source_info invert_polarity_filter = { + .id = "invert_polarity_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_AUDIO, + .get_name = invert_polarity_name, + .create = invert_polarity_create, + .destroy = invert_polarity_destroy, + .filter_audio = invert_polarity_filter_audio, +}; diff --git a/plugins/obs-filters/limiter-filter.c b/plugins/obs-filters/limiter-filter.c new file mode 100644 index 0000000..c6bc139 --- /dev/null +++ b/plugins/obs-filters/limiter-filter.c @@ -0,0 +1,215 @@ +#include +#include +#include + +#include +#include +#include + +/* -------------------------------------------------------- */ + +#define do_log(level, format, ...) \ + blog(level, "[limiter: '%s'] " format, \ + obs_source_get_name(cd->context), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) + +#ifdef _DEBUG +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) +#else +#define debug(format, ...) +#endif + +/* -------------------------------------------------------- */ + +#define S_THRESHOLD "threshold" +#define S_RELEASE_TIME "release_time" + +#define MT_ obs_module_text +#define TEXT_THRESHOLD MT_("Limiter.Threshold") +#define TEXT_RELEASE_TIME MT_("Limiter.ReleaseTime") + +#define MIN_THRESHOLD_DB -60.0 +#define MAX_THRESHOLD_DB 0.0f +#define MIN_ATK_RLS_MS 1 +#define MAX_RLS_MS 1000 +#define DEFAULT_AUDIO_BUF_MS 10 +#define ATK_TIME 0.001f +#define MS_IN_S 1000 +#define MS_IN_S_F ((float)MS_IN_S) + +/* -------------------------------------------------------- */ + +struct limiter_data { + obs_source_t *context; + float *envelope_buf; + size_t envelope_buf_len; + + float threshold; + float attack_gain; + float release_gain; + float output_gain; + + size_t num_channels; + size_t sample_rate; + float envelope; + float slope; +}; + +/* -------------------------------------------------------- */ + +static void resize_env_buffer(struct limiter_data *cd, size_t len) +{ + cd->envelope_buf_len = len; + cd->envelope_buf = brealloc(cd->envelope_buf, len * sizeof(float)); +} + +static inline float gain_coefficient(uint32_t sample_rate, float time) +{ + return (float)exp(-1.0f / (sample_rate * time)); +} + +static const char *limiter_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("Limiter"); +} + +static void limiter_update(void *data, obs_data_t *s) +{ + struct limiter_data *cd = data; + + const uint32_t sample_rate = + audio_output_get_sample_rate(obs_get_audio()); + const size_t num_channels = + audio_output_get_channels(obs_get_audio()); + float attack_time_ms = ATK_TIME; + + const float release_time_ms = + (float)obs_data_get_int(s, S_RELEASE_TIME); + const float output_gain_db = 0; + + cd->threshold = (float)obs_data_get_double(s, S_THRESHOLD); + + cd->attack_gain = gain_coefficient(sample_rate, + attack_time_ms / MS_IN_S_F); + cd->release_gain = gain_coefficient(sample_rate, + release_time_ms / MS_IN_S_F); + cd->output_gain = db_to_mul(output_gain_db); + cd->num_channels = num_channels; + cd->sample_rate = sample_rate; + cd->slope = 1.0f; + + size_t sample_len = sample_rate * DEFAULT_AUDIO_BUF_MS / MS_IN_S; + if (cd->envelope_buf_len == 0) + resize_env_buffer(cd, sample_len); +} + +static void *limiter_create(obs_data_t *settings, obs_source_t *filter) +{ + struct limiter_data *cd = bzalloc(sizeof(struct limiter_data)); + cd->context = filter; + + limiter_update(cd, settings); + return cd; +} + +static void limiter_destroy(void *data) +{ + struct limiter_data *cd = data; + + bfree(cd->envelope_buf); + bfree(cd); +} + +static void analyze_envelope(struct limiter_data *cd, + float **samples, const uint32_t num_samples) +{ + if (cd->envelope_buf_len < num_samples) { + resize_env_buffer(cd, num_samples); + } + + const float attack_gain = cd->attack_gain; + const float release_gain = cd->release_gain; + + memset(cd->envelope_buf, 0, num_samples * sizeof(cd->envelope_buf[0])); + for (size_t chan = 0; chan < cd->num_channels; ++chan) { + if (!samples[chan]) + continue; + + float *envelope_buf = cd->envelope_buf; + float env = cd->envelope; + for (uint32_t i = 0; i < num_samples; ++i) { + const float env_in = fabsf(samples[chan][i]); + if (env < env_in) { + env = env_in + attack_gain * (env - env_in); + } else { + env = env_in + release_gain * (env - env_in); + } + envelope_buf[i] = fmaxf(envelope_buf[i], env); + } + } + cd->envelope = cd->envelope_buf[num_samples - 1]; +} + +static inline void process_compression(const struct limiter_data *cd, + float **samples, uint32_t num_samples) +{ + for (size_t i = 0; i < num_samples; ++i) { + const float env_db = mul_to_db(cd->envelope_buf[i]); + float gain = cd->slope * (cd->threshold - env_db); + gain = db_to_mul(fminf(0, gain)); + + for (size_t c = 0; c < cd->num_channels; ++c) { + if (samples[c]) { + samples[c][i] *= gain * cd->output_gain; + } + } + } +} + +static struct obs_audio_data *limiter_filter_audio(void *data, + struct obs_audio_data *audio) +{ + struct limiter_data *cd = data; + + const uint32_t num_samples = audio->frames; + if (num_samples == 0) + return audio; + + float **samples = (float**)audio->data; + analyze_envelope(cd, samples, num_samples); + process_compression(cd, samples, num_samples); + return audio; +} + +static void limiter_defaults(obs_data_t *s) +{ + obs_data_set_default_double(s, S_THRESHOLD, -6.0f); + obs_data_set_default_int(s, S_RELEASE_TIME, 60); +} + +static obs_properties_t *limiter_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_properties_add_float_slider(props, S_THRESHOLD, TEXT_THRESHOLD, MIN_THRESHOLD_DB, MAX_THRESHOLD_DB, 0.1); + obs_properties_add_int_slider(props, S_RELEASE_TIME, TEXT_RELEASE_TIME, MIN_ATK_RLS_MS, MAX_RLS_MS, 1); + + UNUSED_PARAMETER(data); + return props; +} + +struct obs_source_info limiter_filter = { + .id = "limiter_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_AUDIO, + .get_name = limiter_name, + .create = limiter_create, + .destroy = limiter_destroy, + .update = limiter_update, + .filter_audio = limiter_filter_audio, + .get_defaults = limiter_defaults, + .get_properties = limiter_properties, +}; diff --git a/plugins/obs-filters/luma-key-filter.c b/plugins/obs-filters/luma-key-filter.c new file mode 100644 index 0000000..b738a1c --- /dev/null +++ b/plugins/obs-filters/luma-key-filter.c @@ -0,0 +1,153 @@ +#include + +#define SETTING_LUMA_MAX "luma_max" +#define SETTING_LUMA_MIN "luma_min" +#define SETTING_LUMA_MAX_SMOOTH "luma_max_smooth" +#define SETTING_LUMA_MIN_SMOOTH "luma_min_smooth" + +#define TEXT_LUMA_MAX obs_module_text("Luma.LumaMax") +#define TEXT_LUMA_MIN obs_module_text("Luma.LumaMin") +#define TEXT_LUMA_MAX_SMOOTH obs_module_text("Luma.LumaMaxSmooth") +#define TEXT_LUMA_MIN_SMOOTH obs_module_text("Luma.LumaMinSmooth") + +struct luma_key_filter_data { + obs_source_t *context; + + gs_effect_t *effect; + + gs_eparam_t *luma_max_param; + gs_eparam_t *luma_min_param; + gs_eparam_t *luma_max_smooth_param; + gs_eparam_t *luma_min_smooth_param; + + float luma_max; + float luma_min; + float luma_max_smooth; + float luma_min_smooth; +}; + +static const char *luma_key_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("LumaKeyFilter"); +} + +static void luma_key_update(void *data, obs_data_t *settings) +{ + struct luma_key_filter_data *filter = data; + + double lumaMax = obs_data_get_double(settings, SETTING_LUMA_MAX); + double lumaMin = obs_data_get_double(settings, SETTING_LUMA_MIN); + double lumaMaxSmooth = obs_data_get_double(settings, SETTING_LUMA_MAX_SMOOTH); + double lumaMinSmooth = obs_data_get_double(settings, SETTING_LUMA_MIN_SMOOTH); + + filter->luma_max = (float)lumaMax; + filter->luma_min = (float)lumaMin; + filter->luma_max_smooth = (float)lumaMaxSmooth; + filter->luma_min_smooth = (float)lumaMinSmooth; +} + +static void luma_key_destroy(void *data) +{ + struct luma_key_filter_data *filter = data; + + if (filter->effect) { + obs_enter_graphics(); + gs_effect_destroy(filter->effect); + obs_leave_graphics(); + } + + bfree(data); +} + +static void *luma_key_create(obs_data_t *settings, obs_source_t *context) +{ + struct luma_key_filter_data *filter = + bzalloc(sizeof(struct luma_key_filter_data)); + char *effect_path = obs_module_file("luma_key_filter.effect"); + + filter->context = context; + + obs_enter_graphics(); + + filter->effect = gs_effect_create_from_file(effect_path, NULL); + if (filter->effect) { + filter->luma_max_param = gs_effect_get_param_by_name( + filter->effect, "lumaMax"); + filter->luma_min_param = gs_effect_get_param_by_name( + filter->effect, "lumaMin"); + filter->luma_max_smooth_param = gs_effect_get_param_by_name( + filter->effect, "lumaMaxSmooth"); + filter->luma_min_smooth_param = gs_effect_get_param_by_name( + filter->effect, "lumaMinSmooth"); + } + + obs_leave_graphics(); + + bfree(effect_path); + + if (!filter->effect) { + luma_key_destroy(filter); + return NULL; + } + + luma_key_update(filter, settings); + return filter; +} + +static void luma_key_render(void *data, gs_effect_t *effect) +{ + struct luma_key_filter_data *filter = data; + + if (!obs_source_process_filter_begin(filter->context, GS_RGBA, + OBS_ALLOW_DIRECT_RENDERING)) + return; + + gs_effect_set_float(filter->luma_max_param, filter->luma_max); + gs_effect_set_float(filter->luma_min_param, filter->luma_min); + gs_effect_set_float(filter->luma_max_smooth_param, filter->luma_max_smooth); + gs_effect_set_float(filter->luma_min_smooth_param, filter->luma_min_smooth); + + obs_source_process_filter_end(filter->context, filter->effect, 0, 0); + + UNUSED_PARAMETER(effect); +} + +static obs_properties_t *luma_key_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_properties_add_float_slider(props, SETTING_LUMA_MAX, + TEXT_LUMA_MAX, 0, 1, 0.01); + obs_properties_add_float_slider(props, SETTING_LUMA_MAX_SMOOTH, + TEXT_LUMA_MAX_SMOOTH, 0, 1, 0.01); + obs_properties_add_float_slider(props, SETTING_LUMA_MIN, + TEXT_LUMA_MIN, 0, 1, 0.01); + obs_properties_add_float_slider(props, SETTING_LUMA_MIN_SMOOTH, + TEXT_LUMA_MIN_SMOOTH, 0, 1, 0.01); + + UNUSED_PARAMETER(data); + return props; +} + +static void luma_key_defaults(obs_data_t *settings) +{ + obs_data_set_default_double(settings, SETTING_LUMA_MAX, 1.0); + obs_data_set_default_double(settings, SETTING_LUMA_MIN, 0.0); + obs_data_set_default_double(settings, SETTING_LUMA_MAX_SMOOTH, 0.0); + obs_data_set_default_double(settings, SETTING_LUMA_MIN_SMOOTH, 0.0); +} + + +struct obs_source_info luma_key_filter = { + .id = "luma_key_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = luma_key_name, + .create = luma_key_create, + .destroy = luma_key_destroy, + .video_render = luma_key_render, + .update = luma_key_update, + .get_properties = luma_key_properties, + .get_defaults = luma_key_defaults +}; diff --git a/plugins/obs-filters/mask-filter.c b/plugins/obs-filters/mask-filter.c index ae6e5d1..d832232 100644 --- a/plugins/obs-filters/mask-filter.c +++ b/plugins/obs-filters/mask-filter.c @@ -46,6 +46,7 @@ static void mask_filter_update(void *data, obs_data_t *settings) int opacity = (int)obs_data_get_int(settings, SETTING_OPACITY); char *effect_path; + color &= 0xFFFFFF; color |= (uint32_t)(((double)opacity) * 2.55) << 24; vec4_from_rgba(&filter->color, color); @@ -115,7 +116,8 @@ static obs_properties_t *mask_filter_properties(void *data) obs_properties_add_path(props, SETTING_IMAGE_PATH, TEXT_IMAGE_PATH, OBS_PATH_FILE, filter_str.array, NULL); obs_properties_add_color(props, SETTING_COLOR, TEXT_COLOR); - obs_properties_add_int(props, SETTING_OPACITY, TEXT_OPACITY, 0, 100, 1); + obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, + 0, 100, 1); obs_properties_add_bool(props, SETTING_STRETCH, TEXT_STRETCH); dstr_free(&filter_str); diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 02ac63d..c60e6ee 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -2,8 +2,11 @@ #include "obs-filters-config.h" OBS_DECLARE_MODULE() - OBS_MODULE_USE_DEFAULT_LOCALE("obs-filters", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "OBS core filters"; +} extern struct obs_source_info mask_filter; extern struct obs_source_info crop_filter; @@ -20,8 +23,12 @@ extern struct obs_source_info async_delay_filter; #if SPEEXDSP_ENABLED extern struct obs_source_info noise_suppress_filter; #endif +extern struct obs_source_info invert_polarity_filter; extern struct obs_source_info noise_gate_filter; extern struct obs_source_info compressor_filter; +extern struct obs_source_info limiter_filter; +extern struct obs_source_info expander_filter; +extern struct obs_source_info luma_key_filter; bool obs_module_load(void) { @@ -40,7 +47,11 @@ bool obs_module_load(void) #if SPEEXDSP_ENABLED obs_register_source(&noise_suppress_filter); #endif + obs_register_source(&invert_polarity_filter); obs_register_source(&noise_gate_filter); obs_register_source(&compressor_filter); + obs_register_source(&limiter_filter); + obs_register_source(&expander_filter); + obs_register_source(&luma_key_filter); return true; } diff --git a/plugins/obs-filters/scale-filter.c b/plugins/obs-filters/scale-filter.c index 3a1cfef..c4c4022 100644 --- a/plugins/obs-filters/scale-filter.c +++ b/plugins/obs-filters/scale-filter.c @@ -17,12 +17,15 @@ #define T_SAMPLING_BILINEAR obs_module_text("ScaleFiltering.Bilinear") #define T_SAMPLING_BICUBIC obs_module_text("ScaleFiltering.Bicubic") #define T_SAMPLING_LANCZOS obs_module_text("ScaleFiltering.Lanczos") +#define T_SAMPLING_AREA obs_module_text("ScaleFiltering.Area") #define T_UNDISTORT obs_module_text("UndistortCenter") +#define T_BASE obs_module_text("Base.Canvas") #define S_SAMPLING_POINT "point" #define S_SAMPLING_BILINEAR "bilinear" #define S_SAMPLING_BICUBIC "bicubic" #define S_SAMPLING_LANCZOS "lanczos" +#define S_SAMPLING_AREA "area" struct scale_filter_data { obs_source_t *context; @@ -42,6 +45,7 @@ struct scale_filter_data { bool target_valid; bool valid; bool undistort; + bool base_canvas_resolution; }; static const char *scale_filter_name(void *unused) @@ -59,18 +63,29 @@ static void scale_filter_update(void *data, obs_data_t *settings) const char *sampling = obs_data_get_string(settings, S_SAMPLING); filter->valid = true; + filter->base_canvas_resolution = false; - ret = sscanf(res_str, "%dx%d", &filter->cx_in, &filter->cy_in); - if (ret == 2) { + if (strcmp(res_str, T_BASE) == 0) { + struct obs_video_info ovi; + obs_get_video_info(&ovi); filter->aspect_ratio_only = false; + filter->base_canvas_resolution = true; + filter->cx_in = ovi.base_width; + filter->cy_in = ovi.base_height; } else { - ret = sscanf(res_str, "%d:%d", &filter->cx_in, &filter->cy_in); - if (ret != 2) { - filter->valid = false; - return; - } + ret = sscanf(res_str, "%dx%d", &filter->cx_in, &filter->cy_in); + if (ret == 2) { + filter->aspect_ratio_only = false; + } else { + ret = sscanf(res_str, "%d:%d", &filter->cx_in, + &filter->cy_in); + if (ret != 2) { + filter->valid = false; + return; + } - filter->aspect_ratio_only = true; + filter->aspect_ratio_only = true; + } } if (astrcmpi(sampling, S_SAMPLING_POINT) == 0) { @@ -82,6 +97,9 @@ static void scale_filter_update(void *data, obs_data_t *settings) } else if (astrcmpi(sampling, S_SAMPLING_LANCZOS) == 0) { filter->sampling = OBS_SCALE_LANCZOS; + } else if (astrcmpi(sampling, S_SAMPLING_AREA) == 0) { + filter->sampling = OBS_SCALE_AREA; + } else { /* S_SAMPLING_BICUBIC */ filter->sampling = OBS_SCALE_BICUBIC; } @@ -126,6 +144,13 @@ static void scale_filter_tick(void *data, float seconds) int cx; int cy; + if (filter->base_canvas_resolution) { + struct obs_video_info ovi; + obs_get_video_info(&ovi); + filter->cx_in = ovi.base_width; + filter->cy_in = ovi.base_height; + } + target = obs_filter_get_target(filter->context); filter->cx_out = 0; filter->cy_out = 0; @@ -198,6 +223,7 @@ static void scale_filter_tick(void *data, float seconds) case OBS_SCALE_BILINEAR: type = OBS_EFFECT_DEFAULT; break; case OBS_SCALE_BICUBIC: type = OBS_EFFECT_BICUBIC; break; case OBS_SCALE_LANCZOS: type = OBS_EFFECT_LANCZOS; break; + case OBS_SCALE_AREA: type = OBS_EFFECT_AREA; break; } } @@ -289,15 +315,15 @@ static bool sampling_modified(obs_properties_t *props, obs_property_t *p, bool has_undistort; if (astrcmpi(sampling, S_SAMPLING_POINT) == 0) { has_undistort = false; - } else if (astrcmpi(sampling, S_SAMPLING_BILINEAR) == 0) { has_undistort = false; - } else if (astrcmpi(sampling, S_SAMPLING_LANCZOS) == 0) { has_undistort = true; - + } + else if (astrcmpi(sampling, S_SAMPLING_AREA) == 0) { + has_undistort = false; } else { /* S_SAMPLING_BICUBIC */ has_undistort = true; @@ -340,6 +366,7 @@ static obs_properties_t *scale_filter_properties(void *data) obs_property_list_add_string(p, T_SAMPLING_BILINEAR, S_SAMPLING_BILINEAR); obs_property_list_add_string(p, T_SAMPLING_BICUBIC, S_SAMPLING_BICUBIC); obs_property_list_add_string(p, T_SAMPLING_LANCZOS, S_SAMPLING_LANCZOS); + obs_property_list_add_string(p, T_SAMPLING_AREA, S_SAMPLING_AREA); /* ----------------- */ @@ -347,6 +374,7 @@ static obs_properties_t *scale_filter_properties(void *data) OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_NONE, T_NONE); + obs_property_list_add_string(p, T_BASE, T_BASE); for (size_t i = 0; i < NUM_ASPECTS; i++) obs_property_list_add_string(p, aspects[i], aspects[i]); diff --git a/plugins/obs-libfdk/data/locale/da-DK.ini b/plugins/obs-libfdk/data/locale/da-DK.ini index 7ad6ce7..d136485 100644 --- a/plugins/obs-libfdk/data/locale/da-DK.ini +++ b/plugins/obs-libfdk/data/locale/da-DK.ini @@ -1,4 +1,4 @@ LibFDK="libfdk AAC Encoder" -Bitrate="Bitrate" -Afterburner="Aktivere AAC Afterburner" +Bitrate="Bit-hastighed" +Afterburner="Aktivér AAC Afterburner" diff --git a/plugins/obs-libfdk/data/locale/de-DE.ini b/plugins/obs-libfdk/data/locale/de-DE.ini index 3ae777b..6092b11 100644 --- a/plugins/obs-libfdk/data/locale/de-DE.ini +++ b/plugins/obs-libfdk/data/locale/de-DE.ini @@ -1,4 +1,4 @@ -LibFDK="libfdk AAC Codierer" +LibFDK="libfdk-AAC-Kodierer" Bitrate="Bitrate" -Afterburner="AAC Afterburner aktivieren" +Afterburner="AAC-Afterburner aktivieren" diff --git a/plugins/obs-libfdk/data/locale/fr-FR.ini b/plugins/obs-libfdk/data/locale/fr-FR.ini index 494658a..ab1ab31 100644 --- a/plugins/obs-libfdk/data/locale/fr-FR.ini +++ b/plugins/obs-libfdk/data/locale/fr-FR.ini @@ -1,4 +1,4 @@ LibFDK="Encodeur AAC libfdk" Bitrate="Débit" -Afterburner="Activer \"AAC Afterburner\"" +Afterburner="Activer l'Afterburner" diff --git a/plugins/obs-libfdk/data/locale/it-IT.ini b/plugins/obs-libfdk/data/locale/it-IT.ini index f1dff2b..a78d865 100644 --- a/plugins/obs-libfdk/data/locale/it-IT.ini +++ b/plugins/obs-libfdk/data/locale/it-IT.ini @@ -1,4 +1,4 @@ -LibFDK="Libfdk codificatore AAC" -Bitrate="Bitrate" -Afterburner="Abilita AAC Afterburner" +LibFDK="Codifica libfdk AAC" +Bitrate="Velocità in bit" +Afterburner="Attiva AAC Afterburner" diff --git a/plugins/obs-libfdk/obs-libfdk.c b/plugins/obs-libfdk/obs-libfdk.c index d6eb496..94b139d 100644 --- a/plugins/obs-libfdk/obs-libfdk.c +++ b/plugins/obs-libfdk/obs-libfdk.c @@ -9,7 +9,6 @@ #include - static const char *libfdk_get_error(AACENC_ERROR err) { switch(err) { @@ -224,7 +223,7 @@ static bool libfdk_encode(void *data, struct encoder_frame *frame, void *in_ptr; void *out_ptr; AACENC_ERROR err; - + int64_t encoderDelay; in_ptr = frame->data[0]; in_size = enc->frame_size_bytes; @@ -261,10 +260,13 @@ static bool libfdk_encode(void *data, struct encoder_frame *frame, } *received_packet = true; - - packet->pts = enc->total_samples - - enc->info.encoderDelay; // TODO: Just a guess, find out if that's actualy right - packet->dts = enc->total_samples - enc->info.encoderDelay; +#if (AACENCODER_LIB_VL0 >= 4) + encoderDelay= enc->info.nDelay; +#else + encoderDelay= enc->info.encoderDelay; +#endif + packet->pts = enc->total_samples - encoderDelay; + packet->dts = enc->total_samples - encoderDelay; packet->data = enc->packet_buffer; packet->size = out_args.numOutBytes; packet->type = OBS_ENCODER_AUDIO; diff --git a/plugins/obs-outputs/data/locale/bg-BG.ini b/plugins/obs-outputs/data/locale/bg-BG.ini new file mode 100644 index 0000000..888d99a --- /dev/null +++ b/plugins/obs-outputs/data/locale/bg-BG.ini @@ -0,0 +1,11 @@ +RTMPStream="RTMP поток" +RTMPStream.DropThreshold="Нисък праг (милисекунди)" +FLVOutput="Изходен файл FLV" +FLVOutput.FilePath="Път до файла" +Default="По подразбиране" + +ConnectionTimedOut="Връзката се разпадна. Уверете се, че сте настроили правилно услугата за излъчване и че връзката не е блокирана от защитна стена." +PermissionDenied="Връзката беше блокирана. Проверете Вашата защитна стена и/или антивирусна програма и се уверете, че OBS Studio има пълен достъп до интернет." +ConnectionAborted="Връзката беше прекратена. Това обикновено посочва за проблеми с интернет връзката до услугата за излъчване." +SSLCertVerifyFailed="RTMP сървърът изпрати неправилен SSL сертификат." + diff --git a/plugins/obs-outputs/data/locale/ca-ES.ini b/plugins/obs-outputs/data/locale/ca-ES.ini index 1471d15..5e942e9 100644 --- a/plugins/obs-outputs/data/locale/ca-ES.ini +++ b/plugins/obs-outputs/data/locale/ca-ES.ini @@ -9,7 +9,7 @@ PermissionDenied="La connexió ha estat bloquejada. Comproveu la configuració d ConnectionAborted="La connexió ha estat avortada. Normalment això indica que hi ha problemes de connexió entre el vostre equip i el servei de transmissió." ConnectionReset="La connexió s'ha acabat. Normalment això indica que hi ha problemes de connexió entre el vostre equip i el servei de transmissió." HostNotFound="Nom d'amfitrió no trobat. Assegureu-vos que hi hagi configurat un servidor de transmissió vàlid i que la seva connexió a Internet / DNS estiguin funcionant correctament." -NoData="Nom d'amfitrió trobat, però no hi ha dades del tipus sol·licitat. Això pot passar si heu enllaçat a una adreça IPv6 i el seu servei de transmissió només té adreces IPv4 (veure configuració / avançada)." -AddressNotAvailable="Direcció no disponible. Potser heu intentat enllaçar amb una adreça IP no vàlida (veure configuració / avançada)." +NoData="S'ha trobat el nom d'amfitrió, però no hi ha dades del tipus sol·licitat. Això pot passar si heu enllaçat a una adreça IPv6 i el seu servei de transmissió només té adreces IPv4 (veieu configuració → avançada)." +AddressNotAvailable="Adreça no disponible. Potser heu intentat enllaçar amb una adreça IP no vàlida (veieu configuració → avançada)." SSLCertVerifyFailed="El servidor RTMP ha enviat un certificat SSL no vàlid." diff --git a/plugins/obs-outputs/data/locale/cs-CZ.ini b/plugins/obs-outputs/data/locale/cs-CZ.ini index e48340d..731e9eb 100644 --- a/plugins/obs-outputs/data/locale/cs-CZ.ini +++ b/plugins/obs-outputs/data/locale/cs-CZ.ini @@ -9,7 +9,7 @@ PermissionDenied="Připojení bylo zablokováno. Zkontrolujte, zda má OBS povol 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é)." +NoData="Hostitel byl nalezen, ale žádná data požadovaného typu. Toto se může stát, pokud používáte IPv6 adresu, ale vaše vysílací služba podporuje pouze připojení přes svou IPv4 adresu (viz. Nastavení → Rozšířené)." +AddressNotAvailable="Adresa není k dispozici. Možná jste se snažili použít chybnou IP adresu (viz. Nastavení → Rozšířené)." SSLCertVerifyFailed="RTMP server odeslal neplatný SSL certifikát." diff --git a/plugins/obs-outputs/data/locale/da-DK.ini b/plugins/obs-outputs/data/locale/da-DK.ini index eb3f973..6dafcbd 100644 --- a/plugins/obs-outputs/data/locale/da-DK.ini +++ b/plugins/obs-outputs/data/locale/da-DK.ini @@ -1,15 +1,15 @@ RTMPStream="RTMP Strøm" -RTMPStream.DropThreshold="Tabstærskel (millisekunder)" -FLVOutput="FLV File Output" +RTMPStream.DropThreshold="Drop-tærskel (millisek.)" +FLVOutput="FLV-fil 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)." +ConnectionTimedOut="Forbindelsen fik timeout. Tjek, at du har opsat en gyldig streamingtjeneste samt at ingen firewall blokerer forbindelsen." +PermissionDenied="Forbindelsen blev blokeret. Tjek indstillingerne for firewall/antivirus for at sikre, at OBS har fuld Internetadgang." +ConnectionAborted="Forbindelsen blev afbrudt. Dette indikerer typisk et problem med Internetforbindelsen mellem dig og streamingtjenesten." +ConnectionReset="Forbindelsen afbrudt af modpart. Dette indikerer typisk et problem med Internetforbindelsen mellem dig og streamingtjenesten." +HostNotFound="Værtsnavn ikke fundet. Tjek, at du har angivet en gyldig streamingserver, samt at din Internetforbindelse/DNS fungerer korrekt." +NoData="Værtsnavn fundet, men ingen data af den forespurgte type. Kan f.eks. forekomme ved en tilknyttet IPv6-adresse ifm. en streamingtjeneste, der alene benytter IPv4-adresser (se Indstillinger → Avanceret)." +AddressNotAvailable="Adresse utilgængelig. En ugyldig IP-adresse kan være forsøgt tilknyttet (se Indstillinger → Avanceret)." SSLCertVerifyFailed="RTMP-serveren har sendt et ugyldig SSL-certifikat." diff --git a/plugins/obs-outputs/data/locale/de-DE.ini b/plugins/obs-outputs/data/locale/de-DE.ini index 031a6c0..e332a7a 100644 --- a/plugins/obs-outputs/data/locale/de-DE.ini +++ b/plugins/obs-outputs/data/locale/de-DE.ini @@ -1,15 +1,15 @@ -RTMPStream="RTMP Stream" -RTMPStream.DropThreshold="Drop Threshold (Millisekunden)" -FLVOutput="FLV Dateiausgabe" +RTMPStream="RTMP-Stream" +RTMPStream.DropThreshold="Drop-Threshold (Millisekunden)" +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." +ConnectionTimedOut="Zeitüberschreitung bei der Verbindung. Stellen Sie sicher, dass Sie einen gültigen Streamingdienst 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)." +ConnectionAborted="Die Verbindung wurde abgebrochen. Dies bedeutet in der Regel Probleme mit der Internetverbindung zwischen Ihnen und dem Streamingdienst." +ConnectionReset="Die Verbindung wurde durch Kommunikationspartner zurückgesetzt. Dies bedeutet in der Regel Probleme mit der Internetverbindung zwischen Ihnen und dem Streamingdienst." +HostNotFound="Hostname nicht gefunden. Stellen Sie sicher, dass Sie einen gültigen Streamingserver eingegeben haben und Ihre Internetverbindung/DNS korrekt arbeiten." +NoData="Hostname gefunden, aber keine Daten des angeforderten Typs vorhanden. Dies kann auftreten, wenn Sie eine IPv6-Adresse verwenden, aber Ihr Streamingdienst nur über IPv4-Adressen verfügt (siehe Einstellungen → Erweitert)." +AddressNotAvailable="Adresse nicht verfügbar. Sie haben möglicherweise eine ungültige IP-Adresse versucht zu verwenden (siehe Einstellungen → Erweitert)." SSLCertVerifyFailed="Der RTMP-Server hat ein ungültiges SSL-Zertifikat gesendet." diff --git a/plugins/obs-outputs/data/locale/el-GR.ini b/plugins/obs-outputs/data/locale/el-GR.ini index 3b8cb12..621bd41 100644 --- a/plugins/obs-outputs/data/locale/el-GR.ini +++ b/plugins/obs-outputs/data/locale/el-GR.ini @@ -9,6 +9,4 @@ PermissionDenied="Αποκλείστηκε η σύνδεση. Ελέγξτε τ ConnectionAborted="Η σύνδεση ματαιώθηκε. Αυτό συνήθως υποδεικνύει προβλήματα σύνδεσης στο διαδίκτυο ανάμεσα σε εσάς και την υπηρεσία συνεχούς ροής." ConnectionReset="Η σύνδεση ήταν επαναφέρθηκε στον ομότιμο υπολογιστή. Αυτό συνήθως υποδεικνύει προβλήματα σύνδεσης στο διαδίκτυο ανάμεσα σε εσάς και την υπηρεσία συνεχούς ροής." HostNotFound="Το όνομα του κεντρικού υπολογιστή δεν βρέθηκε. Βεβαιωθείτε ότι πληκτρολογήσατε έναν έγκυρο διακομιστή συνεχούς ροής και η σύνδεση στο διαδίκτυο / DNS λειτουργεί σωστά." -NoData="Το όνομα του κεντρικού υπολογιστή βρέθηκε, αλλά χωρίς δεδομένα του ζητούμενου τύπου. Αυτό μπορεί να συμβεί αν έχετε δεσμεύσει μια διεύθυνση IPv6 και για την υπηρεσία συνεχούς ροής μόνο διευθύνσεις IPv4 (ανατρέξτε στην ενότητα ρυθμίσεις)." -AddressNotAvailable="Η διεύθυνση δεν είναι διαθέσιμη. Μπορεί να έχετε δοκιμάσει να συνδεθείτε σε μια άκυρη διεύθυνση IP (βλ. ρυθμίσεις)." diff --git a/plugins/obs-outputs/data/locale/en-US.ini b/plugins/obs-outputs/data/locale/en-US.ini index 3a915ce..0e98788 100644 --- a/plugins/obs-outputs/data/locale/en-US.ini +++ b/plugins/obs-outputs/data/locale/en-US.ini @@ -9,6 +9,6 @@ PermissionDenied="The connection was blocked. Check your firewall / anti-virus s 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)." +NoData="Hostname found, but no data of the requested type. This can occur if you have bound to an IPv6 address and your streaming service only has IPv4 addresses (see Settings → Advanced)." +AddressNotAvailable="Address not available. You may have tried to bind to an invalid IP address (see Settings → Advanced)." SSLCertVerifyFailed="The RTMP server sent an invalid SSL certificate." diff --git a/plugins/obs-outputs/data/locale/es-ES.ini b/plugins/obs-outputs/data/locale/es-ES.ini index 80a91bb..49a385a 100644 --- a/plugins/obs-outputs/data/locale/es-ES.ini +++ b/plugins/obs-outputs/data/locale/es-ES.ini @@ -9,7 +9,7 @@ PermissionDenied="La conexión ha sido bloqueada. Compruebe su configuración de ConnectionAborted="La conexión ha sido abortada. Normalmente esto indica que hay problemas de conexión entre tu equipo y el servicio de transmisión." ConnectionReset="La conexión se ha terminado. Normalmente esto indica que hay problemas de conexión entre tu equipo y el servicio de transmisión." HostNotFound="Nombre de host no encontrado. Asegúrese que haya configurado un servidor de transmisión valido y que su conexión a Internet / DNS estén funcionando correctamente." -NoData="Nombre de host encontrado, pero no hay datos del tipo solicitado. Esto puede ocurrir si has enlazado a una dirección IPv6 y su servicio de streaming sólo tiene direcciones IPv4 (ver Configuración / Avanzada)." -AddressNotAvailable="Dirección no disponible. Puede que hayas intentado enlazar con una dirección IP no valida (vea Configuración / Avanzado)." +NoData="Nombre de host encontrado, pero no hay datos del tipo solicitado. Esto puede ocurrir si has enlazado a una dirección IPv6 y su servicio de streaming sólo tiene direcciones IPv4 (ver Configuración → Avanzado)." +AddressNotAvailable="Dirección no disponible. Puede que hayas intentado enlazar con una dirección IP no válida (vea Configuración → Avanzado)." SSLCertVerifyFailed="El servidor RTMP envió un certificado SSL no válido." diff --git a/plugins/obs-outputs/data/locale/fi-FI.ini b/plugins/obs-outputs/data/locale/fi-FI.ini index bed1ff4..07a0381 100644 --- a/plugins/obs-outputs/data/locale/fi-FI.ini +++ b/plugins/obs-outputs/data/locale/fi-FI.ini @@ -9,7 +9,7 @@ PermissionDenied="Yhteys estettiin. Tarkista palomuurin / virusturvan asetukset 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)." +NoData="Isäntänimi löytyi, mutta pyydetyn tyyppistä dataa ei löydetty. 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. Saatoit yrittää kiinnittää väärän IP-osoitteen (Katso Asetukset → Lisäasetukset)." SSLCertVerifyFailed="RTMP-palvelin lähetti virheellisen SSL-sertifikaatin." diff --git a/plugins/obs-outputs/data/locale/fil-PH.ini b/plugins/obs-outputs/data/locale/fil-PH.ini index dc224e9..48b23b0 100644 --- a/plugins/obs-outputs/data/locale/fil-PH.ini +++ b/plugins/obs-outputs/data/locale/fil-PH.ini @@ -1,4 +1,4 @@ -RTMPStream="Ang RTMP Stream" +RTMPStream="RTMP Stream" RTMPStream.DropThreshold="Ang Drop Treshold (millisegundos)" FLVOutput="Ang FLV File Awput" FLVOutput.FilePath="Ang Landas ng File" @@ -9,6 +9,4 @@ PermissionDenied="Ang koneksyon ay hinarang. Suriin ang firewall / anti-virus se ConnectionAborted="Ang koneksyon ay naudlot. Ito ay karaniwang nagpapahiwatig na may problema sa iyong internet koneksyon at sa streaming service." ConnectionReset="Ang koneksyon ay na i-reset ng peer. Ito ay karaniwang nagpapahiwatig na may problema sa iyong internet koneksyon at sa streaming service." HostNotFound="Hindi makita ang Hostname. Siguraduhin na nilagay mo ay balidong streaming server at ang iyong internet koneksyon / DNS ay gumagana ng mabuti." -NoData="Ang hostname ay nakita, pero walang hinihinging tipo ng datus. Ito ay ay nangyayari kapag naka bound sa IPv6 address at ang iyong streaming service ay IPv4 lamang (tignan Settings / Advanced)." -AddressNotAvailable="Hindi magamit ang address. Ikaw ay gumamit at na i-bind ito sa di balidong IP address ( tingan ang Settings / Advanced)." diff --git a/plugins/obs-outputs/data/locale/fr-FR.ini b/plugins/obs-outputs/data/locale/fr-FR.ini index 454150f..23ccfe4 100644 --- a/plugins/obs-outputs/data/locale/fr-FR.ini +++ b/plugins/obs-outputs/data/locale/fr-FR.ini @@ -1,15 +1,15 @@ RTMPStream="Flux RTMP" -RTMPStream.DropThreshold="Seuil de baisse (en millisecondes)" -FLVOutput="Fichier FLV sortant" +RTMPStream.DropThreshold="Seuil de perte de paquets (en millisecondes)" +FLVOutput="Sortie vers fichier FLV" FLVOutput.FilePath="Chemin du fichier" Default="Interface par défaut" -ConnectionTimedOut="La connexion à expiré. Assurez-vous que vous avez configuré un service de streaming valide et qu'aucun pare-feu ne bloque la connexion." -PermissionDenied="La connexion a été bloquée. Vérifiez vos paramètres de pare-feu / antivirus pour vous assurer OBS est autorisé à avoir l'accès complet d'internet." -ConnectionAborted="La connexion à été interrompue. Cela indique généralement des problèmes de connexion internet entre vous et le service de streaming." -ConnectionReset="La connexion à été interrompue. Cela indique généralement des problèmes de connexion internet entre vous et le service de diffusion." -HostNotFound="Nom d’hôte non trouvé. Assurez-vous que vous avez spécifié un serveur de diffusion valide et que votre connexion internet / DNS fonctionnent correctement." -NoData="Nom d’hôte trouvé, mais aucune donnée du type requis. Cela peut se produire si vous avez lié à une adresse IPv6 et votre service de diffusion ne possède que des adresses IPv4 (voir Paramètres / Avancé)." -AddressNotAvailable="Adresse non disponible. Vous avez peut-être essayé de la lier à une adresse IP non valide (voir Paramètres / Avancé)." +ConnectionTimedOut="La connexion a 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 qu'OBS est autorisé à avoir un accès complet à Internet." +ConnectionAborted="La connexion à été interrompue. Cela indique généralement des problèmes de connexion Internet entre vous et le service de streaming." +ConnectionReset="La connexion à été interrompue. Cela indique généralement des problèmes de connexion Internet entre vous et le service de streaming." +HostNotFound="Nom d’hôte introuvable. Vérifiez l'adresse du serveur de streaming et assurez-vous que votre connexion Internet fonctionne." +NoData="Nom d’hôte trouvé, mais aucune donnée du type requis. Cela peut se produire si vous utilisez une adresse IPv6 et que le service de streaming ne possède que des adresses IPv4 (voir Paramètres → Avancé)." +AddressNotAvailable="Adresse non disponible. Vous avez peut-être essayé de la lier à une adresse IP non valide (voir Paramètres → Avancé)." SSLCertVerifyFailed="Le serveur RTMP a fourni un certificat SSL incorrect." diff --git a/plugins/obs-outputs/data/locale/gd-GB.ini b/plugins/obs-outputs/data/locale/gd-GB.ini index 2c1f2f6..38ff6b6 100644 --- a/plugins/obs-outputs/data/locale/gd-GB.ini +++ b/plugins/obs-outputs/data/locale/gd-GB.ini @@ -4,4 +4,10 @@ FLVOutput="Às-chur faidhle FLV" FLVOutput.FilePath="Slighe an fhaidhle" Default="Bun-roghainn" +ConnectionTimedOut="Dh’fhalbh an ùine air a’ cheangal. Dèan cinnteach gun do shuidhich thu seirbheis sruthaidh dhligheach ’s nach eil cachaileith-theine a’ bacadh a’ cheangail." +PermissionDenied="Chaidh an ceangal a bhacadh. Thoir sùil air roghainnean na cachaileith-theine / a’ bhathair-bhog an aghaidh bhìorasan agad a dhèanamh cinnteach gu bheil cead làn-inntrigidh dhan eadar-lìon aig OBS." +ConnectionAborted="Chaidh sgur dhen cheangal. Dh’fhaoidte gu bheil duilgheadas leis a’ cheangal eadar-lìn eadar thu fhèin agus an t-seirbheis sruthaidh." +ConnectionReset="Chaidh an ceangal ath-shuidheachadh leis an t-seise. Dh’fhaoidte gu bheil duilgheadas leis a’ cheangal eadar-lìn eadar thu fhèin agus an t-seirbheis sruthaidh." +HostNotFound="Cha deach an t-ainm-òstair a lorg. Dèan cinnteach gun do chuir thu a-steach frithealaiche sruthaidh dligheach ’s gu bheil an ceangal gun eadar-lìn / an DNS agad ag obair mar bu chòir." +SSLCertVerifyFailed="Chuir am frithealaiche RTMP teisteanas SSL mì-dhligheach a-nall." diff --git a/plugins/obs-outputs/data/locale/hu-HU.ini b/plugins/obs-outputs/data/locale/hu-HU.ini index 631f19f..af1d54a 100644 --- a/plugins/obs-outputs/data/locale/hu-HU.ini +++ b/plugins/obs-outputs/data/locale/hu-HU.ini @@ -9,7 +9,7 @@ PermissionDenied="A kapcsolat blokkolásra került. Ellenőrizze a tűzfal / ant 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ó)." +NoData="A hostnév megtalálva, viszont a kért állománytípus nem elérhető. Ez akkor fordulhat elő, ha a IPv6 címhez van kötve, de 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 érvénytelen IP címet adott meg (Lásd: Beállítások → Haladó)." SSLCertVerifyFailed="Az RTMP kiszolgáló által küldött SSL tanúsítvány érvénytelen." diff --git a/plugins/obs-outputs/data/locale/it-IT.ini b/plugins/obs-outputs/data/locale/it-IT.ini index b9cf420..8577529 100644 --- a/plugins/obs-outputs/data/locale/it-IT.ini +++ b/plugins/obs-outputs/data/locale/it-IT.ini @@ -1,14 +1,15 @@ RTMPStream="Stream RTMP" -RTMPStream.DropThreshold="Abbassa il limite (millisecondi)" +RTMPStream.DropThreshold="Abbassa il limite (in millisecondi)" FLVOutput="Uscita file FLV" -FLVOutput.FilePath="Destinazione file" +FLVOutput.FilePath="Percorso file" Default="Predefinito" -ConnectionTimedOut="Timeout della connessione. Assicurarsi di aver configurato un valido servizio di streaming e nessun firewall sta bloccando la connessione." -PermissionDenied="La connessione è stata bloccata. Controlla il tuo firewall / impostazioni di anti-virus per assicurarsi che per OBS sia consentito accesso completo a internet." -ConnectionAborted="La connessione è stata interrotta. In genere indica problemi di connessione tra l'utente e il servizio di streaming." -ConnectionReset="La connessione è stata ripristinata dal peer. In genere indica problemi di connessione tra l'utente e il servizio di streaming." -HostNotFound="Nome host non trovato. Assicurarsi di aver inserito un valido server per lo streaming e che la connessione a internet / DNS funzioni correttamente." -NoData="Nome host trovato, ma nessun dato del tipo richiesto. Ciò può verificarsi se è stato associato a un indirizzo IPv6 e il servizio di streaming ha solo indirizzi IPv4 (vedere Impostazioni / avanzate)." -AddressNotAvailable="Indirizzo non disponibile. Si è cercato di associare un indirizzo IP non valido (vedere Impostazioni / avanzate)." +ConnectionTimedOut="La connessione è scaduta. Assicurati di aver configurato un servizio di dirette valido e controlla che non ci siano firewall che bloccano la connessione." +PermissionDenied="La connessione è stata bloccata. Controlla il tuo firewall e le impostazioni dell'antivirus, assicurati che OBS abbia l'accesso completo a internet." +ConnectionAborted="La connessione è stata interrotta. Generalmente questo problema riguarda la connessione tra l'utente e il servizio di dirette." +ConnectionReset="La connessione è stata ripristinata dal peer. Generalmente questo problema riguarda la connessione tra l'utente e il servizio di dirette." +HostNotFound="Nome dell'host non trovato. Assicurati di aver configurato un servizio di dirette valido e controlla che la connessione a internet e il tuo DNS funzionino correttamente." +NoData="Nome dell'host trovato, ma nessun dato del tipo richiesto. Ciò può verificarsi se sei collegato tramite un indirizzo IPv6 e il servizio di dirette funziona solo con indirizzi IPv4 (vai a controllare: Impostazioni → Avanzate)." +AddressNotAvailable="L'indirizzo non è disponibile. Probabilmente hai cercato di associare un indirizzo IP non valido (vai a controllare: Impostazioni → Avanzate)." +SSLCertVerifyFailed="Il server RTMP ha inviato un certificato SSL non valido." diff --git a/plugins/obs-outputs/data/locale/ja-JP.ini b/plugins/obs-outputs/data/locale/ja-JP.ini index 0366cfb..8319d4c 100644 --- a/plugins/obs-outputs/data/locale/ja-JP.ini +++ b/plugins/obs-outputs/data/locale/ja-JP.ini @@ -4,12 +4,12 @@ FLVOutput="FLV ファイル出力" FLVOutput.FilePath="ファイルのパス" Default="既定" -ConnectionTimedOut="接続がタイムアウトしました。 有効なストリーミングサービスを設定し、ファイアウォールが接続をブロックしていないことを確認してください。" +ConnectionTimedOut="接続がタイムアウトしました。 有効な配信サービスを設定し、ファイアウォールが接続をブロックしていないことを確認してください。" PermissionDenied="接続がブロックされました。 ファイアウォール/アンチウィルスの設定をチェックして、OBSにインターネットへのアクセスがすべて許可されていることを確認してください。" -ConnectionAborted="接続は中止されました。 ストリーミングサービスとの間のインターネット接続に問題があることを示しています。" -ConnectionReset="接続はピアによってリセットされました。 ストリーミングサービスとの間のインターネット接続に問題があることを示しています。" +ConnectionAborted="接続は中止されました。配信サービスとの間のインターネット接続に問題があることを示しています。" +ConnectionReset="接続はピアによってリセットされました。 配信サービスとの間のインターネット接続に問題があることを示しています。" HostNotFound="ホスト名が見つかりません。 有効なストリーミングサーバーを入力していることとインターネット接続/DNSが正しく機能していることを確認してください。" -NoData="ホスト名が見つかりましたが、要求されたタイプのデータがありません。 これはIPv6アドレスにバインドしている状態でストリーミングサービスにIPv4アドレスしかない場合に発生します。 (設定 / 詳細設定 を参照)" -AddressNotAvailable="アドレスを利用できません。 無効なIPアドレスにバインドしようとした可能性があります。 (設定 / 詳細設定 を参照)" +NoData="ホスト名が見つかりましたが、要求されたタイプのデータがありません。 これはIPv6アドレスにバインドしている状態で配信サービスにIPv4アドレスしかない場合に発生します。 (設定 → 詳細設定 を参照)" +AddressNotAvailable="アドレスを利用できません。 無効なIPアドレスにバインドしようとした可能性があります。 (設定 → 詳細設定 を参照)" SSLCertVerifyFailed="RTMPサーバーが無効なSSL証明書を送信しました。" diff --git a/plugins/obs-outputs/data/locale/ka-GE.ini b/plugins/obs-outputs/data/locale/ka-GE.ini index 933c06b..c0ed80e 100644 --- a/plugins/obs-outputs/data/locale/ka-GE.ini +++ b/plugins/obs-outputs/data/locale/ka-GE.ini @@ -1,6 +1,6 @@ -RTMPStream="RTMP ნაკადი" +RTMPStream="RTMP-ნაკადი" RTMPStream.DropThreshold="ქვედა ზღურბლი (მილიწამი)" -FLVOutput="გამომავალი FLV ფაილი" +FLVOutput="გამოტანილი FLV-ფაილი" FLVOutput.FilePath="ფაილის მისამართი" Default="ნაგულისხმევი" @@ -9,7 +9,7 @@ PermissionDenied="კავშირი შეიზღუდა. გადა ConnectionAborted="კავშირი გაუქმდა. ძირითადად, ეს მიუთითებს ინტერნეტკავშირის ხარვეზების არსებობას, თქვენსა და ნაკადის გაშვების მომსახურების მომწოდებელს შორის." ConnectionReset="კავშირი გაწყდა ერთ-ერთი მხარის მიერ. ძირითადად, ეს მიუთითებს ინტერნეტკავშირის ხარვეზების არსებობას, თქვენსა და ნაკადის გაშვების მომსახურების მომწოდებელს შორის." HostNotFound="დაკავშირების წერტილი ვერ მოიძებნა. დარწმუნდით, რომ სწორად უთითებთ ნაკადის გაშვების მომსახურების მონაცემებს და თქვენი DNS / ინტერნეტკავშირის პარამეტრებიც სწორადაა გამართული." -NoData="დაკავშირების წერტილი მოიძებნა, მაგრამ მოთხოვნილი სახის მონაცემები არა. ეს შეიძლება გამოწვეული იყოს იმით, რომ თქვენ უკავშირდებით IPv6 მისამართზე, ხოლო თქვენს ნაკადის გაშვების მომსახურებას, მხოლოდ IPv4 მისამართები გააჩნია (იხილეთ პარამეტრები / დამატებითი)." -AddressNotAvailable="მისამართი მიუწვდომელია. შესაძლოა, თქვენ ცდილობთ მცდარ IP მისამართზე დაკავშირებას (იხილეთ პარამეტრები / დამატებითი)." +NoData="დაკავშირების წერტილი მოიძებნა, მაგრამ მოთხოვნილი სახის მონაცემები არა. ეს შეიძლება გამოწვეული იყოს იმით, რომ თქვენ უკავშირდებით IPv6 მისამართზე, ხოლო თქვენს ნაკადის გაშვების მომსახურებას, მხოლოდ IPv4 მისამართები გააჩნია (იხილეთ პარამეტრები → დამატებითი)." +AddressNotAvailable="მისამართი მიუწვდომელია. შესაძლოა, თქვენ ცდილობთ მცდარ IP-მისამართზე დაკავშირებას (იხილეთ პარამეტრები → დამატებით)." SSLCertVerifyFailed="RTMP სერვერმა გაგზავნა არამართებული SSL სერტიფიკატი." diff --git a/plugins/obs-outputs/data/locale/ko-KR.ini b/plugins/obs-outputs/data/locale/ko-KR.ini index f3c114f..9330e31 100644 --- a/plugins/obs-outputs/data/locale/ko-KR.ini +++ b/plugins/obs-outputs/data/locale/ko-KR.ini @@ -9,7 +9,7 @@ PermissionDenied="연결이 차단되었습니다. 방화벽이나 백신 설정 ConnectionAborted="연결이 취소되었습니다. 보통 사용자와 방송 서비스 간의 연결 상태에 문제가 있음을 의미합니다." ConnectionReset="상호 연결 문제로 초기화되었습니다. 보통 사용자와 방송 서비스 간의 연결 상태에 문제가 있음을 의미합니다." HostNotFound="호스트 이름을 찾을 수 없습니다. 방송 서버 정보가 제대로 입력되었는지 확인하고, 인터넷 접속 혹은 DNS가 제대로 작동하고 있는지 점검하십시오." -NoData="호스트 이름은 찾았지만 요청한 형식의 데이터가 없습니다. 이 문제는 보통 사용자가 IPv6 형식의 주소를 고정하여 사용하면서 IPv4 형식의 주소만 지원하는 방송 서비스에 접속을 시도한 경우 나타납니다 (설정 / 고급 창을 확인하십시오)." -AddressNotAvailable="주소를 사용할 수 없습니다. 잘못된 IP주소를 고정하고 있습니다 (설정 / 고급 창을 확인하십시오)." +NoData="호스트 이름은 찾았지만 요청한 형식의 데이터가 없습니다. 이 문제는 보통 사용자가 IPv6 형식의 주소를 고정하여 사용하면서 IPv4 형식의 주소만 지원하는 방송 서비스에 접속을 시도한 경우 나타납니다 (설정 → 고급 창을 확인하세요)." +AddressNotAvailable="주소를 사용할 수 없습니다. 잘못된 IP주소를 고정하고 있습니다 (설정 → 고급 창을 확인하세요)." SSLCertVerifyFailed="해당 RTMP 서버는 잘못된 SSL 인증서를 보냈습니다." diff --git a/plugins/obs-outputs/data/locale/mn-MN.ini b/plugins/obs-outputs/data/locale/mn-MN.ini new file mode 100644 index 0000000..c22f94e --- /dev/null +++ b/plugins/obs-outputs/data/locale/mn-MN.ini @@ -0,0 +1,3 @@ +Default="Үндсэн" + + diff --git a/plugins/obs-outputs/data/locale/nb-NO.ini b/plugins/obs-outputs/data/locale/nb-NO.ini index 7be490c..e82bb1c 100644 --- a/plugins/obs-outputs/data/locale/nb-NO.ini +++ b/plugins/obs-outputs/data/locale/nb-NO.ini @@ -9,7 +9,7 @@ PermissionDenied="Tilkoblingen ble blokkert. Sjekk at OBS har full internettilga ConnectionAborted="Tilkoblingen ble avbrutt. Dette betyr vanligvis at det er problemer med nettverkskoblingen mellom deg og strømmetjenesten." ConnectionReset="Tilkoblingen ble avbrutt. Dette betyr vanligvis at det er problemer med nettverkskoblingen mellom deg og strømmetjenesten." HostNotFound="Tjeneren ble ikke funnet. Kontroller at du har angitt en gyldig streaming server og at tilkoblingen / DNS fungerer." -NoData="Tjeneren funnet, men ingen data for den forespurte typen. Dette kan skje hvis du har bundet til en IPv6-adresse og streaming tjeneste har bare IPv4-adresser (se Snnstillinger / Avansert)." -AddressNotAvailable="Adresse ikke tilgjengelig. Du prøvde å binde til en ugyldig IP-adresse (se Innstillinger / Avansert)." +NoData="Tjeneren funnet, men ingen data for den forespurte typen. Dette kan skje hvis du har bundet til en IPv6-adresse og streaming tjeneste har bare IPv4-adresser (se Innstillinger → Avansert)." +AddressNotAvailable="Adresse ikke tilgjengelig. Du prøvde å binde til en ugyldig IP-adresse (se Innstillinger → Avansert)." SSLCertVerifyFailed="RTMP-tjeneren sendte et ugyldig SSL-sertifikat." diff --git a/plugins/obs-outputs/data/locale/nl-NL.ini b/plugins/obs-outputs/data/locale/nl-NL.ini index e72f7a1..d4ef9b5 100644 --- a/plugins/obs-outputs/data/locale/nl-NL.ini +++ b/plugins/obs-outputs/data/locale/nl-NL.ini @@ -9,7 +9,7 @@ PermissionDenied="De verbinding was geblokkeerd. Controleer je firewall/anti-vir 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)." +NoData="Hostname gevonden, maar geen data van het verwachte type. Dit kan gebeuren als je aan een IPv6 adres hebt gebonden, en je streaming service alleen IPv4 adressen heeft (zie Instellingen → Geavanceerd)." +AddressNotAvailable="Adres niet beschikbaar. Je hebt misschien geprobeerd om aan een ongeldig IP adres te binden (zie Instellingen → Geavanceerd)." SSLCertVerifyFailed="De RTMP-server heeft een ongeldig SSL-certificaat verzonden." diff --git a/plugins/obs-outputs/data/locale/pl-PL.ini b/plugins/obs-outputs/data/locale/pl-PL.ini index 735c3c3..9012e61 100644 --- a/plugins/obs-outputs/data/locale/pl-PL.ini +++ b/plugins/obs-outputs/data/locale/pl-PL.ini @@ -1,7 +1,7 @@ RTMPStream="Strumień RTMP" RTMPStream.DropThreshold="Próg odrzucania (w milisekundach)" FLVOutput="Wyjście do pliku FLV" -FLVOutput.FilePath="Scieżka do pliku" +FLVOutput.FilePath="Ścieżka 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." @@ -9,7 +9,7 @@ PermissionDenied="Połączenie zostało zablokowane. Sprawdź stan zapory intern 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)." +NoData="Nazwa serwera została znaleziona ale nie stwierdzono poprawności odbieranych danych. Dzieje się tak najczęściej po przypisaniu aplikacji do adresu IPv6, gdy usługa strumieniowania obsługuje jedynie adresy IPv4 (zobacz Ustawienia → Zaawansowane)." +AddressNotAvailable="Adres IP niedostępny. Być może powiązano aplikację z nieprawidłowym adresem IP (zobacz Ustawienia → Zaawansowane)." SSLCertVerifyFailed="Serwer RTMP wysłał nieprawidłowy certyfikat SSL." diff --git a/plugins/obs-outputs/data/locale/pt-BR.ini b/plugins/obs-outputs/data/locale/pt-BR.ini index 7cd9b39..8e80741 100644 --- a/plugins/obs-outputs/data/locale/pt-BR.ini +++ b/plugins/obs-outputs/data/locale/pt-BR.ini @@ -9,7 +9,7 @@ PermissionDenied="A conexão foi bloqueada. Verifique seu firewall / configuraç 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)." +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 indisponível. Você pode ter tentado se vincular a um endereço IP inválido (consulte Configurações → Avançado)." SSLCertVerifyFailed="O servidor RTMP enviou um certificado SSL inválido." diff --git a/plugins/obs-outputs/data/locale/pt-PT.ini b/plugins/obs-outputs/data/locale/pt-PT.ini index c3442dd..4623c8e 100644 --- a/plugins/obs-outputs/data/locale/pt-PT.ini +++ b/plugins/obs-outputs/data/locale/pt-PT.ini @@ -2,5 +2,14 @@ RTMPStream="RTMP Stream" RTMPStream.DropThreshold="Limite de corte (milissegundos)" FLVOutput="Ficheiro de saída FLV" FLVOutput.FilePath="Caminho do ficheiro" +Default="Padrão" +ConnectionTimedOut="A ligação acabou. Verifique se você configurou um serviço de streaming válido e se nenhum firewall está bloqueando a conexão." +PermissionDenied="A ligação foi bloqueada. Verifique as configurações do seu firewall/anti-vírus para se certificar de que o OBS tem acesso total à Internet." +ConnectionAborted="A ligação foi abortada. Isso normalmente indica problemas de conexão de internet entre você e o serviço de streaming." +ConnectionReset="A ligação foi restabelecida pelo colega. Isso normalmente indica problemas de conexão de internet entre você e o serviço de streaming." +HostNotFound="Hostname não encontrado. Verifique se você digitou um servidor de streaming válido e se sua conexão de internet / DNS está funcionando corretamente." +NoData="Hostname encontrado, mas nenhum dado do tipo solicitado. Isso pode ocorrer se você tiver um endereço IPv6 e seu serviço de streaming tiver apenas endereços IPv4 (consulte Configurações → Avançado)." +AddressNotAvailable="Endereço não disponível. Você pode ter tentado vincular-se a um endereço IP inválido (consulte Configurações → Avançado)." +SSLCertVerifyFailed="O servidor RTMP enviou um certificado SSL inválido." diff --git a/plugins/obs-outputs/data/locale/ro-RO.ini b/plugins/obs-outputs/data/locale/ro-RO.ini index 9d67c92..50778e0 100644 --- a/plugins/obs-outputs/data/locale/ro-RO.ini +++ b/plugins/obs-outputs/data/locale/ro-RO.ini @@ -2,5 +2,6 @@ RTMPStream="Flux RTMP" RTMPStream.DropThreshold="Prag de pierderi (milisecunde)" FLVOutput="Ieșire fișier FLV" FLVOutput.FilePath="Calea fișierului" +Default="Implicit" diff --git a/plugins/obs-outputs/data/locale/ru-RU.ini b/plugins/obs-outputs/data/locale/ru-RU.ini index 3834a25..907e1f9 100644 --- a/plugins/obs-outputs/data/locale/ru-RU.ini +++ b/plugins/obs-outputs/data/locale/ru-RU.ini @@ -1,4 +1,4 @@ -RTMPStream="Поток RTMP" +RTMPStream="RTMP-трансляция" RTMPStream.DropThreshold="Нижний порог (мс)" FLVOutput="Выходной файл FLV" FLVOutput.FilePath="Путь к файлу" @@ -9,7 +9,7 @@ PermissionDenied="Соединение было заблокировано. Пр ConnectionAborted="Соединение было прервано. Обычно это указывает на проблемы с интернет-соединением между вами и службой вещания." ConnectionReset="Соединение было сброшено одноранговым узлом. Обычно это указывает на проблемы с интернет-соединением между вами и службой вещания." HostNotFound="Имя узла не найдено. Убедитесь, что вы ввели действительный сервер вещания и ваше подключение к интернету/DNS работают правильно." -NoData="Имя узла найдено, но нет данных запрошенного типа. Такое может случиться, если вы привязаны к IPv6-адресу, а ваш сервис вещания имеет только IPv4-адреса (смотрите Настройки - Расширенные)." -AddressNotAvailable="Адрес недоступен. Возможно вы пытались привязаться к недействительному IP-адресу (смотрите Настройки - Расширенные)." +NoData="Имя узла найдено, но нет данных запрошенного типа. Такое может случиться, если вы привязаны к IPv6-адресу, а ваш сервис вещания имеет только IPv4-адреса (смотрите Настройки → Расширенные)." +AddressNotAvailable="Адрес недоступен. Возможно вы пытались привязаться к недействительному IP-адресу (смотрите Настройки → Расширенные)." SSLCertVerifyFailed="RTMP сервер отправил недействительный сертификат SSL." diff --git a/plugins/obs-outputs/data/locale/sk-SK.ini b/plugins/obs-outputs/data/locale/sk-SK.ini index d2b5d11..a071fff 100644 --- a/plugins/obs-outputs/data/locale/sk-SK.ini +++ b/plugins/obs-outputs/data/locale/sk-SK.ini @@ -5,5 +5,4 @@ FLVOutput.FilePath="Cesta k súboru" Default="Predvolené" ConnectionReset="Pripojenie bolo resetované druhou stranou. Toto obvykle znamená, že nastali problémy s pripojením medzi vami a streaming službou." -AddressNotAvailable="Adresa nie je k dispozícii. Môžno ste sa snažili naviazať na neplatnú IP adresu (pozrite si Nastavenia / Rozšírené)." diff --git a/plugins/obs-outputs/data/locale/sr-CS.ini b/plugins/obs-outputs/data/locale/sr-CS.ini index 2310a6e..20b587f 100644 --- a/plugins/obs-outputs/data/locale/sr-CS.ini +++ b/plugins/obs-outputs/data/locale/sr-CS.ini @@ -1,7 +1,13 @@ RTMPStream="RTMP strim" -RTMPStream.DropThreshold="Tolerancija ispuštanja (milisekunde)" -FLVOutput="Izlaz u FLV datoteku" +RTMPStream.DropThreshold="Donji prag (milisekunde)" +FLVOutput="Izlaz FLV datoteke" FLVOutput.FilePath="Putanja datoteke" Default="Podrazumevani" +ConnectionTimedOut="Konekcija je istekla. Proverite da li ste podesili ispravan servis za strimovanje i da li firewall blokira konekciju." +PermissionDenied="Konekcija je blokirana. Proverite Vaša firewall / anti-virus podešavanja kako biste bili sigurni da OBS ima pristup internetu u potpunosti." +ConnectionAborted="Konekcija je obustavljena. Ovo je obično znak problema sa internet konekcijom između Vas i striming servisa." +ConnectionReset="Veza je resetovana od strane ravnopravnog uređaja. Ovo je obično znak problema sa internet konekcijom između Vas i servisa za strimovanje." +HostNotFound="Naziv hosta nije pronađen. Proverite da li ste uneli ispravan striming server i da Vaša internet konekcija i DNS rade ispravno." +SSLCertVerifyFailed="RTMP server je poslao nevažeći SSL sertifikat." diff --git a/plugins/obs-outputs/data/locale/sr-SP.ini b/plugins/obs-outputs/data/locale/sr-SP.ini index ceb2473..6b4b726 100644 --- a/plugins/obs-outputs/data/locale/sr-SP.ini +++ b/plugins/obs-outputs/data/locale/sr-SP.ini @@ -1,7 +1,13 @@ RTMPStream="RTMP стрим" -RTMPStream.DropThreshold="Толеранција испуштања (milisekunde)" -FLVOutput="Излаз у FLV датотеку" +RTMPStream.DropThreshold="Доњи праг (милисекунде)" +FLVOutput="Излаз FLV датотеке" FLVOutput.FilePath="Путања датотеке" Default="Подразумевана" +ConnectionTimedOut="Конекција је истекла. Проверите да ли сте подесили исправан сервис за стримовање и да ли firewall блокира конекцију." +PermissionDenied="Конекција је блокирана. Проверите Ваша firewall /анти-вирус подешавања како бисте били сигурни да OBS има приступ интернету у потпуности." +ConnectionAborted="Конекција је обустављена. Ово је обично знак проблема са интернет конекцијом између Вас и стриминг сервиса." +ConnectionReset="Веза је ресетована од стране равноправног уређаја. Ово је обично знак проблема са интернет конекцијом између Вас и сервиса за стримовање." +HostNotFound="Назив хоста није пронађен. Проверите да ли сте унели исправан стриминг сервер и да Ваша интернет конекција и DNS раде исправно." +SSLCertVerifyFailed="RTMP сервер је послао неважећи SSL сертификат." diff --git a/plugins/obs-outputs/data/locale/sv-SE.ini b/plugins/obs-outputs/data/locale/sv-SE.ini index f124ace..d306a83 100644 --- a/plugins/obs-outputs/data/locale/sv-SE.ini +++ b/plugins/obs-outputs/data/locale/sv-SE.ini @@ -9,7 +9,7 @@ PermissionDenied="Anslutningen blockerades. Kontrollera inställningarna för di ConnectionAborted="Anslutningen avbröts. Detta kan indikera problem med Internetanslutningen mellan dig och strömningstjänsten." ConnectionReset="Anslutningen återställdes av en peer. Detta kan indikera problem med Internetanslutningen mellan dig och strömningstjänsten." HostNotFound="Värdnamnet hittades inte. Se till att du har angivit en giltigt strömningstjänst och att din Internetanslutning / DNS fungerar på rätt sätt." -NoData="Värdnamnet hittades, men ingen data av den begärda typen. Detta kan hända om du ansluter till en IPv6-adress och din strömningstjänst endast har IPv4-adresser (gå till Inställningar / Avancerat)." -AddressNotAvailable="Adressen är inte tillgänglig. Du kanske försökte ansluta till en ogiltig IP-adress (gå till Inställningar / Avancerat)." +NoData="Värdnamnet hittades, men ingen data av den begärda typen. Detta kan förekomma om du har bundit till en IPv6-adress och din strömtjänst endast har IPv4-adresser (se Inställningar → Avancerat)." +AddressNotAvailable="Adressen är inte tillgänglig. Du kanske har försökt binda till en ogiltig IP-adress (se Inställningar → Avancerat)." SSLCertVerifyFailed="RTMP-servern skickade ett ogiltigt SSL-certifikat." diff --git a/plugins/obs-outputs/data/locale/tl-PH.ini b/plugins/obs-outputs/data/locale/tl-PH.ini index 8ae0ace..1e01e19 100644 --- a/plugins/obs-outputs/data/locale/tl-PH.ini +++ b/plugins/obs-outputs/data/locale/tl-PH.ini @@ -9,6 +9,4 @@ PermissionDenied="Ang koneksyon ay na-block. I-check ang iyong firewall / anti-v ConnectionAborted="Ang koneksyon ay nabigo. Ito ay kadalasang indikasyon na ang internet nakoneksyon ay may problema sa'yo o kay sa serbisyo ng streaming." ConnectionReset="Ang koneksyon ay ni-reset ng peer. Ito ay kadalasang indikasyon na ang internet nakoneksyon ay may problema sa'yo o kay sa serbisyo ng streaming." HostNotFound="Ang hostname ay hindi matagpuan. Siguraduhing ang nilagay mo ay isang balidong stereaming server at ang iyong internet koneksyon / DNS ay gumagana ng sakto." -NoData="Ang hostname ay natagpuan, ngunit walang data sa ni-request na tipo. Ito ay pwedeng mangyari kung wala kang bound para sa IPv6 na address at ang iyong streaming service ay meron lang IPv4 na mga address (tingnan ang Setting / Advanced)." -AddressNotAvailable="Ang address ay hindi pwede. Pwede kang sumubok na i-bind sa isang hindi balidong IP address (tingnan ang Setting / Advanced)." diff --git a/plugins/obs-outputs/data/locale/tr-TR.ini b/plugins/obs-outputs/data/locale/tr-TR.ini index 61aa6c6..e96d745 100644 --- a/plugins/obs-outputs/data/locale/tr-TR.ini +++ b/plugins/obs-outputs/data/locale/tr-TR.ini @@ -9,7 +9,7 @@ PermissionDenied="Bağlantı engellendi. Güvenlik duvarı / virüs koruma ayarl 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ş)." +NoData="Ana bilgisayar adı bulundu, ancak istenen türde veri bulunamadı. Bu bir IPv6 adresine bağlamış ve yayın servisinizin sadece IPv4 adresleri varsa oluşabilir (bkz: Ayarlar → Gelişmiş)." +AddressNotAvailable="Adres kullanılamaz. Geçersiz bir IP adresi bağlamayı denemiş olabilirsiniz (bakın: Ayarlar → Gelişmiş)." SSLCertVerifyFailed="RTMP sunucusu geçersiz bir SSL sertifikası gönderdi." diff --git a/plugins/obs-outputs/data/locale/uk-UA.ini b/plugins/obs-outputs/data/locale/uk-UA.ini index 6ab98ef..a5e4dff 100644 --- a/plugins/obs-outputs/data/locale/uk-UA.ini +++ b/plugins/obs-outputs/data/locale/uk-UA.ini @@ -9,7 +9,7 @@ PermissionDenied="З'єднання було заблоковано. Перев ConnectionAborted="З'єднання було перервано. Зазвичай свідчить про проблеми з Інтернет підключенням між вами і постачальником з сервісу трансляцій." ConnectionReset="З'єднання було скинуте рівноправним вузлом (reset by peer). Зазвичай свідчить про проблеми з Інтернет підключенням між вами і постачальником з сервісу трансляцій." HostNotFound="Ім'я хоста, не знайдено. Переконайтеся, що ви ввели дійсний сервер трансляцій і підключення до Інтернету / DNS працює правильно." -NoData="Ім'я хоста знайдено, але нема жодних даних вказаного типу. Це може статися, якщо ви вказали прив'язку до IPv6-адресу, але ваш сервіс трансляцій підтримує лише адреси IPv4 (див. Налаштування / Розширені)." -AddressNotAvailable="Адреса недоступна. Напевно ви спробували прив'язатись до адаптера з неіснуючую IP-адресою (див. Налаштування / Розширені)." +NoData="Ім'я хоста знайдено, але нема жодних даних вказаного типу. Це може статися, якщо ви вказали прив'язку до IPv6-адресу, але ваш сервіс трансляцій підтримує лише адреси IPv4 (див. Налаштування → Розширені)." +AddressNotAvailable="Адреса недоступна. Напевно ви спробували прив'язатись до адаптера з неіснуючою IP-адресою (див. Налаштування → Розширені)." SSLCertVerifyFailed="RTMP сервер надіслав неприпустимий сертифікат SSL." diff --git a/plugins/obs-outputs/data/locale/vi-VN.ini b/plugins/obs-outputs/data/locale/vi-VN.ini index 5b2e11f..9a6d3b8 100644 --- a/plugins/obs-outputs/data/locale/vi-VN.ini +++ b/plugins/obs-outputs/data/locale/vi-VN.ini @@ -1,4 +1,4 @@ -RTMPStream="RTMP Stream" +RTMPStream="Luồng RTMP" RTMPStream.DropThreshold="Drop Threshold (mili giây)" FLVOutput="FLV tập tin đầu ra" FLVOutput.FilePath="Đường dẫn tệp" @@ -9,6 +9,4 @@ PermissionDenied="Kết nối đã bị chặn. Hãy kiểm tra tường lửa / ConnectionAborted="Kết nối đã bị hủy bỏ. Điều này thường chỉ ra kết nối internet giữa bạn và dịch vụ trực tuyến có vấn đề." ConnectionReset="Kết nối đã được đặt lại bởi peer. Điều này thường chỉ ra các sự cố kết nối Internet giữa bạn và dịch vụ truyền trực tuyến." HostNotFound="Tên máy chủ không tìm thấy. Đảm bảo rằng bạn đã nhập vào một máy chủ stream hợp lệ và kết nối internet của bạn / DNS đang hoạt động tốt." -NoData="Tên máy chủ được tìm thấy nhưng không có dữ liệu được yêu cầu. Điều này có thể xảy ra nếu bạn sử dụng địa chỉ IPv6 và dịch vụ stream của bạn chỉ có địa chỉ IPv4 (xem Cài đặt / Nâng cao)." -AddressNotAvailable="Địa chỉ không có sẵn. Bạn có thể đã cố gắng liên kết với một địa chỉ IP không hợp lệ (xem Cài đặt / Nâng cao)." diff --git a/plugins/obs-outputs/data/locale/zh-CN.ini b/plugins/obs-outputs/data/locale/zh-CN.ini index e8a99af..39c959e 100644 --- a/plugins/obs-outputs/data/locale/zh-CN.ini +++ b/plugins/obs-outputs/data/locale/zh-CN.ini @@ -4,12 +4,12 @@ FLVOutput="FLV 文件输出" FLVOutput.FilePath="文件路径" Default="默认" -ConnectionTimedOut="连接超时. 请确保您已经配置了一个有效的流媒体服务并且没有防火墙阻止连接." -PermissionDenied="连接被阻止. 检查您的防火墙 / 防病毒设置以确保允许 OBS 自由访问互联网." -ConnectionAborted="连接被中止. 这通常表明你和流媒体服务之间的互联网连接问题." -ConnectionReset="对方重置连接. 这通常表明你和流媒体服务之间的互联网连接问题." -HostNotFound="找不到 Hostname. 请确保您输入一个有效的流媒体服务器并且您的互联网连接 / DNS 工作正常." -NoData="Hostname 发现, 但没有请求的类型的数据的主机名. 这有可能因为你绑定到 IPv6 地址并且你的流媒体服务仅有 IPv4 地址 (请参阅设置 / 高级)." -AddressNotAvailable="没有可用的地址. 你可能在试图绑定到一个无效的 IP 地址 (请参阅设置 / 高级)." +ConnectionTimedOut="连接超时。请确保您已经配置了一个有效的流媒体服务并且没有防火墙阻止连接。" +PermissionDenied="连接被阻止。检查您的防火墙 / 防病毒设置以确保允许 OBS 自由访问互联网。" +ConnectionAborted="连接被中止。这通常表明你和流媒体服务器间的互联网存在连接问题。" +ConnectionReset="对方重置了连接。这通常表明你和流媒体服务器间的互联网存在连接问题。" +HostNotFound="找不到主机名。请确保您输入了有效的流媒体服务器并且您的互联网连接 / DNS 工作正常。" +NoData="找到主机名,但没有所请求类型的数据。如果绑定到 IPv6 地址并且流式服务只有 IPv4 地址,则可能发生这种情况(请参阅设置→高级)。" +AddressNotAvailable="地址不可用。您可能试图绑定到一个无效的 IP 地址(请参阅设置→高级)。" SSLCertVerifyFailed="RTMP 服务器发送了无效的 SSL 证书。" diff --git a/plugins/obs-outputs/data/locale/zh-TW.ini b/plugins/obs-outputs/data/locale/zh-TW.ini index 15d5fc6..b444073 100644 --- a/plugins/obs-outputs/data/locale/zh-TW.ini +++ b/plugins/obs-outputs/data/locale/zh-TW.ini @@ -9,7 +9,7 @@ PermissionDenied="連線被阻擋。請檢查防火牆 / 防毒設定以確保 O ConnectionAborted="連線被中止。通常這代表您與串流服務之間有網際網路連線問題。" ConnectionReset="連線被對方重置。通常這代表您與串流服務之間有網際網路連線問題。" HostNotFound="找不到主機名稱。請確定輸入了一個有效的串流服務器且網路連線跟 DNS 工作正常。" -NoData="找到主機名稱,但沒有要求類型的資料。這可能發生在您綁定於 IPv6 位址但串流服務只有 IPv4 位址 (請看 設定/進階)。" -AddressNotAvailable="位址不可用。可能因為嘗試綁定到一個不正確 IP 位址(請確認 設定/進階 的設定)。" +NoData="找到主機名稱,但沒有要求類型的資料。這可能會在您綁定於 IPv6 位址但串流服務只有 IPv4 位址時發生(請看 設定 → 進階)。" +AddressNotAvailable="無法使用位址。可能是因為嘗試綁定到不正確的 IP 位址(請檢視 設定 → 進階 的設定)。" SSLCertVerifyFailed="RTMP 伺服器發送了一則不合法的 SSL 憑證。" diff --git a/plugins/obs-outputs/flv-output.c b/plugins/obs-outputs/flv-output.c index 680a954..de093aa 100644 --- a/plugins/obs-outputs/flv-output.c +++ b/plugins/obs-outputs/flv-output.c @@ -193,7 +193,7 @@ static void flv_output_stop(void *data, uint64_t ts) os_atomic_set_bool(&stream->stopping, true); } -static void flv_output_actual_stop(struct flv_output *stream) +static void flv_output_actual_stop(struct flv_output *stream, int code) { os_atomic_set_bool(&stream->active, false); @@ -203,7 +203,11 @@ static void flv_output_actual_stop(struct flv_output *stream) fclose(stream->file); } - obs_output_end_data_capture(stream->output); + if (code) { + obs_output_signal_stop(stream->output, code); + } else { + obs_output_end_data_capture(stream->output); + } info("FLV file output complete"); } @@ -218,9 +222,14 @@ static void flv_output_data(void *data, struct encoder_packet *packet) if (!active(stream)) goto unlock; + if (!packet) { + flv_output_actual_stop(stream, OBS_OUTPUT_ENCODE_ERROR); + goto unlock; + } + if (stopping(stream)) { if (packet->sys_dts_usec >= (int64_t)stream->stop_ts) { - flv_output_actual_stop(stream); + flv_output_actual_stop(stream, 0); goto unlock; } } diff --git a/plugins/obs-outputs/ftl-stream.c b/plugins/obs-outputs/ftl-stream.c index 1ea06de..6bd155f 100644 --- a/plugins/obs-outputs/ftl-stream.c +++ b/plugins/obs-outputs/ftl-stream.c @@ -72,6 +72,7 @@ struct ftl_stream { volatile bool active; volatile bool disconnected; + volatile bool encode_error; pthread_t send_thread; int max_shutdown_time_sec; @@ -516,8 +517,12 @@ static void *send_thread(void *data) } } + bool encode_error = os_atomic_load_bool(&stream->encode_error); + if (disconnected(stream)) { info("Disconnected from %s", stream->path.array); + } else if (encode_error) { + info("Encoder error, disconnecting"); } else { info("User stopped the stream"); } @@ -525,6 +530,8 @@ static void *send_thread(void *data) if (!stopping(stream)) { pthread_detach(stream->send_thread); obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); + } else if (encode_error) { + obs_output_signal_stop(stream->output, OBS_OUTPUT_ENCODE_ERROR); } else { obs_output_end_data_capture(stream->output); } @@ -809,6 +816,13 @@ static void ftl_stream_data(void *data, struct encoder_packet *packet) if (disconnected(stream) || !active(stream)) return; + /* encoder failure */ + if (!packet) { + os_atomic_set_bool(&stream->encode_error, true); + os_sem_post(stream->send_sem); + return; + } + if (packet->type == OBS_ENCODER_VIDEO) obs_parse_avc_packet(&new_packet, packet); else @@ -1034,6 +1048,7 @@ static int init_connect(struct ftl_stream *stream) } os_atomic_set_bool(&stream->disconnected, false); + os_atomic_set_bool(&stream->encode_error, false); stream->total_bytes_sent = 0; stream->dropped_frames = 0; stream->min_priority = 0; diff --git a/plugins/obs-outputs/librtmp/handshake.h b/plugins/obs-outputs/librtmp/handshake.h index 8cd3e32..f14a0a8 100644 --- a/plugins/obs-outputs/librtmp/handshake.h +++ b/plugins/obs-outputs/librtmp/handshake.h @@ -34,9 +34,9 @@ typedef mbedtls_md_context_t *HMAC_CTX; #define HMAC_setup(ctx, key, len) ctx = malloc(sizeof(mbedtls_md_context_t)); mbedtls_md_init(ctx); \ mbedtls_md_setup(ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); \ mbedtls_md_hmac_starts(ctx, (const unsigned char *)key, len) -#define HMAC_crunch(ctx, buf, len) mbedtls_md_hmac_update(ctx, buf, len) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; mbedtls_md_hmac_finish(ctx, dig) -#define HMAC_close(ctx) mbedtls_md_free(ctx); free(ctx); ctx = NULL +#define HMAC_crunch(ctx, buf, len) mbedtls_md_hmac_update(ctx, buf, len) +#define HMAC_finish(ctx, dig) mbedtls_md_hmac_finish(ctx, dig) +#define HMAC_close(ctx) mbedtls_md_free(ctx); free(ctx); ctx = NULL typedef mbedtls_arc4_context* RC4_handle; #define RC4_alloc(h) *h = malloc(sizeof(mbedtls_arc4_context)); mbedtls_arc4_init(*h) @@ -54,7 +54,7 @@ typedef mbedtls_arc4_context* RC4_handle; #define HMAC_CTX sha2_context #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0) #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig) +#define HMAC_finish(ctx, dig) sha2_hmac_finish(&ctx, dig) typedef arc4_context * RC4_handle; #define RC4_alloc(h) *h = malloc(sizeof(arc4_context)) @@ -73,7 +73,7 @@ typedef arc4_context * RC4_handle; #define HMAC_CTX struct hmac_sha256_ctx #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key) #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig) +#define HMAC_finish(ctx, dig) hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig) #define HMAC_close(ctx) typedef struct arcfour_ctx* RC4_handle; @@ -92,7 +92,7 @@ typedef struct arcfour_ctx* RC4_handle; #endif #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, EVP_sha256(), 0) #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, buf, len) -#define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx) +#define HMAC_finish(ctx, dig, len) HMAC_Final(&ctx, dig, &len); HMAC_CTX_cleanup(&ctx) typedef RC4_KEY * RC4_handle; #define RC4_alloc(h) *h = malloc(sizeof(RC4_KEY)) @@ -139,7 +139,9 @@ static void InitRC4Encryption uint8_t * pubKeyOut, RC4_handle *rc4keyIn, RC4_handle *rc4keyOut) { uint8_t digest[SHA256_DIGEST_LENGTH]; +#if !(defined(USE_MBEDTLS) || defined(USE_POLARSSL) || defined(USE_GNUTLS)) unsigned int digestLen = 0; +#endif HMAC_CTX ctx; RC4_alloc(rc4keyIn); @@ -147,7 +149,11 @@ static void InitRC4Encryption HMAC_setup(ctx, secretKey, 128); HMAC_crunch(ctx, pubKeyIn, 128); +#if defined(USE_MBEDTLS) || defined(USE_POLARSSL) || defined(USE_GNUTLS) + HMAC_finish(ctx, digest); +#else HMAC_finish(ctx, digest, digestLen); +#endif RTMP_Log(RTMP_LOGDEBUG, "RC4 Out Key: "); RTMP_LogHex(RTMP_LOGDEBUG, digest, 16); @@ -156,7 +162,11 @@ static void InitRC4Encryption HMAC_setup(ctx, secretKey, 128); HMAC_crunch(ctx, pubKeyOut, 128); +#if defined(USE_MBEDTLS) || defined(USE_POLARSSL) || defined(USE_GNUTLS) + HMAC_finish(ctx, digest); +#else HMAC_finish(ctx, digest, digestLen); +#endif RTMP_Log(RTMP_LOGDEBUG, "RC4 In Key: "); RTMP_LogHex(RTMP_LOGDEBUG, digest, 16); @@ -303,7 +313,13 @@ HMACsha256(const uint8_t *message, size_t messageLen, const uint8_t *key, HMAC_setup(ctx, key, keylen); HMAC_crunch(ctx, message, messageLen); + +#if defined(USE_MBEDTLS) || defined(USE_POLARSSL) || defined(USE_GNUTLS) + digestLen = SHA256_DIGEST_LENGTH; + HMAC_finish(ctx, digest); +#else HMAC_finish(ctx, digest, digestLen); +#endif assert(digestLen == 32); } diff --git a/plugins/obs-outputs/librtmp/hashswf.c b/plugins/obs-outputs/librtmp/hashswf.c index b6c2b61..bbc8d31 100644 --- a/plugins/obs-outputs/librtmp/hashswf.c +++ b/plugins/obs-outputs/librtmp/hashswf.c @@ -39,9 +39,9 @@ typedef mbedtls_md_context_t *HMAC_CTX; #define HMAC_setup(ctx, key, len) ctx = malloc(sizeof(mbedtls_md_context_t)); mbedtls_md_init(ctx); \ mbedtls_md_setup(ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); \ mbedtls_md_hmac_starts(ctx, (const unsigned char *)key, len) -#define HMAC_crunch(ctx, buf, len) mbedtls_md_hmac_update(ctx, buf, len) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; mbedtls_md_hmac_finish(ctx, dig) -#define HMAC_close(ctx) free(ctx); mbedtls_md_free(ctx); ctx = NULL +#define HMAC_crunch(ctx, buf, len) mbedtls_md_hmac_update(ctx, buf, len) +#define HMAC_finish(ctx, dig) mbedtls_md_hmac_finish(ctx, dig) +#define HMAC_close(ctx) free(ctx); mbedtls_md_free(ctx); ctx = NULL #elif defined(USE_POLARSSL) #include @@ -51,7 +51,7 @@ typedef mbedtls_md_context_t *HMAC_CTX; #define HMAC_CTX sha2_context #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0) #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig) +#define HMAC_finish(ctx, dig) sha2_hmac_finish(&ctx, dig) #define HMAC_close(ctx) #elif defined(USE_GNUTLS) @@ -63,7 +63,7 @@ typedef mbedtls_md_context_t *HMAC_CTX; #define HMAC_CTX struct hmac_sha256_ctx #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key) #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig) +#define HMAC_finish(ctx, dig) hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig) #define HMAC_close(ctx) #else /* USE_OPENSSL */ @@ -73,7 +73,7 @@ typedef mbedtls_md_context_t *HMAC_CTX; #include #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0) #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len) -#define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen); +#define HMAC_finish(ctx, dig, len) HMAC_Final(&ctx, (unsigned char *)dig, &len); #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx) #endif @@ -658,7 +658,11 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, if (!in.first) { +#if defined(USE_MBEDTLS) || defined(USE_POLARSSL) || defined(USE_GNUTLS) + HMAC_finish(in.ctx, hash); +#else HMAC_finish(in.ctx, hash, hlen); +#endif *size = in.size; fprintf(f, "date: %s\n", date); diff --git a/plugins/obs-outputs/librtmp/rtmp.c b/plugins/obs-outputs/librtmp/rtmp.c index 09e965e..289ab51 100644 --- a/plugins/obs-outputs/librtmp/rtmp.c +++ b/plugins/obs-outputs/librtmp/rtmp.c @@ -34,6 +34,10 @@ #include +#if !defined(MSG_NOSIGNAL) +#define MSG_NOSIGNAL 0 +#endif + #ifdef CRYPTO #ifdef __APPLE__ @@ -242,8 +246,10 @@ int RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize) { char *ptr; +#if ARCH_BITS == 32 if (nSize > SIZE_MAX - RTMP_MAX_HEADER_SIZE) return FALSE; +#endif ptr = calloc(1, nSize + RTMP_MAX_HEADER_SIZE); if (!ptr) @@ -928,6 +934,11 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service, socklen_t addrlen) if (r->m_sb.sb_socket != INVALID_SOCKET) { +#ifndef _WIN32 +#ifdef SO_NOSIGPIPE + setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof(int)); +#endif +#endif if(r->m_bindIP.addrLen) { if (bind(r->m_sb.sb_socket, (const struct sockaddr *)&r->m_bindIP.addr, r->m_bindIP.addrLen) < 0) @@ -4587,7 +4598,7 @@ RTMPSockBuf_Fill(RTMPSockBuf *sb) else #endif { - nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0); + nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, MSG_NOSIGNAL); } if (nBytes > 0) { @@ -4640,7 +4651,7 @@ RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len) else #endif { - rc = send(sb->sb_socket, buf, len, 0); + rc = send(sb->sb_socket, buf, len, MSG_NOSIGNAL); } return rc; } diff --git a/plugins/obs-outputs/librtmp/rtmp.h b/plugins/obs-outputs/librtmp/rtmp.h index bd3abe6..dd90fb0 100644 --- a/plugins/obs-outputs/librtmp/rtmp.h +++ b/plugins/obs-outputs/librtmp/rtmp.h @@ -381,6 +381,7 @@ extern "C" void RTMP_Init(RTMP *r); void RTMP_Close(RTMP *r); RTMP *RTMP_Alloc(void); + void RTMP_TLS_Free(); void RTMP_Free(RTMP *r); void RTMP_EnableWrite(RTMP *r); diff --git a/plugins/obs-outputs/net-if.c b/plugins/obs-outputs/net-if.c index f467f2a..9f91cad 100644 --- a/plugins/obs-outputs/net-if.c +++ b/plugins/obs-outputs/net-if.c @@ -175,7 +175,7 @@ static inline PIP_ADAPTER_ADDRESSES get_adapters(void) { PIP_ADAPTER_ADDRESSES adapter = NULL; unsigned long ret = 0; - unsigned long out_buf_len = 4096; + unsigned long out_buf_len = 16384; unsigned long flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c index 43e0e89..e8fbe48 100644 --- a/plugins/obs-outputs/obs-outputs.c +++ b/plugins/obs-outputs/obs-outputs.c @@ -9,6 +9,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-outputs", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "OBS core RTMP/FLV/null/FTL outputs"; +} extern struct obs_output_info rtmp_output_info; extern struct obs_output_info null_output_info; diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index def2788..93629df 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -92,6 +92,7 @@ static void rtmp_stream_destroy(void *data) } } + RTMP_TLS_Free(); free_packets(stream); dstr_free(&stream->path); dstr_free(&stream->key); @@ -486,8 +487,12 @@ static void *send_thread(void *data) } } + bool encode_error = os_atomic_load_bool(&stream->encode_error); + if (disconnected(stream)) { info("Disconnected from %s", stream->path.array); + } else if (encode_error) { + info("Encoder error, disconnecting"); } else { info("User stopped the stream"); } @@ -506,6 +511,8 @@ static void *send_thread(void *data) if (!stopping(stream)) { pthread_detach(stream->send_thread); obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); + } else if (encode_error) { + obs_output_signal_stop(stream->output, OBS_OUTPUT_ENCODE_ERROR); } else { obs_output_end_data_capture(stream->output); } @@ -885,6 +892,7 @@ static bool init_connect(struct rtmp_stream *stream) return false; os_atomic_set_bool(&stream->disconnected, false); + os_atomic_set_bool(&stream->encode_error, false); stream->total_bytes_sent = 0; stream->dropped_frames = 0; stream->min_priority = 0; @@ -1102,6 +1110,13 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) if (disconnected(stream) || !active(stream)) return; + /* encoder fail */ + if (!packet) { + os_atomic_set_bool(&stream->encode_error, true); + os_sem_post(stream->send_sem); + return; + } + if (packet->type == OBS_ENCODER_VIDEO) { if (!stream->got_first_video) { stream->start_dts_offset = diff --git a/plugins/obs-outputs/rtmp-stream.h b/plugins/obs-outputs/rtmp-stream.h index 1c4715a..77d1b44 100644 --- a/plugins/obs-outputs/rtmp-stream.h +++ b/plugins/obs-outputs/rtmp-stream.h @@ -59,6 +59,7 @@ struct rtmp_stream { volatile bool active; volatile bool disconnected; + volatile bool encode_error; pthread_t send_thread; int max_shutdown_time_sec; diff --git a/plugins/obs-text/data/locale/ar-SA.ini b/plugins/obs-text/data/locale/ar-SA.ini new file mode 100644 index 0000000..e7936ec --- /dev/null +++ b/plugins/obs-text/data/locale/ar-SA.ini @@ -0,0 +1,34 @@ +TextGDIPlus="نص (GDI+)" +Font="الخط" +Text="النص" +ReadFromFile="قراءة من ملف" +TextFile="ملف نصي (UTF-8)" +Filter.TextFiles="ملفات نصية" +Filter.AllFiles="جميع أنواع الملفات" +Color="اللون" +Opacity="الشفافيّة" +Gradient="تدرج" +Gradient.Color="لون التدرج" +Gradient.Opacity="شفافية التدرج" +Gradient.Direction="إتجاه التدرج" +BkColor="لون الخلفية" +BkOpacity="شفافية الخلفية" +Alignment="المحاذاة" +Alignment.Left="يسار" +Alignment.Center="وسط" +Alignment.Right="يمين" +Vertical="عمودياً/رأسياً" +VerticalAlignment="المحاذاة العمودية" +VerticalAlignment.Top="إلى أعلى" +VerticalAlignment.Bottom="إلى أسفل" +Outline="حد خارجي" +Outline.Size="حجم الحد الخارجي" +Outline.Color="لون الحد الخارجي" +Outline.Opacity="شفافية الحد الخارجي" +ChatlogMode="وضعية الدردشة" +ChatlogMode.Lines="الحد الأقصى لعدد الاسطر" +UseCustomExtents="استخدام أبعاد مخصصة لصندوق النص" +UseCustomExtents.Wrap="التفاف النص" +Width="العرض" +Height="الارتفاع" + diff --git a/plugins/obs-text/data/locale/ca-ES.ini b/plugins/obs-text/data/locale/ca-ES.ini index 0c81137..e7a6c22 100644 --- a/plugins/obs-text/data/locale/ca-ES.ini +++ b/plugins/obs-text/data/locale/ca-ES.ini @@ -31,4 +31,8 @@ UseCustomExtents="Utilitza extensions de text personalitzat" UseCustomExtents.Wrap="Ajusta" Width="Amplada" Height="Alçada" +Transform="Transformació de text" +Transform.None="Cap" +Transform.Uppercase="Majúscula" +Transform.Lowercase="Minúscula" diff --git a/plugins/obs-text/data/locale/cs-CZ.ini b/plugins/obs-text/data/locale/cs-CZ.ini index 9a0d01c..4abd92b 100644 --- a/plugins/obs-text/data/locale/cs-CZ.ini +++ b/plugins/obs-text/data/locale/cs-CZ.ini @@ -31,4 +31,8 @@ UseCustomExtents="Použít vlastní rozsah textu" UseCustomExtents.Wrap="Zalomit" Width="Šířka" Height="Výška" +Transform="Transformace textu" +Transform.None="Žádná" +Transform.Uppercase="VELKÝMI PÍSMENY" +Transform.Lowercase="malými písmeny" diff --git a/plugins/obs-text/data/locale/da-DK.ini b/plugins/obs-text/data/locale/da-DK.ini index f914eec..f8fed23 100644 --- a/plugins/obs-text/data/locale/da-DK.ini +++ b/plugins/obs-text/data/locale/da-DK.ini @@ -8,11 +8,11 @@ Filter.AllFiles="Alle filer" Color="Farve" Opacity="Gennemsigtighed" Gradient="Gradient" -Gradient.Color="Gradient farve" -Gradient.Opacity="Gradient gennemsigtighed" -Gradient.Direction="Gradient retning" +Gradient.Color="Gradientfarve" +Gradient.Opacity="Gradientgennemsigtighed" +Gradient.Direction="Gradientretning" BkColor="Baggrundsfarve" -BkOpacity="Baggrunds gennemsigtighed" +BkOpacity="Baggrundsgennemsigtighed" Alignment="Justering" Alignment.Left="Venstre" Alignment.Center="Centreret" @@ -22,13 +22,17 @@ VerticalAlignment="Vertikal justering" VerticalAlignment.Top="Top" VerticalAlignment.Bottom="Bund" Outline="Kontur" -Outline.Size="Kontur størrelse" -Outline.Color="Kontur farve" -Outline.Opacity="Kontur gennemsigtighed" -ChatlogMode="Chatlog tilstand" -ChatlogMode.Lines="Chatlog linebegrænsning" -UseCustomExtents="Brug tilpasset tekstomfang" +Outline.Size="Konturstørrelse" +Outline.Color="Konturfarve" +Outline.Opacity="Konturgennemsigtighed" +ChatlogMode="Chatlog-tilstand" +ChatlogMode.Lines="Chatlog-linebegrænsning" +UseCustomExtents="Benyt tilpasset tekstomfang" UseCustomExtents.Wrap="Ombryd" Width="Bredde" Height="Højde" +Transform="Teksttransformation" +Transform.None="Ingen" +Transform.Uppercase="Versaler" +Transform.Lowercase="Minuskler" diff --git a/plugins/obs-text/data/locale/de-DE.ini b/plugins/obs-text/data/locale/de-DE.ini index b8980fc..ee44b97 100644 --- a/plugins/obs-text/data/locale/de-DE.ini +++ b/plugins/obs-text/data/locale/de-DE.ini @@ -25,10 +25,14 @@ Outline="Kontur" Outline.Size="Konturgröße" Outline.Color="Konturfarbe" Outline.Opacity="Deckkraft der Kontur" -ChatlogMode="Chatlog-Modus" -ChatlogMode.Lines="Chatlog Zeilenlimit" -UseCustomExtents="Nutze benutzerdefinierten Textbereich" +ChatlogMode="Chatprotokollmodus" +ChatlogMode.Lines="Chatprotokollzeilenlimit" +UseCustomExtents="Benutzerdefinierten Textbereich benutzen" UseCustomExtents.Wrap="Umbruch" Width="Breite" Height="Höhe" +Transform="Texttransformation" +Transform.None="Keine" +Transform.Uppercase="Großbuchstaben" +Transform.Lowercase="Kleinbuchstaben" diff --git a/plugins/obs-text/data/locale/en-US.ini b/plugins/obs-text/data/locale/en-US.ini index d048ce7..260243b 100644 --- a/plugins/obs-text/data/locale/en-US.ini +++ b/plugins/obs-text/data/locale/en-US.ini @@ -31,3 +31,7 @@ UseCustomExtents="Use Custom Text Extents" UseCustomExtents.Wrap="Wrap" Width="Width" Height="Height" +Transform="Text Transform" +Transform.None="None" +Transform.Uppercase="Uppercase" +Transform.Lowercase="Lowercase" diff --git a/plugins/obs-text/data/locale/es-ES.ini b/plugins/obs-text/data/locale/es-ES.ini index 35cea31..d02ed34 100644 --- a/plugins/obs-text/data/locale/es-ES.ini +++ b/plugins/obs-text/data/locale/es-ES.ini @@ -31,4 +31,8 @@ UseCustomExtents="Usar extensiones de texto personalizado" UseCustomExtents.Wrap="Ajustar" Width="Ancho" Height="Alto" +Transform="Transformación de Texto" +Transform.None="Ninguno" +Transform.Uppercase="Mayúscula" +Transform.Lowercase="Minúscula" diff --git a/plugins/obs-text/data/locale/eu-ES.ini b/plugins/obs-text/data/locale/eu-ES.ini index 6c998b0..a03b482 100644 --- a/plugins/obs-text/data/locale/eu-ES.ini +++ b/plugins/obs-text/data/locale/eu-ES.ini @@ -31,4 +31,8 @@ UseCustomExtents="Erabili testu hedapen pertsonalak" UseCustomExtents.Wrap="Egokitu" Width="Zabalera" Height="Altuera" +Transform="Testu-eraldaketa" +Transform.None="Gabe" +Transform.Uppercase="Hizki-larriak" +Transform.Lowercase="Hizki-xeheak" diff --git a/plugins/obs-text/data/locale/fa-IR.ini b/plugins/obs-text/data/locale/fa-IR.ini new file mode 100644 index 0000000..b7d7218 --- /dev/null +++ b/plugins/obs-text/data/locale/fa-IR.ini @@ -0,0 +1,38 @@ +TextGDIPlus="متن (GDI+)" +Font="فونت" +Text="متن" +ReadFromFile="از فایل بخوان" +TextFile="فایل متن (UTF-8)" +Filter.TextFiles="پرونده های متنی" +Filter.AllFiles="همه فایل ها" +Color="رنگ" +Opacity="شفافیت" +Gradient="شیب" +Gradient.Color="رنگ شیب" +Gradient.Opacity="شفافیت شیب" +Gradient.Direction="جهت شیب" +BkColor="رنگ پس زمینه" +BkOpacity="شفافیت پس زمینه" +Alignment="تراز چینش" +Alignment.Left="چپ" +Alignment.Center="مرکز" +Alignment.Right="راست" +Vertical="عمودی" +VerticalAlignment="تراز عمودی" +VerticalAlignment.Top="بالا" +VerticalAlignment.Bottom="پایین" +Outline="برون نما" +Outline.Size="اندازه طرح کلی" +Outline.Color="رنگ طرح کلی" +Outline.Opacity="تاری دید" +ChatlogMode="حالت Chatlog" +ChatlogMode.Lines="محدودیت Chatlog Line" +UseCustomExtents="استفاده از متن سفارشی" +UseCustomExtents.Wrap="پیچ وتاب" +Width="عرض" +Height="ارتفاع" +Transform="تبدیل متن" +Transform.None="هیچ‌کدام" +Transform.Uppercase="حروف بزرگ" +Transform.Lowercase="حروف کوچک" + diff --git a/plugins/obs-text/data/locale/fi-FI.ini b/plugins/obs-text/data/locale/fi-FI.ini index 1df85fd..51b8991 100644 --- a/plugins/obs-text/data/locale/fi-FI.ini +++ b/plugins/obs-text/data/locale/fi-FI.ini @@ -31,4 +31,8 @@ UseCustomExtents="Käytä valinnaisia fonttilaajennuksia" UseCustomExtents.Wrap="Sido" Width="Leveys" Height="Korkeus" +Transform="Tekstin muunnos" +Transform.None="Ei mitään" +Transform.Uppercase="Isot kirjaimet" +Transform.Lowercase="Pienet kirjaimet" diff --git a/plugins/obs-text/data/locale/fr-FR.ini b/plugins/obs-text/data/locale/fr-FR.ini index 3a9bbca..0aac42b 100644 --- a/plugins/obs-text/data/locale/fr-FR.ini +++ b/plugins/obs-text/data/locale/fr-FR.ini @@ -1,7 +1,7 @@ TextGDIPlus="Texte (GDI+)" Font="Police" Text="Texte" -ReadFromFile="Lire depuis un fichier" +ReadFromFile="Lire à partir d'un fichier" TextFile="Fichier texte (UTF-8)" Filter.TextFiles="Fichiers texte" Filter.AllFiles="Tous les fichiers" @@ -31,4 +31,8 @@ UseCustomExtents="Utiliser une taille personnalisée" UseCustomExtents.Wrap="Retour à la ligne automatique" Width="Largeur" Height="Hauteur" +Transform="Transformer le texte" +Transform.None="Aucune" +Transform.Uppercase="Majuscules" +Transform.Lowercase="Minuscules" diff --git a/plugins/obs-text/data/locale/hu-HU.ini b/plugins/obs-text/data/locale/hu-HU.ini index b5d5966..d622ff9 100644 --- a/plugins/obs-text/data/locale/hu-HU.ini +++ b/plugins/obs-text/data/locale/hu-HU.ini @@ -31,4 +31,8 @@ UseCustomExtents="Egyedi szövegdoboz használata" UseCustomExtents.Wrap="Sortörés" Width="Szélesség" Height="Magasság" +Transform="Szöveg alakítása" +Transform.None="Egyik sem" +Transform.Uppercase="Nagybetűs" +Transform.Lowercase="Kisbetűs" diff --git a/plugins/obs-text/data/locale/it-IT.ini b/plugins/obs-text/data/locale/it-IT.ini index 5200095..1671058 100644 --- a/plugins/obs-text/data/locale/it-IT.ini +++ b/plugins/obs-text/data/locale/it-IT.ini @@ -1,5 +1,5 @@ TextGDIPlus="Testo (GDI+)" -Font="Font" +Font="Carattere" Text="Testo" ReadFromFile="Leggi da file" TextFile="File di testo (UTF-8)" @@ -8,27 +8,31 @@ Filter.AllFiles="Tutti i file" Color="Colore" Opacity="Opacità" Gradient="Sfumato" -Gradient.Color="Colore sfumatura" -Gradient.Opacity="Opacità sfumatura" -Gradient.Direction="Direzione sfumatura" +Gradient.Color="Colore della sfumatura" +Gradient.Opacity="Opacità della sfumatura" +Gradient.Direction="Direzione della sfumatura" BkColor="Colore dello sfondo" -BkOpacity="Opacità Sfondo" +BkOpacity="Opacità dello sfondo" Alignment="Allineamento" Alignment.Left="A sinistra" Alignment.Center="Al centro" Alignment.Right="A destra" Vertical="Verticale" VerticalAlignment="Allineamento verticale" -VerticalAlignment.Top="Alto" -VerticalAlignment.Bottom="Dal basso" -Outline="Contorno" -Outline.Size="Dimensione contorno" +VerticalAlignment.Top="In alto" +VerticalAlignment.Bottom="In basso" +Outline="Contorno del testo" +Outline.Size="Dimensione del contorno" Outline.Color="Colore del contorno" -Outline.Opacity="Opacità contorno" -ChatlogMode="Modalità Chatlog" -ChatlogMode.Lines="Limite linea Chatlog" -UseCustomExtents="Usa personalizzazioni testo" +Outline.Opacity="Opacità del contorno" +ChatlogMode="Modalità chat" +ChatlogMode.Lines="Righe da visualizzare in modalità chat" +UseCustomExtents="Utilizza un formato del testo personalizzato" UseCustomExtents.Wrap="A capo automatico" Width="Larghezza" Height="Altezza" +Transform="Trasformazione del testo" +Transform.None="Nessuna" +Transform.Uppercase="In lettere maiuscole" +Transform.Lowercase="In lettere minuscole" diff --git a/plugins/obs-text/data/locale/ja-JP.ini b/plugins/obs-text/data/locale/ja-JP.ini index 386b4ce..e2d0b17 100644 --- a/plugins/obs-text/data/locale/ja-JP.ini +++ b/plugins/obs-text/data/locale/ja-JP.ini @@ -31,4 +31,8 @@ UseCustomExtents="テキスト領域の範囲を指定する" UseCustomExtents.Wrap="折り返す" Width="幅" Height="高さ" +Transform="テキスト変換" +Transform.None="未設定" +Transform.Uppercase="大文字" +Transform.Lowercase="小文字" diff --git a/plugins/obs-text/data/locale/ka-GE.ini b/plugins/obs-text/data/locale/ka-GE.ini index 37643c8..e36caa6 100644 --- a/plugins/obs-text/data/locale/ka-GE.ini +++ b/plugins/obs-text/data/locale/ka-GE.ini @@ -7,12 +7,12 @@ Filter.TextFiles="ტექსტური ფაილები" Filter.AllFiles="ყველა ფაილი" Color="ფერი" Opacity="გაუმჭვირვალობა" -Gradient="გრადიენტი" -Gradient.Color="გრადიენტის ფერი" -Gradient.Opacity="გრადიენტის გაუმჭვირვალობა" -Gradient.Direction="გრადიენტის მიმართულება" +Gradient="გარდამავალი" +Gradient.Color="გარდამავალი ფერი" +Gradient.Opacity="გარდამავლობის გაუმჭვირვალობა" +Gradient.Direction="გარდამავლობის მიმართულება" BkColor="ფონის ფერი" -BkOpacity="ფონის გაუმჭვივალობა" +BkOpacity="ფონის გამჭვირვალობა" Alignment="განლაგება" Alignment.Left="მარცხნივ" Alignment.Center="შუაში" @@ -31,4 +31,8 @@ UseCustomExtents="ტექსტის ველის მითითებუ UseCustomExtents.Wrap="ხაზზე გადატანა" Width="სიგანე" Height="სიმაღლე" +Transform="ტექსტის გარდაქმნა" +Transform.None="არცერთი" +Transform.Uppercase="მთავრული" +Transform.Lowercase="ნუსხური" diff --git a/plugins/obs-text/data/locale/ko-KR.ini b/plugins/obs-text/data/locale/ko-KR.ini index 1d2a0dd..e52ecf3 100644 --- a/plugins/obs-text/data/locale/ko-KR.ini +++ b/plugins/obs-text/data/locale/ko-KR.ini @@ -31,4 +31,8 @@ UseCustomExtents="사용자 정의 텍스트 설정" UseCustomExtents.Wrap="자동 줄 바꿈" Width="너비" Height="높이" +Transform="글자 변환" +Transform.None="없음" +Transform.Uppercase="대문자" +Transform.Lowercase="소문자" diff --git a/plugins/obs-text/data/locale/mn-MN.ini b/plugins/obs-text/data/locale/mn-MN.ini new file mode 100644 index 0000000..c622ccf --- /dev/null +++ b/plugins/obs-text/data/locale/mn-MN.ini @@ -0,0 +1,25 @@ +Font="Фонт" +Text="Текст" +ReadFromFile="Файлаас уншуулах" +TextFile="Текст Файл (UTF-8)" +Filter.TextFiles="Текст файлууд" +Filter.AllFiles="Бүх файлууд" +Color="Өнгө" +Opacity="Харанхуйлах" +Gradient="Уусгалтай" +Gradient.Color="Уусгалтай Өнгө" +Gradient.Opacity="Уусгалтай Харанхуйлах" +BkColor="Дэвсгэрийн Өнгө" +Alignment.Left="Зүүн" +Alignment.Center="Төв" +Alignment.Right="Баруун" +Vertical="Босоо" +VerticalAlignment.Top="Дээд хэсэг" +VerticalAlignment.Bottom="Доод хэсэг" +Outline="Гадуурх зураас" +Outline.Size="Гадуурх Зураасны Хэмжээ" +Outline.Color="Гадуурх Зураасны Өнгө" +UseCustomExtents.Wrap="Доош Эгнээх" +Width="Өргөн" +Height="Өндөр" + diff --git a/plugins/obs-text/data/locale/nb-NO.ini b/plugins/obs-text/data/locale/nb-NO.ini index 97eed0a..eeb9709 100644 --- a/plugins/obs-text/data/locale/nb-NO.ini +++ b/plugins/obs-text/data/locale/nb-NO.ini @@ -31,4 +31,8 @@ UseCustomExtents="Bruk egendefinerte tekst-utvidelser" UseCustomExtents.Wrap="Ordbrytning" Width="Bredde" Height="Høyde" +Transform="Tekst transformering" +Transform.None="Ingen" +Transform.Uppercase="Store bokstaver" +Transform.Lowercase="Små bokstaver" diff --git a/plugins/obs-text/data/locale/nl-NL.ini b/plugins/obs-text/data/locale/nl-NL.ini index 098be9f..c9bcab5 100644 --- a/plugins/obs-text/data/locale/nl-NL.ini +++ b/plugins/obs-text/data/locale/nl-NL.ini @@ -31,4 +31,8 @@ UseCustomExtents="Aangepaste tekst-extents gebruiken" UseCustomExtents.Wrap="Terugloop" Width="Breedte" Height="Hoogte" +Transform="Tekst transformatie" +Transform.None="Geen" +Transform.Uppercase="Hoofdletters" +Transform.Lowercase="Kleine letters" diff --git a/plugins/obs-text/data/locale/pl-PL.ini b/plugins/obs-text/data/locale/pl-PL.ini index 384ca15..12c3eca 100644 --- a/plugins/obs-text/data/locale/pl-PL.ini +++ b/plugins/obs-text/data/locale/pl-PL.ini @@ -31,4 +31,8 @@ UseCustomExtents="Użyj niestandardowego zakresu tekstu" UseCustomExtents.Wrap="Zawiń" Width="Szerokość" Height="Wysokość" +Transform="Przekształcanie tekstu" +Transform.None="Brak" +Transform.Uppercase="Wielkie litery" +Transform.Lowercase="Małe litery" diff --git a/plugins/obs-text/data/locale/pt-BR.ini b/plugins/obs-text/data/locale/pt-BR.ini index 64a966e..5b6e42d 100644 --- a/plugins/obs-text/data/locale/pt-BR.ini +++ b/plugins/obs-text/data/locale/pt-BR.ini @@ -31,4 +31,8 @@ UseCustomExtents="Usar extensões de texto personalizadas" UseCustomExtents.Wrap="Ajustar" Width="Largura" Height="Altura" +Transform="Transformação" +Transform.None="Nenhuma" +Transform.Uppercase="Letras Maiúsculas" +Transform.Lowercase="Letras Minúsculas" diff --git a/plugins/obs-text/data/locale/pt-PT.ini b/plugins/obs-text/data/locale/pt-PT.ini index d2338c7..555ac8c 100644 --- a/plugins/obs-text/data/locale/pt-PT.ini +++ b/plugins/obs-text/data/locale/pt-PT.ini @@ -7,10 +7,12 @@ Gradient="Gradiente" Gradient.Color="Cor do Gradiente" Gradient.Opacity="Opacidade do Gradiente" Gradient.Direction="Direção do Gradiente" +Alignment="Alinhamento" Alignment.Left="Esquerda" Alignment.Center="Centro" Alignment.Right="Direita" Vertical="Vertical" +VerticalAlignment="Alinhamento Vertical" VerticalAlignment.Top="Topo" Width="Largura" Height="Altura" diff --git a/plugins/obs-text/data/locale/ru-RU.ini b/plugins/obs-text/data/locale/ru-RU.ini index 07dafec..45c66d7 100644 --- a/plugins/obs-text/data/locale/ru-RU.ini +++ b/plugins/obs-text/data/locale/ru-RU.ini @@ -31,4 +31,8 @@ UseCustomExtents="Свои размеры текстового поля" UseCustomExtents.Wrap="Перенос строк" Width="Ширина" Height="Высота" +Transform="Преобразование текста" +Transform.None="Нет" +Transform.Uppercase="Верхний регистр" +Transform.Lowercase="Нижний регистр" diff --git a/plugins/obs-text/data/locale/sk-SK.ini b/plugins/obs-text/data/locale/sk-SK.ini index 9704ae3..3199c88 100644 --- a/plugins/obs-text/data/locale/sk-SK.ini +++ b/plugins/obs-text/data/locale/sk-SK.ini @@ -31,4 +31,8 @@ UseCustomExtents="Použiť vlastné rozsahy textu" UseCustomExtents.Wrap="Zalomiť" Width="Šírka" Height="Výška" +Transform="Transformácia textu" +Transform.None="Žiadna" +Transform.Uppercase="Veľké písmená" +Transform.Lowercase="Malé písmená" diff --git a/plugins/obs-text/data/locale/sv-SE.ini b/plugins/obs-text/data/locale/sv-SE.ini index 7f108f8..402c94a 100644 --- a/plugins/obs-text/data/locale/sv-SE.ini +++ b/plugins/obs-text/data/locale/sv-SE.ini @@ -31,4 +31,8 @@ UseCustomExtents="Använd anpassade textmått" UseCustomExtents.Wrap="Radbryt" Width="Bredd" Height="Höjd" +Transform="Textomvandling" +Transform.None="Ingen" +Transform.Uppercase="Versaler" +Transform.Lowercase="Gemener" diff --git a/plugins/obs-text/data/locale/tr-TR.ini b/plugins/obs-text/data/locale/tr-TR.ini index 289e570..a54bcc4 100644 --- a/plugins/obs-text/data/locale/tr-TR.ini +++ b/plugins/obs-text/data/locale/tr-TR.ini @@ -31,4 +31,8 @@ UseCustomExtents="İsteğe Bağlı Metin Boyutu Kullan" UseCustomExtents.Wrap="Metni Kaydır" Width="Genişlik" Height="Yükseklik" +Transform="Metin Dönüşümü" +Transform.None="Yok" +Transform.Uppercase="Büyük harf" +Transform.Lowercase="Küçük harf" diff --git a/plugins/obs-text/data/locale/uk-UA.ini b/plugins/obs-text/data/locale/uk-UA.ini index a6c9ba9..6f489bb 100644 --- a/plugins/obs-text/data/locale/uk-UA.ini +++ b/plugins/obs-text/data/locale/uk-UA.ini @@ -31,4 +31,8 @@ UseCustomExtents="Особливі властивості текстового UseCustomExtents.Wrap="Перенос слів" Width="Ширина" Height="Висота" +Transform="Перетворення тексту" +Transform.None="Немає" +Transform.Uppercase="До верхнього регістру" +Transform.Lowercase="До нижнього регістру" diff --git a/plugins/obs-text/data/locale/zh-CN.ini b/plugins/obs-text/data/locale/zh-CN.ini index 83d0361..f7a084b 100644 --- a/plugins/obs-text/data/locale/zh-CN.ini +++ b/plugins/obs-text/data/locale/zh-CN.ini @@ -31,4 +31,8 @@ UseCustomExtents="使用自定义文本区" UseCustomExtents.Wrap="自动换行" Width="宽度" Height="高度" +Transform="文本转换" +Transform.None="无" +Transform.Uppercase="大写" +Transform.Lowercase="小写" diff --git a/plugins/obs-text/data/locale/zh-TW.ini b/plugins/obs-text/data/locale/zh-TW.ini index 8f18a35..29010fa 100644 --- a/plugins/obs-text/data/locale/zh-TW.ini +++ b/plugins/obs-text/data/locale/zh-TW.ini @@ -31,4 +31,8 @@ UseCustomExtents="使用自動文字區塊大小" UseCustomExtents.Wrap="自動換行" Width="寬度" Height="高度" +Transform="文字變型" +Transform.None="無" +Transform.Uppercase="大寫" +Transform.Lowercase="小寫" diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index 2efc108..3619126 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -62,6 +62,7 @@ using namespace Gdiplus; #define S_EXTENTS_WRAP "extents_wrap" #define S_EXTENTS_CX "extents_cx" #define S_EXTENTS_CY "extents_cy" +#define S_TRANSFORM "transform" #define S_ALIGN_LEFT "left" #define S_ALIGN_CENTER "center" @@ -71,6 +72,10 @@ using namespace Gdiplus; #define S_VALIGN_CENTER S_ALIGN_CENTER #define S_VALIGN_BOTTOM "bottom" +#define S_TRANSFORM_NONE 0 +#define S_TRANSFORM_UPPERCASE 1 +#define S_TRANSFORM_LOWERCASE 2 + #define T_(v) obs_module_text(v) #define T_FONT T_("Font") #define T_USE_FILE T_("ReadFromFile") @@ -97,6 +102,7 @@ using namespace Gdiplus; #define T_EXTENTS_WRAP T_("UseCustomExtents.Wrap") #define T_EXTENTS_CX T_("Width") #define T_EXTENTS_CY T_("Height") +#define T_TRANSFORM T_("Transform") #define T_FILTER_TEXT_FILES T_("Filter.TextFiles") #define T_FILTER_ALL_FILES T_("Filter.AllFiles") @@ -109,6 +115,10 @@ using namespace Gdiplus; #define T_VALIGN_CENTER T_ALIGN_CENTER #define T_VALIGN_BOTTOM T_("VerticalAlignment.Bottom") +#define T_TRANSFORM_NONE T_("Transform.None") +#define T_TRANSFORM_UPPERCASE T_("Transform.Uppercase") +#define T_TRANSFORM_LOWERCASE T_("Transform.Lowercase") + /* ------------------------------------------------------------------------- */ static inline DWORD get_alpha_val(uint32_t opacity) @@ -229,6 +239,8 @@ struct TextSource { uint32_t extents_cx = 0; uint32_t extents_cy = 0; + int text_transform = S_TRANSFORM_NONE; + bool chatlog_mode = false; int chatlog_lines = 6; @@ -266,7 +278,7 @@ struct TextSource { inline void Update(obs_data_t *settings); inline void Tick(float seconds); - inline void Render(gs_effect_t *effect); + inline void Render(); }; static time_t get_modified_timestamp(const char *filename) @@ -659,6 +671,7 @@ inline void TextSource::Update(obs_data_t *s) bool new_extents_wrap = obs_data_get_bool(s, S_EXTENTS_WRAP); uint32_t n_extents_cx = obs_data_get_uint32(s, S_EXTENTS_CX); uint32_t n_extents_cy = obs_data_get_uint32(s, S_EXTENTS_CY); + int new_text_transform = (int)obs_data_get_int(s, S_TRANSFORM); const char *font_face = obs_data_get_string(font_obj, "face"); int font_size = (int)obs_data_get_int(font_obj, "size"); @@ -712,6 +725,7 @@ inline void TextSource::Update(obs_data_t *s) wrap = new_extents_wrap; extents_cx = n_extents_cx; extents_cy = n_extents_cy; + text_transform = new_text_transform; if (!gradient) { color2 = color; @@ -737,6 +751,10 @@ inline void TextSource::Update(obs_data_t *s) if (!text.empty()) text.push_back('\n'); } + if(text_transform == S_TRANSFORM_UPPERCASE) + transform(text.begin(), text.end(), text.begin(), towupper); + else if(text_transform == S_TRANSFORM_LOWERCASE) + transform(text.begin(), text.end(), text.begin(), towlower); use_outline = new_outline; outline_color = new_o_color; @@ -789,13 +807,22 @@ inline void TextSource::Tick(float seconds) } } -inline void TextSource::Render(gs_effect_t *effect) +inline void TextSource::Render() { if (!tex) return; + gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); + gs_technique_t *tech = gs_effect_get_technique(effect, "Draw"); + + gs_technique_begin(tech); + gs_technique_begin_pass(tech, 0); + gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), tex); gs_draw_sprite(tex, 0, cx, cy); + + gs_technique_end_pass(tech); + gs_technique_end(tech); } /* ------------------------------------------------------------------------- */ @@ -804,6 +831,10 @@ static ULONG_PTR gdip_token = 0; OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-text", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "Windows GDI+ text source"; +} #define set_vis(var, val, show) \ do { \ @@ -898,6 +929,13 @@ static obs_properties_t *get_properties(void *data) obs_properties_add_path(props, S_FILE, T_FILE, OBS_PATH_FILE, filter.c_str(), path.c_str()); + p = obs_properties_add_list(props, S_TRANSFORM, T_TRANSFORM, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, T_TRANSFORM_NONE, S_TRANSFORM_NONE); + obs_property_list_add_int(p, T_TRANSFORM_UPPERCASE, S_TRANSFORM_UPPERCASE); + obs_property_list_add_int(p, T_TRANSFORM_LOWERCASE, S_TRANSFORM_LOWERCASE); + + obs_properties_add_bool(props, S_VERTICAL, T_VERTICAL); obs_properties_add_color(props, S_COLOR, T_COLOR); obs_properties_add_int_slider(props, S_OPACITY, T_OPACITY, 0, 100, 1); @@ -910,7 +948,7 @@ static obs_properties_t *get_properties(void *data) T_GRADIENT_OPACITY, 0, 100, 1); obs_properties_add_float_slider(props, S_GRADIENT_DIR, T_GRADIENT_DIR, 0, 360, 0.1); - + obs_properties_add_color(props, S_BKCOLOR, T_BKCOLOR); obs_properties_add_int_slider(props, S_BKOPACITY, T_BKOPACITY, 0, 100, 1); @@ -956,7 +994,7 @@ bool obs_module_load(void) obs_source_info si = {}; si.id = "text_gdiplus"; si.type = OBS_SOURCE_TYPE_INPUT; - si.output_flags = OBS_SOURCE_VIDEO; + si.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW; si.get_properties = get_properties; si.get_name = [] (void*) @@ -1002,6 +1040,7 @@ bool obs_module_load(void) obs_data_set_default_bool(settings, S_EXTENTS_WRAP, true); obs_data_set_default_int(settings, S_EXTENTS_CX, 100); obs_data_set_default_int(settings, S_EXTENTS_CY, 100); + obs_data_set_default_int(settings, S_TRANSFORM, S_TRANSFORM_NONE); obs_data_release(font_obj); }; @@ -1013,9 +1052,9 @@ bool obs_module_load(void) { reinterpret_cast(data)->Tick(seconds); }; - si.video_render = [] (void *data, gs_effect_t *effect) + si.video_render = [] (void *data, gs_effect_t*) { - reinterpret_cast(data)->Render(effect); + reinterpret_cast(data)->Render(); }; obs_register_source(&si); diff --git a/plugins/obs-transitions/data/fade_to_color_transition.effect b/plugins/obs-transitions/data/fade_to_color_transition.effect index 130d44a..0e8417c 100644 --- a/plugins/obs-transitions/data/fade_to_color_transition.effect +++ b/plugins/obs-transitions/data/fade_to_color_transition.effect @@ -14,8 +14,6 @@ struct VertData { float2 uv : TEXCOORD0; }; -#include "premultiplied.inc" - VertData VSDefault(VertData v_in) { VertData vert_out; @@ -26,7 +24,8 @@ VertData VSDefault(VertData v_in) float4 PSFadeToColor(VertData v_in) : TARGET { - return lerp(convert_pmalpha(tex.Sample(textureSampler, v_in.uv)), color, swp); + float4 premultiplied = float4(color.rgb * color.a, color.a); + return lerp(tex.Sample(textureSampler, v_in.uv), premultiplied, swp); } technique FadeToColor diff --git a/plugins/obs-transitions/data/fade_transition.effect b/plugins/obs-transitions/data/fade_transition.effect index bba5031..f728f3a 100644 --- a/plugins/obs-transitions/data/fade_transition.effect +++ b/plugins/obs-transitions/data/fade_transition.effect @@ -14,8 +14,6 @@ struct VertData { float2 uv : TEXCOORD0; }; -#include "premultiplied.inc" - VertData VSDefault(VertData v_in) { VertData vert_out; @@ -26,8 +24,8 @@ VertData VSDefault(VertData v_in) float4 PSFade(VertData v_in) : TARGET { - float4 a_val = convert_pmalpha(tex_a.Sample(textureSampler, v_in.uv)); - float4 b_val = convert_pmalpha(tex_b.Sample(textureSampler, v_in.uv)); + float4 a_val = tex_a.Sample(textureSampler, v_in.uv); + float4 b_val = tex_b.Sample(textureSampler, v_in.uv); return lerp(a_val, b_val, fade_val); } diff --git a/plugins/obs-transitions/data/locale/da-DK.ini b/plugins/obs-transitions/data/locale/da-DK.ini index 52e60c6..18c3b2f 100644 --- a/plugins/obs-transitions/data/locale/da-DK.ini +++ b/plugins/obs-transitions/data/locale/da-DK.ini @@ -1,39 +1,39 @@ FadeTransition="Overgang" CutTransition="Klip" -SwipeTransition="Swipe" +SwipeTransition="Stryg" SlideTransition="Glide" StingerTransition="Stinger-overgang" -FadeToColorTransition="Fade til farve" +FadeToColorTransition="Farveovergang" Direction="Retning" Direction.Left="Venstre" Direction.Right="Højre" Direction.Up="Op" Direction.Down="Ned" -SwipeIn="Swipe ind" +SwipeIn="Stryg ind" Color="Farve" VideoFile="Videofil" -TransitionPoint="Overgangspunkt (millisekunder)" -TransitionPointFrame="Overgangspunkt (ramme)" +TransitionPoint="Overgangspunkt (millisek.)" +TransitionPointFrame="Overgangspunkt (billede)" TransitionPointType="Overgangspunkttype" TransitionPointTypeFrame="Billede" -TransitionPointTypeTime="Tid (ms)" +TransitionPointTypeTime="Tid (millisek.)" AudioFadeStyle="Lydudtoningsstil" AudioFadeStyle.FadeOutFadeIn="Udtone til overgangspunkt, dernæst indtone" AudioFadeStyle.CrossFade="Krydstoning (crossfade)" -SwitchPoint="Farvepeakpunkt (procent)" -LumaWipeTransition="Luma overgang" +SwitchPoint="Farvespidspunkt (procent)" +LumaWipeTransition="Luma-overgang" LumaWipe.Image="Billede" -LumaWipe.Invert="Inverter" +LumaWipe.Invert="Invertér" LumaWipe.Softness="Blødhed" LumaWipe.Type.BarndoorBottomLeft="Stalddør nederst til venstre" LumaWipe.Type.BarndoorHorizontal="Stalddør horisontal" LumaWipe.Type.BarndoorTopLeft="Stalddør øverst til venstre" LumaWipe.Type.BarndoorVertical="Stalddør vertikal" LumaWipe.Type.BlindsHorizontal="Persienne horisontal" -LumaWipe.Type.BoxBottomLeft="Boks nederst til venstre" -LumaWipe.Type.BoxBottomRight="Boks nederst til højre" -LumaWipe.Type.BoxTopLeft="Boks øverst til venstre" -LumaWipe.Type.BoxTopRight="Boks øverst til højre" +LumaWipe.Type.BoxBottomLeft="Kasse nederst til venstre" +LumaWipe.Type.BoxBottomRight="Kasse nederst til højre" +LumaWipe.Type.BoxTopLeft="Kasse øverst til venstre" +LumaWipe.Type.BoxTopRight="Kasse øverst til højre" LumaWipe.Type.Burst="Sprængning" LumaWipe.Type.CheckerboardSmall="Små skaktern" LumaWipe.Type.Circles="Cirkler" diff --git a/plugins/obs-transitions/data/locale/de-DE.ini b/plugins/obs-transitions/data/locale/de-DE.ini index 8913752..3e2a034 100644 --- a/plugins/obs-transitions/data/locale/de-DE.ini +++ b/plugins/obs-transitions/data/locale/de-DE.ini @@ -3,7 +3,7 @@ CutTransition="Schnitt" SwipeTransition="Swipe" SlideTransition="Slide" StingerTransition="Stinger" -FadeToColorTransition="Fade to Color" +FadeToColorTransition="Farbübergang" Direction="Richtung" Direction.Left="Links" Direction.Right="Rechts" @@ -17,13 +17,13 @@ TransitionPointFrame="Übergangspunkt (Frame)" TransitionPointType="Übergangspunkttyp" TransitionPointTypeFrame="Frame" TransitionPointTypeTime="Zeit (Millisekunden)" -AudioFadeStyle="Audio Überblendstil" +AudioFadeStyle="Audioüberblendstil" AudioFadeStyle.FadeOutFadeIn="Zu Übergangspunkt ausblenden und dann einblenden" AudioFadeStyle.CrossFade="Überblendung" -SwitchPoint="Peakfarbpunkt (Prozent)" +SwitchPoint="Spitzenfarbpunkt (in Prozent)" LumaWipeTransition="Luma Wipe" LumaWipe.Image="Bild" -LumaWipe.Invert="Invertieren" +LumaWipe.Invert="Umkehren" LumaWipe.Softness="Weichheit" LumaWipe.Type.BarndoorBottomLeft="Scheunentor unten links" LumaWipe.Type.BarndoorHorizontal="Scheunentor Horizontal" @@ -61,6 +61,6 @@ LumaWipe.Type.ZigzagHorizontal="Zigzag horizontal" LumaWipe.Type.ZigzagVertical="Zigzag vertikal" AudioMonitoring="Audiomonitoring" AudioMonitoring.None="Monitor aus" -AudioMonitoring.MonitorOnly="Nur Monitor (Ausgabe stumm schalten)" +AudioMonitoring.MonitorOnly="Nur Monitor (Ausgabe stummschalten)" AudioMonitoring.Both="Monitor und Ausgabe" diff --git a/plugins/obs-transitions/data/locale/eu-ES.ini b/plugins/obs-transitions/data/locale/eu-ES.ini index 688e65b..f155235 100644 --- a/plugins/obs-transitions/data/locale/eu-ES.ini +++ b/plugins/obs-transitions/data/locale/eu-ES.ini @@ -60,7 +60,7 @@ LumaWipe.Type.Watercolor="Akuarela" LumaWipe.Type.ZigzagHorizontal="Sigi-saga horizontala" LumaWipe.Type.ZigzagVertical="Sigi-saga bertikala" AudioMonitoring="Adioaren monitorizazioa" -AudioMonitoring.None="Monitorea itzalita" -AudioMonitoring.MonitorOnly="Monitorea bakarrik (isildu irteera)" +AudioMonitoring.None="Ez monitorizatu" +AudioMonitoring.MonitorOnly="Monitorea bakarrik (irteera mututua)" AudioMonitoring.Both="Monitorea eta irteera" diff --git a/plugins/obs-transitions/data/locale/fr-FR.ini b/plugins/obs-transitions/data/locale/fr-FR.ini index e4ed43c..6fcb4b3 100644 --- a/plugins/obs-transitions/data/locale/fr-FR.ini +++ b/plugins/obs-transitions/data/locale/fr-FR.ini @@ -3,7 +3,7 @@ CutTransition="Coupure" SwipeTransition="Balayage" SlideTransition="Glissement" StingerTransition="Stinger" -FadeToColorTransition="Fondu avec couleur" +FadeToColorTransition="Fondu vers couleur" Direction="Direction" Direction.Left="Gauche" Direction.Right="Droite" @@ -21,7 +21,7 @@ AudioFadeStyle="Style de fondu audio" AudioFadeStyle.FadeOutFadeIn="Fondu en fermeture jusqu'au point de transition puis fondu en ouverture" AudioFadeStyle.CrossFade="Fondu enchaîné" SwitchPoint="Point de couleur maximal (pourcentage)" -LumaWipeTransition="Luma" +LumaWipeTransition="Balayage Luma" LumaWipe.Image="Image" LumaWipe.Invert="Inverser" LumaWipe.Softness="Douceur" @@ -59,8 +59,8 @@ LumaWipe.Type.StripsVertical="Bandes verticales" LumaWipe.Type.Watercolor="Aquarelle" LumaWipe.Type.ZigzagHorizontal="Zigzags à l'horizontale" LumaWipe.Type.ZigzagVertical="Zigzags à la verticale" -AudioMonitoring="Surveillance audio" -AudioMonitoring.None="Pas de surveillance" -AudioMonitoring.MonitorOnly="Surveillance seule (sortie en sourdine)" -AudioMonitoring.Both="Surveillance et sortie" +AudioMonitoring="Monitoring audio" +AudioMonitoring.None="Pas de Monitoring" +AudioMonitoring.MonitorOnly="Monitoring seul (sortie coupée)" +AudioMonitoring.Both="Monotoring et sortie" diff --git a/plugins/obs-transitions/data/locale/it-IT.ini b/plugins/obs-transitions/data/locale/it-IT.ini index dbf3438..4cd1f2c 100644 --- a/plugins/obs-transitions/data/locale/it-IT.ini +++ b/plugins/obs-transitions/data/locale/it-IT.ini @@ -1,66 +1,66 @@ FadeTransition="Dissolvenza" CutTransition="Taglio" -SwipeTransition="Scorri" +SwipeTransition="Scorrimento" SlideTransition="Scivola" StingerTransition="Stinger" -FadeToColorTransition="Dissolvenza a colore" +FadeToColorTransition="Dissolvenza ad un colore" Direction="Direzione" -Direction.Left="Sinistra" -Direction.Right="Destra" -Direction.Up="Sù" -Direction.Down="Giù" -SwipeIn="Scorri verso l'alto" +Direction.Left="Verso sinistra" +Direction.Right="Verso destra" +Direction.Up="Verso l'alto" +Direction.Down="Verso il basso" +SwipeIn="Inverti l'effetto" Color="Colore" VideoFile="File video" -TransitionPoint="Punto di transizione (millisecondi)" -TransitionPointFrame="Punto di transizione (frame)" -TransitionPointType="Tipo di punto di transizione" -TransitionPointTypeFrame="Fotogramma" -TransitionPointTypeTime="Tempo (millisecondi)" -AudioFadeStyle="Stile dissolvenza audio" +TransitionPoint="Punto di transizione (in millisecondi)" +TransitionPointFrame="Punto di transizione (in fotogrammi)" +TransitionPointType="Tipo del punto di transizione" +TransitionPointTypeFrame="In fotogrammi" +TransitionPointTypeTime="A tempo (in millisecondi)" +AudioFadeStyle="Stile della dissolvenza audio" AudioFadeStyle.FadeOutFadeIn="Dissolvenza fino al punto di transizione, poi dissolvenza in entrata" -AudioFadeStyle.CrossFade="Dissolvenza" -SwitchPoint="Picco Punto Colore (percentuale)" -LumaWipeTransition="Luma Wipe" +AudioFadeStyle.CrossFade="Dissolvenza incrociata" +SwitchPoint="Picco del colore (in percentuale)" +LumaWipeTransition="Scansione Luma" LumaWipe.Image="Immagine" LumaWipe.Invert="Inverti" LumaWipe.Softness="Morbidezza" LumaWipe.Type.BarndoorBottomLeft="Portello in basso a sinistra" -LumaWipe.Type.BarndoorHorizontal="Portello Orizzontale" +LumaWipe.Type.BarndoorHorizontal="Portello orizzontale" LumaWipe.Type.BarndoorTopLeft="Portello in alto a sinistra" LumaWipe.Type.BarndoorVertical="Portello verticale" -LumaWipe.Type.BlindsHorizontal="Tapparelle Orizzontale" -LumaWipe.Type.BoxBottomLeft="Box in basso a sinistra" -LumaWipe.Type.BoxBottomRight="Box in basso a destra" -LumaWipe.Type.BoxTopLeft="Box in alto a sinistra" -LumaWipe.Type.BoxTopRight="Box in alto a destra" -LumaWipe.Type.Burst="Burst" +LumaWipe.Type.BlindsHorizontal="Tapparelle orizzontali" +LumaWipe.Type.BoxBottomLeft="Casella in basso a sinistra" +LumaWipe.Type.BoxBottomRight="Casella in basso a destra" +LumaWipe.Type.BoxTopLeft="Casella in alto a sinistra" +LumaWipe.Type.BoxTopRight="Casella in alto a destra" +LumaWipe.Type.Burst="Esplosione" LumaWipe.Type.CheckerboardSmall="Scacchiera piccola" LumaWipe.Type.Circles="Cerchi" LumaWipe.Type.Clock="Orologio" LumaWipe.Type.Cloud="Nuvola" -LumaWipe.Type.Curtain="Cortina" +LumaWipe.Type.Curtain="Tendina" LumaWipe.Type.Fan="Ventola" LumaWipe.Type.Fractal="Frattale" LumaWipe.Type.Iris="Diaframma" -LumaWipe.Type.LinearHorizontal="Lineare Orizzontale" +LumaWipe.Type.LinearHorizontal="Lineare orizzontale" LumaWipe.Type.LinearTopLeft="Lineare in alto a sinistra" LumaWipe.Type.LinearTopRight="Lineare in alto a destra" LumaWipe.Type.LinearVertical="Lineare verticale" LumaWipe.Type.ParallelZigzagHorizontal="Parallelo a zig-zag orizzontale" LumaWipe.Type.ParallelZigzagVertical="Parallelo a zig-zag verticale" -LumaWipe.Type.Sinus9="Sinusale 9" +LumaWipe.Type.Sinus9="Plasma" LumaWipe.Type.Spiral="Spirale" LumaWipe.Type.Square="Quadrato" LumaWipe.Type.Squares="Quadrati" LumaWipe.Type.Stripes="Striscie" -LumaWipe.Type.StripsHorizontal="Striscie Orizzontale" +LumaWipe.Type.StripsHorizontal="Striscie orizzontali" LumaWipe.Type.StripsVertical="Striscie verticali" LumaWipe.Type.Watercolor="Acquerello" LumaWipe.Type.ZigzagHorizontal="Zig-zag orizzontale" LumaWipe.Type.ZigzagVertical="Zig-zag verticale" -AudioMonitoring="Monitoraggio Audio" -AudioMonitoring.None="Monitoraggio off" -AudioMonitoring.MonitorOnly="Solo Monitoraggio (output mutato)" -AudioMonitoring.Both="Monitoraggio e Output" +AudioMonitoring="Monitoraggio audio" +AudioMonitoring.None="Disattivato" +AudioMonitoring.MonitorOnly="Solo monitoraggio (uscita audio nel file disattivata)" +AudioMonitoring.Both="Monitora l'audio e invia all'uscita" diff --git a/plugins/obs-transitions/data/locale/ka-GE.ini b/plugins/obs-transitions/data/locale/ka-GE.ini index 948601b..77b411e 100644 --- a/plugins/obs-transitions/data/locale/ka-GE.ini +++ b/plugins/obs-transitions/data/locale/ka-GE.ini @@ -22,7 +22,7 @@ AudioFadeStyle.FadeOutFadeIn="თანდათან მილევა გა AudioFadeStyle.CrossFade="ჯვარედინი გადასვლა" SwitchPoint="ფერის უმაღლესი წერტილი (პროცენტი)" LumaWipeTransition="Luma Wipe" -LumaWipe.Image="სურათი" +LumaWipe.Image="გამოსახულება" LumaWipe.Invert="შებრუნება" LumaWipe.Softness="სირბილე" LumaWipe.Type.BarndoorBottomLeft="მარცხნივ ქვემოთ დახრილი გადაფარვა" @@ -30,10 +30,10 @@ LumaWipe.Type.BarndoorHorizontal="თარაზული გადაფარ LumaWipe.Type.BarndoorTopLeft="მარცხნივ ზემოთ დახრილი გადაფარვა" LumaWipe.Type.BarndoorVertical="შვეული გადაფარვა" LumaWipe.Type.BlindsHorizontal="თარაზული ჟალუზები" -LumaWipe.Type.BoxBottomLeft="ქვედა მარცხენა კუთხიდან გადაფარვა" -LumaWipe.Type.BoxBottomRight="ქვედა მარჯვენა კუთხიდან გადაფარვა" -LumaWipe.Type.BoxTopLeft="ზედა მარცხენა კუთხიდან გადაფარვა" -LumaWipe.Type.BoxTopRight="ზედა მარჯვენა კუთხიდან გადაფარვა" +LumaWipe.Type.BoxBottomLeft="ქვედა მარცხენა კუთხისკენ გადაფარვა" +LumaWipe.Type.BoxBottomRight="ქვედა მარჯვენა კუთხისკენ გადაფარვა" +LumaWipe.Type.BoxTopLeft="ზედა მარცხენა კუთხისკენ გადაფარვა" +LumaWipe.Type.BoxTopRight="ზედა მარჯვენა კუთხისკენ გადაფარვა" LumaWipe.Type.Burst="აფეთქება" LumaWipe.Type.CheckerboardSmall="უჯრები" LumaWipe.Type.Circles="წრეები" @@ -61,6 +61,6 @@ LumaWipe.Type.ZigzagHorizontal="თარაზული ტეხილი" LumaWipe.Type.ZigzagVertical="შვეული ტეხილი" AudioMonitoring="ხმის მოსმენა" AudioMonitoring.None="მოსმენის გარეშე" -AudioMonitoring.MonitorOnly="მხოლოდ მოსმენა (უხმო გამომავალი სიგნალით)" +AudioMonitoring.MonitorOnly="მხოლოდ მოსმენა (უხმო გამოტანა)" AudioMonitoring.Both="მოსმენა და გამოტანა" diff --git a/plugins/obs-transitions/data/locale/pt-PT.ini b/plugins/obs-transitions/data/locale/pt-PT.ini index ac3a110..880cf60 100644 --- a/plugins/obs-transitions/data/locale/pt-PT.ini +++ b/plugins/obs-transitions/data/locale/pt-PT.ini @@ -2,6 +2,7 @@ FadeTransition="Desvanecer" CutTransition="Cortar" SwipeTransition="Deslizar" SlideTransition="Deslizar" +StingerTransition="Ferrão" FadeToColorTransition="Desvanecer para Cor" Direction="Direção" Direction.Left="Esquerda" @@ -10,5 +11,10 @@ Direction.Up="Cima" Direction.Down="Baixo" SwipeIn="Deslizar para dentro" Color="Cor" +VideoFile="Ficheiro Vídeo" +TransitionPoint="Ponto de Transição (milissegundos)" +TransitionPointFrame="Ponto de transição (quadro)" +TransitionPointType="Tipo de Ponto de Transição" +TransitionPointTypeTime="Tempo (milissegundos)" SwitchPoint="Ponto de pico de Cor (percentagem)" diff --git a/plugins/obs-transitions/data/locale/ro-RO.ini b/plugins/obs-transitions/data/locale/ro-RO.ini index c8f7860..b7bef52 100644 --- a/plugins/obs-transitions/data/locale/ro-RO.ini +++ b/plugins/obs-transitions/data/locale/ro-RO.ini @@ -1,18 +1,31 @@ FadeTransition="Estompare" -CutTransition="Tăiere" +CutTransition="Decupare" SwipeTransition="Glisare" -SlideTransition="Gliseaza" +SlideTransition="Culisare" +StingerTransition="Stinger" FadeToColorTransition="Estompare prin culoare" Direction="Direcție" Direction.Left="Stânga" Direction.Right="Dreapta" Direction.Up="Sus" Direction.Down="Jos" -SwipeIn="Glisați înauntru" +SwipeIn="Glisează peste" Color="Culoare" +TransitionPoint="Punct de tranziție (milisecunde)" +TransitionPointFrame="Punct de tranziție (fotogramă)" +TransitionPointType="Tipul punctului de tranziție" +TransitionPointTypeFrame="Fotogramă" +TransitionPointTypeTime="Timp (milisecunde)" SwitchPoint="Punctul de vârf al culorii (procent)" LumaWipe.Image="Imagine" +LumaWipe.Invert="Inversează" +LumaWipe.Softness="Moliciune" +LumaWipe.Type.Clock="Ceas" LumaWipe.Type.Cloud="Cloud" LumaWipe.Type.ZigzagHorizontal="Zigzag orizontal" LumaWipe.Type.ZigzagVertical="Zigzag Vertical" +AudioMonitoring="Monitorizare audio" +AudioMonitoring.None="Monitorizare dezactivată" +AudioMonitoring.MonitorOnly="Numai monitorizare (amuțește ieșirea)" +AudioMonitoring.Both="Monitorizare și ieșire" diff --git a/plugins/obs-transitions/data/locale/sk-SK.ini b/plugins/obs-transitions/data/locale/sk-SK.ini index 6f80bac..3d3bf31 100644 --- a/plugins/obs-transitions/data/locale/sk-SK.ini +++ b/plugins/obs-transitions/data/locale/sk-SK.ini @@ -26,4 +26,13 @@ LumaWipe.Type.Curtain="Záves" LumaWipe.Type.Fan="Ventilátor" LumaWipe.Type.Fractal="Fractal" LumaWipe.Type.Iris="Iris" +LumaWipe.Type.Sinus9="Sínus 9" +LumaWipe.Type.Spiral="Špirála" +LumaWipe.Type.Square="Štvorec" +LumaWipe.Type.Squares="Štvorce" +LumaWipe.Type.Stripes="Pásiky" +AudioMonitoring="Monitorovanie zvuku" +AudioMonitoring.None="Vypnuté monitorovanie" +AudioMonitoring.MonitorOnly="Iba monitorovanie (žiaden výstup)" +AudioMonitoring.Both="Monitorovanie a výstup" diff --git a/plugins/obs-transitions/data/locale/sr-CS.ini b/plugins/obs-transitions/data/locale/sr-CS.ini index 473e46f..14be8c6 100644 --- a/plugins/obs-transitions/data/locale/sr-CS.ini +++ b/plugins/obs-transitions/data/locale/sr-CS.ini @@ -2,6 +2,7 @@ FadeTransition="Zatamnjenje" CutTransition="Sečenje" SwipeTransition="Prevlačenje" SlideTransition="Klizanje" +StingerTransition="Stinger" FadeToColorTransition="Iščezavanje u boju" Direction="Pravac" Direction.Left="Levo" @@ -10,6 +11,15 @@ Direction.Up="Gore" Direction.Down="Dole" SwipeIn="Uvlačenje" Color="Boja" +VideoFile="Video fajl" +TransitionPoint="Početak prelaza (milisekunde)" +TransitionPointFrame="Početak prelaza (frejm)" +TransitionPointType="Tip početka prelaza" +TransitionPointTypeFrame="Frejm" +TransitionPointTypeTime="Vreme (milisekunde)" +AudioFadeStyle="Način na koji zvuk nestaje" +AudioFadeStyle.FadeOutFadeIn="Nestaje do tačke prelaza a zatim se ponovo vraća" +AudioFadeStyle.CrossFade="Nestaje unakrsno" SwitchPoint="Tačka vrhunca boje (procenat)" LumaWipeTransition="Luma brisanje" LumaWipe.Image="Slika" @@ -21,7 +31,7 @@ LumaWipe.Type.BarndoorTopLeft="Ambarska vrata odozgo sleva" LumaWipe.Type.BarndoorVertical="Ambarska vrata vertikalna" LumaWipe.Type.BlindsHorizontal="Zastori horizontalno" LumaWipe.Type.BoxBottomLeft="Kutija odozdo sleva" -LumaWipe.Type.BoxBottomRight="Kutija odozdo sdesna" +LumaWipe.Type.BoxBottomRight="Kutija odozdo zdesna" LumaWipe.Type.BoxTopLeft="Kutija odozgo sleva" LumaWipe.Type.BoxTopRight="Kutija odozgo sdesna" LumaWipe.Type.Burst="Rafal" @@ -35,7 +45,7 @@ LumaWipe.Type.Fractal="Fraktal" LumaWipe.Type.Iris="Žiža" LumaWipe.Type.LinearHorizontal="Linijski horizontalno" LumaWipe.Type.LinearTopLeft="Linijski odozgo sleva" -LumaWipe.Type.LinearTopRight="Linijski odozgo sdesna" +LumaWipe.Type.LinearTopRight="Linijski odozgo zdesna" LumaWipe.Type.LinearVertical="Linijski vertikalno" LumaWipe.Type.ParallelZigzagHorizontal="Paralelni cik-cak horizontalno" LumaWipe.Type.ParallelZigzagVertical="Paralelni cik-cak vertikalni" @@ -49,4 +59,8 @@ LumaWipe.Type.StripsVertical="Trake vertikalno" LumaWipe.Type.Watercolor="Vodene bojice" LumaWipe.Type.ZigzagHorizontal="Cik-cak horizontalno" LumaWipe.Type.ZigzagVertical="Cik-cak vertikalno" +AudioMonitoring="Audio nadzor" +AudioMonitoring.None="Monitor je isključen" +AudioMonitoring.MonitorOnly="Uključen je samo monitor (isključen je zvuk)" +AudioMonitoring.Both="Uključeni su i monitor i zvuk" diff --git a/plugins/obs-transitions/data/locale/sr-SP.ini b/plugins/obs-transitions/data/locale/sr-SP.ini index 12be3a2..0934699 100644 --- a/plugins/obs-transitions/data/locale/sr-SP.ini +++ b/plugins/obs-transitions/data/locale/sr-SP.ini @@ -2,6 +2,7 @@ FadeTransition="Затамњење" CutTransition="Сечење" SwipeTransition="Превлачење" SlideTransition="Клизање" +StingerTransition="Stinger" FadeToColorTransition="Ишчезавање у боју" Direction="Правац" Direction.Left="Лево" @@ -10,6 +11,15 @@ Direction.Up="Горе" Direction.Down="Доле" SwipeIn="Увлачење" Color="Боја" +VideoFile="Видео фајл" +TransitionPoint="Почетак прелаза (милисекунде)" +TransitionPointFrame="Почетак прелаза (фрејм)" +TransitionPointType="Тип почетка прелаза" +TransitionPointTypeFrame="Фрејм" +TransitionPointTypeTime="Време (милисекунде)" +AudioFadeStyle="Начин на који звук нестаје" +AudioFadeStyle.FadeOutFadeIn="Нестаје до тачке прелаза а затим се поново враћа" +AudioFadeStyle.CrossFade="Нестаје унакрсно" SwitchPoint="Тачка врхунца боје (проценат)" LumaWipeTransition="Лума брисање" LumaWipe.Image="Слика" @@ -21,7 +31,7 @@ LumaWipe.Type.BarndoorTopLeft="Амбарска врата одозго слев LumaWipe.Type.BarndoorVertical="Амбарска врата вертикална" LumaWipe.Type.BlindsHorizontal="Застори хоризонтално" LumaWipe.Type.BoxBottomLeft="Кутија одоздо слева" -LumaWipe.Type.BoxBottomRight="Кутија одоздо сдесна" +LumaWipe.Type.BoxBottomRight="Кутија одоздо здесна" LumaWipe.Type.BoxTopLeft="Кутија одозго слева" LumaWipe.Type.BoxTopRight="Кутија одозго сдесна" LumaWipe.Type.Burst="Рафал" @@ -35,7 +45,7 @@ LumaWipe.Type.Fractal="Фрактал" LumaWipe.Type.Iris="Жижа" LumaWipe.Type.LinearHorizontal="Линијски хоризонтално" LumaWipe.Type.LinearTopLeft="Линијски одозго слева" -LumaWipe.Type.LinearTopRight="Линијски одозго сдесна" +LumaWipe.Type.LinearTopRight="Линијски одозго здесна" LumaWipe.Type.LinearVertical="Линијски вертикално" LumaWipe.Type.ParallelZigzagHorizontal="Паралелни цик-цак хоризонтално" LumaWipe.Type.ParallelZigzagVertical="Паралални цик-цак вертикално" @@ -49,4 +59,8 @@ LumaWipe.Type.StripsVertical="Траке вертикално" LumaWipe.Type.Watercolor="Водене бојице" LumaWipe.Type.ZigzagHorizontal="Цик-цак хоризонтално" LumaWipe.Type.ZigzagVertical="Цик-цак вертикално" +AudioMonitoring="Аудио надзор" +AudioMonitoring.None="Монитор је искључен" +AudioMonitoring.MonitorOnly="Укључен је само монитор (искључен је звук)" +AudioMonitoring.Both="Укључени су и монитор и звук" diff --git a/plugins/obs-transitions/data/locale/zh-CN.ini b/plugins/obs-transitions/data/locale/zh-CN.ini index bfd17d7..7bd0868 100644 --- a/plugins/obs-transitions/data/locale/zh-CN.ini +++ b/plugins/obs-transitions/data/locale/zh-CN.ini @@ -1,22 +1,22 @@ FadeTransition="淡出" -CutTransition="剪切" -SwipeTransition="滑动" -SlideTransition="滑动" -StingerTransition="毒刺" +CutTransition="直接切换" +SwipeTransition="滑入滑出" +SlideTransition="幻灯片" +StingerTransition="插入视频" FadeToColorTransition="色彩淡入淡出" Direction="方向" Direction.Left="左" Direction.Right="右" Direction.Up="上" Direction.Down="下" -SwipeIn="向上滑动" +SwipeIn="滑入" Color="色彩" VideoFile="视频文件" -TransitionPoint="转换点 (毫秒)" -TransitionPointFrame="转换点 (帧)" +TransitionPoint="转换点(毫秒)" +TransitionPointFrame="转换点(帧)" TransitionPointType="转换点类型" TransitionPointTypeFrame="帧" -TransitionPointTypeTime="时间 (毫秒)" +TransitionPointTypeTime="时间(毫秒)" AudioFadeStyle="音频淡入淡出样式" AudioFadeStyle.FadeOutFadeIn="淡出到过渡点然后淡入" AudioFadeStyle.CrossFade="交叉淡入淡出" @@ -24,10 +24,10 @@ SwitchPoint="峰值颜色点(百分比)" LumaWipeTransition="亮度擦除" LumaWipe.Image="图像" LumaWipe.Invert="反转" -LumaWipe.Softness="柔和" +LumaWipe.Softness="柔和度" LumaWipe.Type.BarndoorBottomLeft="左下角门" LumaWipe.Type.BarndoorHorizontal="水平门" -LumaWipe.Type.BarndoorTopLeft="右上角门" +LumaWipe.Type.BarndoorTopLeft="左上角门" LumaWipe.Type.BarndoorVertical="垂直门" LumaWipe.Type.BlindsHorizontal="水平百叶窗" LumaWipe.Type.BoxBottomLeft="左下角盒子" @@ -41,17 +41,17 @@ LumaWipe.Type.Clock="时钟" LumaWipe.Type.Cloud="云" LumaWipe.Type.Curtain="窗帘" LumaWipe.Type.Fan="风扇" -LumaWipe.Type.Fractal="不规则碎片" +LumaWipe.Type.Fractal="分形" LumaWipe.Type.Iris="中心" LumaWipe.Type.LinearHorizontal="线性水平" LumaWipe.Type.LinearTopLeft="线性左上" LumaWipe.Type.LinearTopRight="线性右上" LumaWipe.Type.LinearVertical="线性垂直" -LumaWipe.Type.ParallelZigzagHorizontal="平行水波水平" -LumaWipe.Type.ParallelZigzagVertical="平行水波垂直" +LumaWipe.Type.ParallelZigzagHorizontal="并排Z字形水平" +LumaWipe.Type.ParallelZigzagVertical="并排Z字形垂直" LumaWipe.Type.Sinus9="Sinus 9" LumaWipe.Type.Spiral="螺旋" -LumaWipe.Type.Square="正方型" +LumaWipe.Type.Square="正方形" LumaWipe.Type.Squares="方块" LumaWipe.Type.Stripes="条纹" LumaWipe.Type.StripsHorizontal="条纹水平" @@ -59,8 +59,8 @@ LumaWipe.Type.StripsVertical="条纹垂直" LumaWipe.Type.Watercolor="水彩" LumaWipe.Type.ZigzagHorizontal="Z字形水平" LumaWipe.Type.ZigzagVertical="Z字形垂直" -AudioMonitoring="音频监测" -AudioMonitoring.None="关闭监视" -AudioMonitoring.MonitorOnly="仅显示器(输出静音)" -AudioMonitoring.Both="显示器和输出" +AudioMonitoring="音频监听" +AudioMonitoring.None="关闭监听" +AudioMonitoring.MonitorOnly="仅监听(不输出音频)" +AudioMonitoring.Both="监听并输出" diff --git a/plugins/obs-transitions/data/luma_wipe_transition.effect b/plugins/obs-transitions/data/luma_wipe_transition.effect index 92fb318..baab294 100644 --- a/plugins/obs-transitions/data/luma_wipe_transition.effect +++ b/plugins/obs-transitions/data/luma_wipe_transition.effect @@ -20,8 +20,6 @@ struct VertData { float2 uv : TEXCOORD0; }; -#include "premultiplied.inc" - VertData VSDefault(VertData v_in) { VertData vert_out; @@ -33,8 +31,8 @@ VertData VSDefault(VertData v_in) float4 PSLumaWipe(VertData v_in) : TARGET { float2 uv = v_in.uv; - float4 a_color = convert_pmalpha(a_tex.Sample(textureSampler, uv)); - float4 b_color = convert_pmalpha(b_tex.Sample(textureSampler, uv)); + float4 a_color = a_tex.Sample(textureSampler, uv); + float4 b_color = b_tex.Sample(textureSampler, uv); float luma = l_tex.Sample(textureSampler, uv).x; if (invert) diff --git a/plugins/obs-transitions/data/premultiplied.inc b/plugins/obs-transitions/data/premultiplied.inc deleted file mode 100644 index 450f6d4..0000000 --- a/plugins/obs-transitions/data/premultiplied.inc +++ /dev/null @@ -1,9 +0,0 @@ -float4 convert_pmalpha(float4 color) -{ - float4 ret = color; - if (color.a >= 0.001) - ret.xyz /= color.a; - else - ret = float4(0.0, 0.0, 0.0, 0.0); - return ret; -} diff --git a/plugins/obs-transitions/data/slide_transition.effect b/plugins/obs-transitions/data/slide_transition.effect index 489ae7d..3193893 100644 --- a/plugins/obs-transitions/data/slide_transition.effect +++ b/plugins/obs-transitions/data/slide_transition.effect @@ -16,8 +16,6 @@ struct VertData { float2 uv : TEXCOORD0; }; -#include "premultiplied.inc" - VertData VSDefault(VertData v_in) { VertData vert_out; @@ -37,7 +35,7 @@ float4 PSSlide(VertData v_in) : TARGET ? tex_b.Sample(textureSampler, tex_b_uv) : tex_a.Sample(textureSampler, tex_a_uv); - return convert_pmalpha(outc); + return outc; } technique Slide diff --git a/plugins/obs-transitions/data/swipe_transition.effect b/plugins/obs-transitions/data/swipe_transition.effect index 532399e..e2e6dd1 100644 --- a/plugins/obs-transitions/data/swipe_transition.effect +++ b/plugins/obs-transitions/data/swipe_transition.effect @@ -14,8 +14,6 @@ struct VertData { float2 uv : TEXCOORD0; }; -#include "premultiplied.inc" - VertData VSDefault(VertData v_in) { VertData vert_out; @@ -34,7 +32,7 @@ float4 PSSwipe(VertData v_in) : TARGET ? tex_b.Sample(textureSampler, v_in.uv) : tex_a.Sample(textureSampler, swipe_uv); - return convert_pmalpha(outc); + return outc; } technique Swipe diff --git a/plugins/obs-transitions/obs-transitions.c b/plugins/obs-transitions/obs-transitions.c index 22276d3..3646993 100644 --- a/plugins/obs-transitions/obs-transitions.c +++ b/plugins/obs-transitions/obs-transitions.c @@ -1,8 +1,11 @@ #include OBS_DECLARE_MODULE() - OBS_MODULE_USE_DEFAULT_LOCALE("obs-transitions", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "OBS core transitions"; +} extern struct obs_source_info cut_transition; extern struct obs_source_info fade_transition; diff --git a/plugins/obs-x264/data/locale/da-DK.ini b/plugins/obs-x264/data/locale/da-DK.ini index 6c22fff..d3eb6ec 100644 --- a/plugins/obs-x264/data/locale/da-DK.ini +++ b/plugins/obs-x264/data/locale/da-DK.ini @@ -1,13 +1,13 @@ -Bitrate="Bitrate" -CustomBufsize="Brug brugerdefineret bufferstørrelse" -BufferSize="Bufferstørrelsen" +Bitrate="Bit-hastighed" +CustomBufsize="Benyt tilpasset bufferstørrelse" +BufferSize="Bufferstørrelse" RateControl="Rate kontrol" CRF="CRF" -KeyframeIntervalSec="Keyframe interval (sekunder, 0 = auto)" -CPUPreset="CPU forbrug indstilling (højere = mindre CPU)" +KeyframeIntervalSec="Keyframe-interval (sek., 0 = auto)" +CPUPreset="CPU-forbrugsforvalg (højere = mindre CPU)" Profile="Profil" -Tune="Stil ind" +Tune="Tuning" None="(Ingen)" -EncoderOptions="x264 indstillinger (adskilt af mellemrum)" -VFR="Variable framerate (VFR)" +EncoderOptions="x264-indstillinger (adskilt af mellemrum)" +VFR="Variable billedhastighed (VFR)" diff --git a/plugins/obs-x264/data/locale/de-DE.ini b/plugins/obs-x264/data/locale/de-DE.ini index 15adf9e..904eab9 100644 --- a/plugins/obs-x264/data/locale/de-DE.ini +++ b/plugins/obs-x264/data/locale/de-DE.ini @@ -8,6 +8,6 @@ CPUPreset="Prozessorauslastung-Voreinstellung (höher = weniger CPU Auslastung)" Profile="Profil" Tune="Tune" None="(Nichts)" -EncoderOptions="x264 Optionen (durch Leerzeichen getrennt)" +EncoderOptions="x264-Optionen (durch Leerzeichen getrennt)" VFR="Variable Framerate (VFR)" diff --git a/plugins/obs-x264/data/locale/fa-IR.ini b/plugins/obs-x264/data/locale/fa-IR.ini new file mode 100644 index 0000000..3da9807 --- /dev/null +++ b/plugins/obs-x264/data/locale/fa-IR.ini @@ -0,0 +1,13 @@ +Bitrate="نرخ بیت" +CustomBufsize="استفاده از اندازه بافر سفارشی" +BufferSize="اندازه بافر" +RateControl="کنترل نرخ" +CRF="سی آر اف" +KeyframeIntervalSec="فاصله Keyframe (ثانیه 0 = خودکار)" +CPUPreset="استفاده پیشفرض از CPU (بالاتر = CPU کمتر)" +Profile="پروفایل" +Tune="تون" +None="(هیچ کدام)" +EncoderOptions="آپشن های x264 (با فضای جدا شده )" +VFR="فریم های متغیر (وی اف ار)" + diff --git a/plugins/obs-x264/data/locale/fr-FR.ini b/plugins/obs-x264/data/locale/fr-FR.ini index 949d7cb..eddaa3a 100644 --- a/plugins/obs-x264/data/locale/fr-FR.ini +++ b/plugins/obs-x264/data/locale/fr-FR.ini @@ -1,13 +1,13 @@ Bitrate="Débit" -CustomBufsize="Utiliser une taille de buffer personnalisée" +CustomBufsize="Utiliser une taille de tampon personnalisée" BufferSize="Taille du tampon" RateControl="Contrôle du débit" CRF="CRF" KeyframeIntervalSec="Intervalle d'image-clé (en secondes, 0 = auto)" -CPUPreset="Réglages prédéfinis du CPU (élevé = charge CPU faible)" +CPUPreset="Pré-réglages (plus rapide = charge CPU plus faible)" Profile="Profil" -Tune="Régler" +Tune="Réglage fin" None="(Aucun)" EncoderOptions="Options x264 (séparées par un espace)" -VFR="Fréquence d'images variable (VFR)" +VFR="Débit d'images variable (VFR)" diff --git a/plugins/obs-x264/data/locale/it-IT.ini b/plugins/obs-x264/data/locale/it-IT.ini index dd6ffc2..f953c4e 100644 --- a/plugins/obs-x264/data/locale/it-IT.ini +++ b/plugins/obs-x264/data/locale/it-IT.ini @@ -1,13 +1,13 @@ -Bitrate="Bitrate" -CustomBufsize="Usa dimensione personalizzata del buffer" -BufferSize="Grandezza buffer" -RateControl="Controllo frequenza" -CRF="CRF" -KeyframeIntervalSec="Intervallo keyframe (secondi, 0=auto)" -CPUPreset="Preset di utilizzo della CPU (superiore = meno CPU)" +Bitrate="Velocità in bit" +CustomBufsize="Utilizza una dimensione personalizzata del buffer" +BufferSize="Dimensione del buffer" +RateControl="Tipo di controllo della frequenza" +CRF="Fattore costante della frequenza (CRF)" +KeyframeIntervalSec="Intervallo dei fotogrammi chiave (in secondi, 0=automatico)" +CPUPreset="Preset dell'utilizzo della CPU (superiore = meno CPU)" Profile="Profilo" -Tune="Regola" +Tune="Regolazione di precisione" None="(nessuno)" -EncoderOptions="Opzioni x264 (separati da spazio)" -VFR="Framerate variabile (VFR)" +EncoderOptions="Parametri x264 (separati da uno spazio)" +VFR="Velocità dei fotogrammi variabile (VFR)" diff --git a/plugins/obs-x264/data/locale/ko-KR.ini b/plugins/obs-x264/data/locale/ko-KR.ini index f00a1ce..fd9ad08 100644 --- a/plugins/obs-x264/data/locale/ko-KR.ini +++ b/plugins/obs-x264/data/locale/ko-KR.ini @@ -1,5 +1,5 @@ Bitrate="비트레이트" -CustomBufsize="사용자 임의 버퍼 크기 설정" +CustomBufsize="사용자 임의 버퍼 크기 설정" BufferSize="버퍼 크기" RateControl="데이터율 제어" CRF="CRF" diff --git a/plugins/obs-x264/data/locale/mn-MN.ini b/plugins/obs-x264/data/locale/mn-MN.ini new file mode 100644 index 0000000..d6085d1 --- /dev/null +++ b/plugins/obs-x264/data/locale/mn-MN.ini @@ -0,0 +1,13 @@ +Bitrate="Битийн хурд" +CustomBufsize="Өөр Хэмжээг Хэрэглэх" +BufferSize="Өөр Хэмжээ" +RateControl="Чанарын Хяналт" +CRF="CRF" +KeyframeIntervalSec="Гол Фрэймийн Интервал (секунд, 0=авто)" +CPUPreset="CPU-ны хэрэглэх хурд (илүү өндөр = бага CPU)" +Profile="Профайл" +Tune="Тааруулах" +None="(Байхгүй)" +EncoderOptions="x264 Сонголт (зайгаар тусгаарлагдсан)" +VFR="Тогтмол Биш Фрэйм-ийн хэмжээ (VFR)" + diff --git a/plugins/obs-x264/data/locale/ro-RO.ini b/plugins/obs-x264/data/locale/ro-RO.ini index b308056..ff909b8 100644 --- a/plugins/obs-x264/data/locale/ro-RO.ini +++ b/plugins/obs-x264/data/locale/ro-RO.ini @@ -1,6 +1,7 @@ Bitrate="Rată de biți" -CustomBufsize="Folosește dimensiune personalizată pentru buffer" +CustomBufsize="Folosește dimensiune personalizată pentru zona tampon" BufferSize="Dimensiune pentru buffer" +RateControl="Controlul ratei" CRF="CRF" KeyframeIntervalSec="Interval de cadre cheie (secunde, 0=auto)" CPUPreset="Presetare pentru utilizare CPU (mai mare = mai puțin CPU)" diff --git a/plugins/obs-x264/data/locale/sk-SK.ini b/plugins/obs-x264/data/locale/sk-SK.ini index fb2e74c..b4117ed 100644 --- a/plugins/obs-x264/data/locale/sk-SK.ini +++ b/plugins/obs-x264/data/locale/sk-SK.ini @@ -1,9 +1,13 @@ -Bitrate="Bitrate" +Bitrate="Dátový tok" CustomBufsize="Použiť vlastnú veľkosť medzipamäte" BufferSize="Veľkosť medzipamäte" +RateControl="Riadenie toku" CRF="CRF" KeyframeIntervalSec="Kľúčová snímka každých (sekúnd, 0 = automaticky)" +CPUPreset="CPU predvoľba používania (vyššie = menej CPU)" Profile="Profil" Tune="Vyladenie" -None="(Žiadny)" +None="(Žiadne)" +EncoderOptions="Možnosti enkodéra x264 (oddelené medzerou)" +VFR="Premenlivá rýchlosť snímkov (VFR)" diff --git a/plugins/obs-x264/data/locale/zh-CN.ini b/plugins/obs-x264/data/locale/zh-CN.ini index 058c829..08767a2 100644 --- a/plugins/obs-x264/data/locale/zh-CN.ini +++ b/plugins/obs-x264/data/locale/zh-CN.ini @@ -1,12 +1,12 @@ Bitrate="比特率" CustomBufsize="使用自定义缓存大小" BufferSize="缓冲大小" -RateControl="速率控制" +RateControl="码率控制" CRF="CRF" KeyframeIntervalSec="关键帧间隔(秒, 0=自动)" CPUPreset="CPU 使用预设 (高 = 较少的 CPU占用)" -Profile="Profile" -Tune="协调(类型)" +Profile="配置(Profile)" +Tune="微调(Tune)" None="(无)" EncoderOptions="x264 选项 (用空格分隔)" VFR="可变帧率 (VFR)" diff --git a/plugins/obs-x264/obs-x264-plugin-main.c b/plugins/obs-x264/obs-x264-plugin-main.c index fbb4c2c..71d7d02 100644 --- a/plugins/obs-x264/obs-x264-plugin-main.c +++ b/plugins/obs-x264/obs-x264-plugin-main.c @@ -2,6 +2,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-x264", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "x264 based encoder"; +} extern struct obs_encoder_info obs_x264_encoder; diff --git a/plugins/obs-x264/obs-x264.c b/plugins/obs-x264/obs-x264.c index d177a8e..6e0d2e6 100644 --- a/plugins/obs-x264/obs-x264.c +++ b/plugins/obs-x264/obs-x264.c @@ -179,7 +179,9 @@ static obs_properties_t *obs_x264_props(void *unused) obs_property_set_modified_callback(list, rate_control_modified); - obs_properties_add_int(props, "bitrate", TEXT_BITRATE, 50, 10000000, 1); + p = obs_properties_add_int(props, "bitrate", + TEXT_BITRATE, 50, 10000000, 50); + obs_property_int_set_suffix(p, " Kbps"); p = obs_properties_add_bool(props, "use_bufsize", TEXT_CUSTOM_BUF); obs_property_set_modified_callback(p, use_bufsize_modified); diff --git a/plugins/rtmp-services/data/locale/da-DK.ini b/plugins/rtmp-services/data/locale/da-DK.ini index 0c413be..294a759 100644 --- a/plugins/rtmp-services/data/locale/da-DK.ini +++ b/plugins/rtmp-services/data/locale/da-DK.ini @@ -1,11 +1,11 @@ -StreamingServices="Streaming Tjenester" -CustomStreamingServer="Brugerdefineret Streaming Server" -Service="Service" +StreamingServices="Streamingtjenester" +CustomStreamingServer="Tilpasset Streamingserver" +Service="Tjeneste" Server="Server" Server.Auto="Auto (anbefalet)" -StreamKey="Stream nøgle" -UseAuth="Brug godkendelse" +StreamKey="Streamnøgle" +UseAuth="Benyt godkendelse" Username="Brugernavn" -Password="Kodeord" -ShowAll="Vis alle services" +Password="Adgangskode" +ShowAll="Vis alle tjenester" diff --git a/plugins/rtmp-services/data/locale/it-IT.ini b/plugins/rtmp-services/data/locale/it-IT.ini index 384d30d..2b18124 100644 --- a/plugins/rtmp-services/data/locale/it-IT.ini +++ b/plugins/rtmp-services/data/locale/it-IT.ini @@ -1,11 +1,11 @@ -StreamingServices="Servizi di streaming" -CustomStreamingServer="Personalizza il server di streaming" +StreamingServices="Servizi di trasmissione/dirette" +CustomStreamingServer="Server di trasmissione/dirette personalizzato" Service="Servizio" Server="Server" -Server.Auto="Auto (consigliato)" -StreamKey="Chiave Stream" -UseAuth="Usa autenticazione" -Username="Username" +Server.Auto="Automatico (consigliato)" +StreamKey="Codice delle dirette" +UseAuth="Utilizza l'autenticazione" +Username="Nome utente" Password="Password" ShowAll="Mostra tutti i servizi" diff --git a/plugins/rtmp-services/data/locale/ja-JP.ini b/plugins/rtmp-services/data/locale/ja-JP.ini index 80623e5..bf89ba7 100644 --- a/plugins/rtmp-services/data/locale/ja-JP.ini +++ b/plugins/rtmp-services/data/locale/ja-JP.ini @@ -1,4 +1,4 @@ -StreamingServices="ストリーミングサービス" +StreamingServices="配信サービス" CustomStreamingServer="カスタムストリーミングサーバー" Service="サービス" Server="サーバー" diff --git a/plugins/rtmp-services/data/locale/ka-GE.ini b/plugins/rtmp-services/data/locale/ka-GE.ini index a095db7..1d04352 100644 --- a/plugins/rtmp-services/data/locale/ka-GE.ini +++ b/plugins/rtmp-services/data/locale/ka-GE.ini @@ -4,7 +4,7 @@ Service="მომსახურება" Server="სერვერი" Server.Auto="ავტომატური (სასურველია)" StreamKey="ნაკადის გასაღები" -UseAuth="ანგარიშზე შესვლის გამოყენება" +UseAuth="ანგარიშზე შესვლით" Username="მომხმარებლის სახელი" Password="პაროლი" ShowAll="ყველა მომსახურების ჩვენება" diff --git a/plugins/rtmp-services/data/locale/mn-MN.ini b/plugins/rtmp-services/data/locale/mn-MN.ini new file mode 100644 index 0000000..a3fbe1c --- /dev/null +++ b/plugins/rtmp-services/data/locale/mn-MN.ini @@ -0,0 +1,11 @@ +StreamingServices="Цацалтийн Сервис" +CustomStreamingServer="Өөр Цацалтийн Сервис" +Service="Сервис" +Server="Сервер" +Server.Auto="Авто (Зохимжтой)" +StreamKey="Цацалтын түлхүүр үгс" +UseAuth="Цахим баталгаажуулалт" +Username="Хэрэглэгчийн нэр" +Password="Нууц үг" +ShowAll="Бүх сервисийг харуул" + diff --git a/plugins/rtmp-services/data/locale/pt-PT.ini b/plugins/rtmp-services/data/locale/pt-PT.ini index 9e092d3..baa732a 100644 --- a/plugins/rtmp-services/data/locale/pt-PT.ini +++ b/plugins/rtmp-services/data/locale/pt-PT.ini @@ -2,6 +2,7 @@ StreamingServices="Serviço de transmissão" CustomStreamingServer="Servidor de transmissão personalizado" Service="Serviço" Server="Servidor" +Server.Auto="Automático (Recomendado)" StreamKey="Chave da transmissão" UseAuth="Utilizar autenticação" Username="Nome de utilizador" diff --git a/plugins/rtmp-services/data/locale/ro-RO.ini b/plugins/rtmp-services/data/locale/ro-RO.ini index 4e3ae39..1fa6543 100644 --- a/plugins/rtmp-services/data/locale/ro-RO.ini +++ b/plugins/rtmp-services/data/locale/ro-RO.ini @@ -1,10 +1,11 @@ -StreamingServices="Servicii de streaming" -CustomStreamingServer="Server de streaming personalizat" +StreamingServices="Servicii de transmisiune" +CustomStreamingServer="Server personalizat de transmisiune" Service="Serviciu" Server="Server" -StreamKey="Cheie de stream" -UseAuth="Folosește autentificare" +Server.Auto="Automat (Recomandat)" +StreamKey="Cheie de transmisiune" +UseAuth="Folosește autentificarea" Username="Nume de utilizator" Password="Parolă" -ShowAll="Arată toate serviciile" +ShowAll="Afișează toate serviciile" diff --git a/plugins/rtmp-services/data/locale/sr-CS.ini b/plugins/rtmp-services/data/locale/sr-CS.ini index ef513b6..db1ce28 100644 --- a/plugins/rtmp-services/data/locale/sr-CS.ini +++ b/plugins/rtmp-services/data/locale/sr-CS.ini @@ -2,6 +2,7 @@ StreamingServices="Servisi za strimovanje" CustomStreamingServer="Posebni server za strim" Service="Servis" Server="Server" +Server.Auto="Automatski (Preporučljivo)" StreamKey="Strim ključ" UseAuth="Koristi autentifikaciju" Username="Korisničko ime" diff --git a/plugins/rtmp-services/data/locale/sr-SP.ini b/plugins/rtmp-services/data/locale/sr-SP.ini index f09913a..a5c7ffc 100644 --- a/plugins/rtmp-services/data/locale/sr-SP.ini +++ b/plugins/rtmp-services/data/locale/sr-SP.ini @@ -2,6 +2,7 @@ StreamingServices="Сервиси за стримовање" CustomStreamingServer="Посебни сервер за стрим" Service="Сервис" Server="Сервер" +Server.Auto="Аутоматски (препоручљиво)" StreamKey="Стрим кључ" UseAuth="Користи аутентификацију" Username="Корисничко име" diff --git a/plugins/rtmp-services/data/locale/uk-UA.ini b/plugins/rtmp-services/data/locale/uk-UA.ini index c7ab4df..12a84af 100644 --- a/plugins/rtmp-services/data/locale/uk-UA.ini +++ b/plugins/rtmp-services/data/locale/uk-UA.ini @@ -4,7 +4,7 @@ Service="Сервіс" Server="Сервер" Server.Auto="Автоматично (рекомендується)" StreamKey="Ключ трансляції" -UseAuth="Використовувати авторизацію" +UseAuth="Використовувати автентифікацію" Username="Логін" Password="Пароль" ShowAll="Показати всі сервіси" diff --git a/plugins/rtmp-services/data/locale/vi-VN.ini b/plugins/rtmp-services/data/locale/vi-VN.ini index 7694d88..77bc84a 100644 --- a/plugins/rtmp-services/data/locale/vi-VN.ini +++ b/plugins/rtmp-services/data/locale/vi-VN.ini @@ -1,4 +1,4 @@ -StreamingServices="Dịch vụ stream" +StreamingServices="Dịch vụ truyền" CustomStreamingServer="Tùy chọn máy chủ truyền trực tuyến" Service="Dịch vụ" Server="Máy chủ" diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 0f439c3..f514d10 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": 88, + "version": 107, "files": [ { "name": "services.json", - "version": 88 + "version": 107 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 416ecd8..9276a35 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -1,5 +1,5 @@ { - "format_version": 1, + "format_version": 2, "services": [ { "name": "Twitch", @@ -37,6 +37,10 @@ "name": "EU: Berlin, DE", "url": "rtmp://live-ber.twitch.tv/app" }, + { + "name": "Europe: Copenhagen, DK", + "url": "rtmp://live-cph.twitch.tv/app" + }, { "name": "EU: Frankfurt, DE", "url": "rtmp://live-fra.twitch.tv/app" @@ -220,26 +224,10 @@ "name": "EU-North: Amsterdam, Netherlands", "url": "rtmp://live.ams.hitbox.tv/push" }, - { - "name": "EU-West: Frankfurt, Germany", - "url": "rtmp://live.fra.hitbox.tv/push" - }, { "name": "EU-West: Paris, France", "url": "rtmp://live.cdg.hitbox.tv/push" }, - { - "name": "EU-West: London, United Kingdom", - "url": "rtmp://live.lhr.hitbox.tv/push" - }, - { - "name": "EU-Central: Nurnberg, Germany", - "url": "rtmp://live.nbg.hitbox.tv/push" - }, - { - "name": "EU-East: Vienna, Austria", - "url": "rtmp://live.vie.hitbox.tv/push" - }, { "name": "EU-South: Milan, Italia", "url": "rtmp://live.mxp.hitbox.tv/push" @@ -249,17 +237,9 @@ "url": "rtmp://live.dme.hitbox.tv/push" }, { - "name": "US-East: New York - 1", + "name": "US-East: New York", "url": "rtmp://live.jfk.hitbox.tv/push" }, - { - "name": "US-East: New York - 2", - "url": "rtmp://live.nyc.hitbox.tv/push" - }, - { - "name": "US-Central: Denver", - "url": "rtmp://live.den.hitbox.tv/push" - }, { "name": "US-West: San Francisco", "url": "rtmp://live.sfo.hitbox.tv/push" @@ -272,18 +252,10 @@ "name": "South America: Sao Paulo, Brazil", "url": "rtmp://live.gru.hitbox.tv/push" }, - { - "name": "South Korea: Seoul", - "url": "rtmp://live.icn.hitbox.tv/push" - }, { "name": "Asia: Singapore", "url": "rtmp://live.sin.hitbox.tv/push" }, - { - "name": "China: Hong Kong", - "url": "rtmp://live.hkg.hitbox.tv/push" - }, { "name": "Oceania: Sydney, Australia", "url": "rtmp://live.syd.hitbox.tv/push" @@ -378,7 +350,7 @@ } ], "recommended": { - "keyint": 3, + "keyint": 2, "output": "ftl_output", "max audio bitrate": 160, "max video bitrate": 10000, @@ -468,7 +440,7 @@ } ], "recommended": { - "keyint": 3, + "keyint": 2, "max audio bitrate": 160, "max video bitrate": 10000, "profile": "main" @@ -490,23 +462,19 @@ } }, { - "name": "DailyMotion", - "common": true, + "name": "GamePlank", "servers": [ { "name": "Primary", - "url": "rtmp://publish.dailymotion.com/publish-dm" + "url": "rtmp://live.gameplank.tv/go" } - ] - }, - { - "name": "WatchPeopleCode.com", - "servers": [ - { - "name": "Primary", - "url": "rtmp://streaming.watchpeoplecode.com/live" - } - ] + ], + "recommended": { + "keyint": 1, + "profile": "main", + "max video bitrate": 2500, + "max audio bitrate": 160 + } }, { "name": "Web.TV", @@ -532,33 +500,6 @@ } ] }, - { - "name": "GamePlank", - "servers": [ - { - "name": "Primary", - "url": "rtmp://live.gameplank.tv/app" - }, - { - "name": "US: Oregon", - "url": "rtmp://live-or.gameplank.tv/app" - }, - { - "name": "US: Virginia", - "url": "rtmp://live-va.gameplank.tv/app" - }, - { - "name": "UK: London", - "url": "rtmp://live-ldn.gameplank.tv/app" - } - ], - "recommended": { - "keyint": 1, - "max video bitrate": 1500, - "max audio bitrate": 160, - "x264opts": "scenecut=0" - } - }, { "name": "Vaughn Live / iNSTAGIB", "servers": [ @@ -664,7 +605,10 @@ } }, { - "name": "Restream.io", + "name": "Restream.io - RTMP", + "alt_names": [ + "Restream.io" + ], "common": true, "servers": [ { @@ -743,6 +687,10 @@ "name": "Asia (Tokyo, Japan)", "url": "rtmp://tokyo.restream.io/live" }, + { + "name": "India (Bangalore)", + "url": "rtmp://india.restream.io/live" + }, { "name": "Australia (Sydney)", "url": "rtmp://au.restream.io/live" @@ -752,6 +700,109 @@ "keyint": 2 } }, + { + "name": "Restream.io - FTL", + "common": true, + "servers": [ + { + "name": "Autodetect", + "url": "live.restream.io" + }, + { + "name": "EU-West (London, GB)", + "url": "eu-london.restream.io" + }, + { + "name": "EU-West (Amsterdam, NL)", + "url": "eu-ams.restream.io" + }, + { + "name": "EU-West (Luxembourg)", + "url": "eu-luxembourg.restream.io" + }, + { + "name": "EU-West (Paris, FR)", + "url": "eu-paris.restream.io" + }, + { + "name": "EU-Central (Frankfurt, DE)", + "url": "eu-central.restream.io" + }, + { + "name": "EU-East (Falkenstein, DE)", + "url": "eu-east.restream.io" + }, + { + "name": "EU-South (Madrid, Spain)", + "url": "eu-madrid.restream.io" + }, + { + "name": "Russia (Moscow)", + "url": "ru.restream.io" + }, + { + "name": "US-West (Seattle, WA)", + "url": "us-seattle.restream.io" + }, + { + "name": "US-West (San Jose, CA)", + "url": "us-west.restream.io" + }, + { + "name": "US-Central (Dallas, TX)", + "url": "us-central.restream.io" + }, + { + "name": "US-East (Washington, DC)", + "url": "us-east.restream.io" + }, + { + "name": "US-East (Miami, FL)", + "url": "us-miami.restream.io" + }, + { + "name": "NA-East (Toronto, Canada)", + "url": "na-toronto.restream.io" + }, + { + "name": "SA (Saint Paul, Brazil)", + "url": "sa.restream.io" + }, + { + "name": "Asia (Singapore)", + "url": "singapore.restream.io" + }, + { + "name": "Asia (Seoul, South Korea)", + "url": "seoul.restream.io" + }, + { + "name": "Asia (Tokyo, Japan)", + "url": "tokyo.restream.io" + }, + { + "name": "Australia (Sydney)", + "url": "au.restream.io" + } + ], + "recommended": { + "keyint": 2, + "output": "ftl_output", + "max audio bitrate": 160, + "max video bitrate": 10000, + "profile": "main", + "bframes": 0 + } + }, + { + "name": "GameTips.TV", + "servers": [ + { + "name": "Server Iran", + "url": "rtmp://rtmp.cdn.server1.gametips.tv:1935/hls" + } + ] + }, { "name": "Nood", "servers": [ @@ -838,6 +889,10 @@ "name": "Los Angeles US", "url": "rtmp://la.castr.io" }, + { + "name": "New York US", + "url": "rtmp://ny.castr.io" + }, { "name": "Montreal CA", "url": "rtmp://qc.castr.io" @@ -850,6 +905,10 @@ "name": "Frankfurt DE", "url": "rtmp://de.castr.io" }, + { + "name": "Frankfurt DE 2", + "url": "rtmp://fr.castr.io" + }, { "name": "Moscow RU", "url": "rtmp://ru.castr.io" @@ -861,6 +920,34 @@ { "name": "Sydney AU", "url": "rtmp://au.castr.io" + }, + { + "name": "Brazil", + "url": "rtmp://br.castr.io" + }, + { + "name": "US Central", + "url": "rtmp://us-central.castr.io" + }, + { + "name": "US West", + "url": "rtmp://us-west.castr.io" + }, + { + "name": "US East", + "url": "rtmp://us-east.castr.io" + }, + { + "name": "US South", + "url": "rtmp://us-south.castr.io" + }, + { + "name": "South America", + "url": "rtmp://south-am.castr.io" + }, + { + "name": "EU Central", + "url": "rtmp://eu-central.castr.io" } ], "recommended": { @@ -917,10 +1004,6 @@ { "name": "Asia : Singapore", "url": "rtmp://rtmpmanager-aws-sg.afreeca.tv/live" - }, - { - "name": "Asia : South Korea", - "url": "rtmp://rtmpmanager-en-ko.afreeca.tv/live" } ], "recommended": { @@ -930,44 +1013,6 @@ "max audio bitrate": 192 } }, - { - "name": "アフリカTV", - "servers": [ - { - "name": "Japan", - "url": "rtmp://rtmpmanager-aws-jp.afreeca.tv/live/" - }, - { - "name": "South Korea", - "url": "rtmp://rtmpmanager-jp.afreeca.tv/live/" - } - ], - "recommended": { - "keyint": 1, - "profile": "main", - "max video bitrate": 5000, - "max audio bitrate": 192 - } - }, - { - "name": "艾菲卡TV", - "servers": [ - { - "name": "Taiwan", - "url": "rtmp://rtmpmanager-gcp-tw.afreeca.tv/live/" - }, - { - "name": "South Korea", - "url": "rtmp://rtmpmanager-tw-ko.afreeca.tv/live/" - } - ], - "recommended": { - "keyint": 1, - "profile": "main", - "max video bitrate": 5000, - "max audio bitrate": 192 - } - }, { "name": "아프리카TV", "servers": [ @@ -1042,62 +1087,76 @@ "name": "Chaturbate", "servers": [ { - "name": "Default Global Auto Select - Recommended", + "name": "Global Main Fastest - Recommended", "url": "rtmp://live.stream.highwebmedia.com/live-origin" }, { - "name": "US West", - "url": "rtmp://live-us-west.stream.highwebmedia.com/live-origin" + "name": "Global Backup", + "url": "rtmp://live-backup.stream.highwebmedia.com/live-origin" }, { - "name": "US Central", - "url": "rtmp://live-us-central.stream.highwebmedia.com/live-origin" + "name": "US West: Seattle, WA", + "url": "rtmp://live-sea.stream.highwebmedia.com/live-origin" }, { - "name": "US East", - "url": "rtmp://live-us-east.stream.highwebmedia.com/live-origin" + "name": "US West: Phoenix, AZ", + "url": "rtmp://live-phx.stream.highwebmedia.com/live-origin" }, { - "name": "Europe West", - "url": "rtmp://live-eu-west.stream.highwebmedia.com/live-origin" + "name": "US Central: Salt Lake City, UT", + "url": "rtmp://live-slc.stream.highwebmedia.com/live-origin" }, { - "name": "Europe East", - "url": "rtmp://live-eu-east.stream.highwebmedia.com/live-origin" + "name": "US Central: Chicago, IL", + "url": "rtmp://live-chi.stream.highwebmedia.com/live-origin" }, { - "name": "Asia/Pacific South", - "url": "rtmp://live-as-south.stream.highwebmedia.com/live-origin" + "name": "US East: Atlanta, GA", + "url": "rtmp://live-atl.stream.highwebmedia.com/live-origin" }, { - "name": "Asia/Pacific North-East", - "url": "rtmp://live-as-northeast.stream.highwebmedia.com/live-origin" + "name": "US East: Ashburn, VA", + "url": "rtmp://live-ash.stream.highwebmedia.com/live-origin" + }, + { + "name": "South America: Sao Paulo, Brazil", + "url": "rtmp://live-gru.stream.highwebmedia.com/live-origin" + }, + { + "name": "EU: Amsterdam, NL", + "url": "rtmp://live-nld.stream.highwebmedia.com/live-origin" + }, + { + "name": "EU: Alblasserdam, NL", + "url": "rtmp://live-alb.stream.highwebmedia.com/live-origin" + }, + { + "name": "EU: Frankfurt, DE", + "url": "rtmp://live-fra.stream.highwebmedia.com/live-origin" + }, + { + "name": "EU: Belgrade, Serbia", + "url": "rtmp://live-srb.stream.highwebmedia.com/live-origin" + }, + { + "name": "Asia: Singapore", + "url": "rtmp://live-sin.stream.highwebmedia.com/live-origin" + }, + { + "name": "Asia: Tokyo, Japan", + "url": "rtmp://live-nrt.stream.highwebmedia.com/live-origin" + }, + { + "name": "Australia: Sydney", + "url": "rtmp://live-syd.stream.highwebmedia.com/live-origin" } ], "recommended": { "keyint": 2, - "max video bitrate": 20000, + "max video bitrate": 50000, "max audio bitrate": 192 } }, - { - "name": "LiveEdu.tv", - "common": true, - "servers": [ - { - "name": "US", - "url": "rtmp://usmedia11.liveedu.tv/liveedutv" - }, - { - "name": "EU", - "url": "rtmp://eumedia8.liveedu.tv/liveedutv" - }, - { - "name": "Asia", - "url": "rtmp://apmedia1.liveedu.tv/liveedutv" - } - ] - }, { "name": "Twitter / Periscope", "common": true, @@ -1242,16 +1301,22 @@ } }, { - "name": "Lahzenegar - لحظه نگار", + "name": "Lahzenegar - StreamG | لحظه‌نگار - استریمجی", "servers": [ { "name": "Primary", - "url": "rtmp://live.lahzenegar.com:80/pro" + "url": "rtmp://rtmp.lahzecdn.com/pro" + }, + { + "name": "Iran", + "url": "rtmp://rtmp-iran.lahzecdn.com/pro" } ], "recommended": { - "max video bitrate": 1000, - "max audio bitrate": 96 + "keyint": 2, + "profile": "main", + "max video bitrate": 2800, + "max audio bitrate": 128 } }, { @@ -1326,13 +1391,120 @@ } }, { - "name": "DTube", + "name": "STAGE TEN", + "servers": [ + { + "name": "STAGE TEN", + "url": "rtmps://app-rtmp.stageten.tv:443/stageten" + } + ], + "recommended": { + "keyint": 2, + "profile": "baseline", + "max video bitrate": 4000, + "max audio bitrate": 128 + } + }, + { + "name": "DLive", "servers": [ { "name": "Default", - "url": "rtmp://stream.dtube.top/live/" + "url": "rtmp://stream.dlive.tv/live" } - ] + ], + "recommend": { + "keyint": 2, + "max video bitrate": 6000, + "max audio bitrate": 160 + } + }, + { + "name": "Lightcast.com", + "servers": [ + { + "name": "North America / East", + "url": "rtmp://us-east.live.lightcast.com/202E1F/default" + }, + { + "name": "North America / West", + "url": "rtmp://us-west.live.lightcast.com/202E1F/default" + }, + { + "name": "Europe / Amsterdam", + "url": "rtmp://europe.live.lightcast.com/202E1F/default" + }, + { + "name": "Europe / Frankfurt", + "url": "rtmp://europe-fra.live.lightcast.com/202E1F/default" + }, + { + "name": "Europe / Stockholm", + "url": "rtmp://europe-sto.live.lightcast.com/202E1F/default" + }, + { + "name": "Asia / Hong Kong", + "url": "rtmp://asia.live.lightcast.com/202E1F/default" + }, + { + "name": "Australia / Sydney", + "url": "rtmp://australia.live.lightcast.com/202E1F/default" + } + ], + "recommend": { + "keyint": 2, + "max video bitrate": 6000, + "max audio bitrate": 160 + } + }, + { + "name": "Bongacams", + "servers": [ + { + "name": "Default", + "url": "rtmp://origin.bcrncdn.com:1934/live" + } + ], + "recommend": { + "keyint": 2, + "max video bitrate": 6000, + "max audio bitrate": 192 + } + }, + { + "name": "Camplace", + "servers": [ + { + "name": "Camplace - Default", + "url": "rtmp://rtmp.camplace.com" + } + ], + "recommend": { + "keyint": 2, + "max video bitrate": 3000, + "max audio bitrate": 128 + } + }, + { + "name": "OnlyFans.com", + "servers": [ + { + "name": "USA", + "url": "rtmp://route0.onlyfans.com/live" + }, + { + "name": "Europe", + "url": "rtmp://route0-dc2.onlyfans.com/live" + } + ], + "recommend": { + "keyint": 2, + "profile": "main", + "max video bitrate": 2500, + "max audio bitrate": 192, + "bframes": 0, + "x264opts": "tune=zerolatency" + } } ] } diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index b7bce8c..272fa77 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -21,7 +21,8 @@ static const char *rtmp_common_getname(void *unused) } static json_t *open_services_file(void); -static inline json_t *find_service(json_t *root, const char *name); +static inline json_t *find_service(json_t *root, const char *name, + const char **p_new_name); static inline const char *get_string_val(json_t *service, const char *key); extern void twitch_ingests_refresh(int seconds); @@ -75,7 +76,14 @@ static void rtmp_common_update(void *data, obs_data_t *settings) json_t *root = open_services_file(); if (root) { - json_t *serv = find_service(root, service->service); + const char *new_name; + json_t *serv = find_service(root, service->service, &new_name); + + if (new_name) { + bfree(service->service); + service->service = bstrdup(new_name); + } + if (serv) { json_t *rec = json_object_get(serv, "recommended"); if (rec && json_is_object(rec)) { @@ -191,7 +199,7 @@ static void add_services(obs_property_t *list, json_t *root, bool show_all, add_service(list, service, show_all, cur_service); } - service = find_service(root, cur_service); + service = find_service(root, cur_service, NULL); if (!service && cur_service && *cur_service) { obs_property_list_insert_string(list, 0, cur_service, cur_service); @@ -346,16 +354,32 @@ static void fill_servers(obs_property_t *servers_prop, json_t *service, } } -static inline json_t *find_service(json_t *root, const char *name) +static inline json_t *find_service(json_t *root, const char *name, + const char **p_new_name) { size_t index; json_t *service; + if (p_new_name) *p_new_name = NULL; + json_array_foreach (root, index, service) { const char *cur_name = get_string_val(service, "name"); if (strcmp(name, cur_name) == 0) return service; + + /* check for alternate names */ + json_t *alt_names = json_object_get(service, "alt_names"); + size_t alt_name_idx; + json_t *alt_name_obj; + + json_array_foreach (alt_names, alt_name_idx, alt_name_obj) { + const char *alt_name = json_string_value(alt_name_obj); + if (alt_name && strcmp(name, alt_name) == 0) { + if (p_new_name) *p_new_name = cur_name; + return service; + } + } } return NULL; @@ -367,11 +391,12 @@ static bool service_selected(obs_properties_t *props, obs_property_t *p, const char *name = obs_data_get_string(settings, "service"); json_t *root = obs_properties_get_param(props); json_t *service; + const char *new_name; if (!name || !*name) return false; - service = find_service(root, name); + service = find_service(root, name, &new_name); if (!service) { const char *server = obs_data_get_string(settings, "server"); @@ -383,6 +408,10 @@ static bool service_selected(obs_properties_t *props, obs_property_t *p, obs_property_list_item_disable(p, 0, true); return true; } + if (new_name) { + name = new_name; + obs_data_set_string(settings, "service", name); + } fill_servers(obs_properties_get(props, "server"), service, name); @@ -498,7 +527,7 @@ static void apply_audio_encoder_settings(obs_data_t *settings, static void initialize_output(struct rtmp_common *service, json_t *root, obs_data_t *video_settings, obs_data_t *audio_settings) { - json_t *json_service = find_service(root, service->service); + json_t *json_service = find_service(root, service->service, NULL); json_t *recommended; if (!json_service) { diff --git a/plugins/rtmp-services/rtmp-format-ver.h b/plugins/rtmp-services/rtmp-format-ver.h index b861e18..8e4952a 100644 --- a/plugins/rtmp-services/rtmp-format-ver.h +++ b/plugins/rtmp-services/rtmp-format-ver.h @@ -1,3 +1,3 @@ #pragma once -#define RTMP_SERVICES_FORMAT_VERSION 1 +#define RTMP_SERVICES_FORMAT_VERSION 2 diff --git a/plugins/rtmp-services/rtmp-services-main.c b/plugins/rtmp-services/rtmp-services-main.c index 145d5b1..0eeaa73 100644 --- a/plugins/rtmp-services/rtmp-services-main.c +++ b/plugins/rtmp-services/rtmp-services-main.c @@ -10,6 +10,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("rtmp-services", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "OBS core RTMP services"; +} #define RTMP_SERVICES_LOG_STR "[rtmp-services plugin] " #define RTMP_SERVICES_VER_STR "rtmp-services plugin (libobs " OBS_VERSION ")" diff --git a/plugins/text-freetype2/data/locale/bg-BG.ini b/plugins/text-freetype2/data/locale/bg-BG.ini new file mode 100644 index 0000000..4a32502 --- /dev/null +++ b/plugins/text-freetype2/data/locale/bg-BG.ini @@ -0,0 +1,12 @@ +TextFreetype2="Текст (FreeType 2)" +Font="Шрифт" +Text="Текст" +TextFile="Текстов файл (UTF-8 или UTF-16)" +TextFileFilter="Текстови файлове (*.txt);;" +Color1="Цвят 1" +Color2="Цвят 2" +Outline="Контур" +DropShadow="Слагане на сянка" +ReadFromFile="Четене от файл" +CustomWidth="Специфична ширина на текста" + diff --git a/plugins/text-freetype2/data/locale/da-DK.ini b/plugins/text-freetype2/data/locale/da-DK.ini index bff6d6e..ffb05d0 100644 --- a/plugins/text-freetype2/data/locale/da-DK.ini +++ b/plugins/text-freetype2/data/locale/da-DK.ini @@ -1,7 +1,7 @@ TextFreetype2="Tekst (FreeType 2)" Font="Skrifttype" Text="Tekst" -TextFile="Tekstfil (UTF-8- eller UTF-16)" +TextFile="Tekstfil (UTF-8/-16)" TextFileFilter="Tekstfiler (*.txt);;" ChatLogMode="Chatlogtilstand" ChatLogLines="Chatloglinjer" @@ -9,7 +9,7 @@ Color1="Farve 1" Color2="Farve 2" Outline="Kontur" DropShadow="Drop Shadow" -ReadFromFile="Læse fra fil" -CustomWidth="Brugerdefineret tekstbredde" +ReadFromFile="Læs fra fil" +CustomWidth="Tilpasset tekstbredde" WordWrap="Tekstombrydning" diff --git a/plugins/text-freetype2/data/locale/de-DE.ini b/plugins/text-freetype2/data/locale/de-DE.ini index c043f37..46db766 100644 --- a/plugins/text-freetype2/data/locale/de-DE.ini +++ b/plugins/text-freetype2/data/locale/de-DE.ini @@ -3,8 +3,8 @@ Font="Schriftart" Text="Text" TextFile="Textdatei (UTF-8 oder UTF-16)" TextFileFilter="Textdateien (*.txt);;" -ChatLogMode="Chatlogmodus" -ChatLogLines="Chatlogzeilen" +ChatLogMode="Chatprotokollmodus" +ChatLogLines="Chatprotokollzeilen" Color1="Farbe 1" Color2="Farbe 2" Outline="Umrandung" diff --git a/plugins/text-freetype2/data/locale/fa-IR.ini b/plugins/text-freetype2/data/locale/fa-IR.ini new file mode 100644 index 0000000..6a1e508 --- /dev/null +++ b/plugins/text-freetype2/data/locale/fa-IR.ini @@ -0,0 +1,15 @@ +TextFreetype2="متن (FreeType 2)" +Font="فونت" +Text="متن" +TextFile="فایل متن (UTF-8 یا UTF-16)" +TextFileFilter="فایل های متنی (*.txt);;" +ChatLogMode="حالت Chatlog" +ChatLogLines="خط های Chatlog" +Color1="رنگ 1" +Color2="رنگ 2" +Outline="برون نما" +DropShadow="افتادن سایه" +ReadFromFile="از فایل بخوان" +CustomWidth="رنگ متن سفارشی" +WordWrap="بسته بندی کلمات" + diff --git a/plugins/text-freetype2/data/locale/fr-FR.ini b/plugins/text-freetype2/data/locale/fr-FR.ini index 9960695..2b8ffa3 100644 --- a/plugins/text-freetype2/data/locale/fr-FR.ini +++ b/plugins/text-freetype2/data/locale/fr-FR.ini @@ -11,5 +11,5 @@ Outline="Contour" DropShadow="Ombre portée" ReadFromFile="Lire depuis le fichier" CustomWidth="Largeur du texte personnalisée" -WordWrap="Retour à la ligne" +WordWrap="Retour à la ligne automatique" diff --git a/plugins/text-freetype2/data/locale/it-IT.ini b/plugins/text-freetype2/data/locale/it-IT.ini index fd54ce2..57313f9 100644 --- a/plugins/text-freetype2/data/locale/it-IT.ini +++ b/plugins/text-freetype2/data/locale/it-IT.ini @@ -3,13 +3,13 @@ Font="Carattere" Text="Testo" TextFile="File di testo (UTF-8 o UTF-16)" TextFileFilter="File di testo (*.txt);;" -ChatLogMode="Modalità di chat log" -ChatLogLines="Modalità di chat righe" +ChatLogMode="Modalità chat" +ChatLogLines="Righe da visualizzare in modalità chat" Color1="Colore 1" Color2="Colore 2" -Outline="Contorno linea" -DropShadow="Ombra esterna" +Outline="Contorno del testo" +DropShadow="Ombreggiatura del testo" ReadFromFile="Leggi da file" -CustomWidth="Larghezza testo personalizzato" -WordWrap="Word Wrap" +CustomWidth="Larghezza del testo personalizzata" +WordWrap="A capo automatico" diff --git a/plugins/text-freetype2/data/locale/mn-MN.ini b/plugins/text-freetype2/data/locale/mn-MN.ini new file mode 100644 index 0000000..b7e53dd --- /dev/null +++ b/plugins/text-freetype2/data/locale/mn-MN.ini @@ -0,0 +1,15 @@ +TextFreetype2="Текст (FreeType 2)" +Font="Фонт" +Text="Текст" +TextFile="Текст Файл (UTF-8 or UTF-16)" +TextFileFilter="Текст Файл (*.txt);;" +ChatLogMode="Чат маягийн горим" +ChatLogLines="Чат маягийн шугам" +Color1="1-р Өнгө" +Color2="2-р Өнгө" +Outline="Гадуурх зураас" +DropShadow="Сүүдэр Оруулах" +ReadFromFile="Файлаас уншуулах" +CustomWidth="Өөр өргөн текст" +WordWrap="Үг доошоо дараалах" + diff --git a/plugins/text-freetype2/data/locale/pt-PT.ini b/plugins/text-freetype2/data/locale/pt-PT.ini index e30102f..6234345 100644 --- a/plugins/text-freetype2/data/locale/pt-PT.ini +++ b/plugins/text-freetype2/data/locale/pt-PT.ini @@ -3,6 +3,8 @@ Font="Tipo de letra" Text="Texto" TextFile="Ficheiro de texto (UTF-8 ou UTF-16)" TextFileFilter="Ficheiros de texto (*.txt);;" +ChatLogMode="Modo de registo de chat" +ChatLogLines="Linhas do registo de chat" Color1="Cor 1" Color2="Cor 2" Outline="Contorno" diff --git a/plugins/text-freetype2/data/locale/sk-SK.ini b/plugins/text-freetype2/data/locale/sk-SK.ini index 3ef3bee..a7fc002 100644 --- a/plugins/text-freetype2/data/locale/sk-SK.ini +++ b/plugins/text-freetype2/data/locale/sk-SK.ini @@ -3,6 +3,8 @@ Font="Písmo" Text="Text" TextFile="Textový súbor (UTF-8 alebo UTF-16)" TextFileFilter="Textové súbory (*.txt);;" +ChatLogMode="Režim chatu" +ChatLogLines="Počet riadkov v režime chatu" Color1="Farba 1" Color2="Farba 2" Outline="Obrys" diff --git a/plugins/text-freetype2/data/locale/sr-CS.ini b/plugins/text-freetype2/data/locale/sr-CS.ini index 04947b3..9517468 100644 --- a/plugins/text-freetype2/data/locale/sr-CS.ini +++ b/plugins/text-freetype2/data/locale/sr-CS.ini @@ -3,6 +3,8 @@ Font="Font" Text="Tekst" TextFile="Tekstualni dokument (UTF-8 ili UTF-16)" TextFileFilter="Tekstualne datoteke (*.txt);;" +ChatLogMode="Režim prijavljivanja na chat" +ChatLogLines="Broj linija za prijavu na chat" Color1="Boja 1" Color2="Boja 2" Outline="Ivice" diff --git a/plugins/text-freetype2/data/locale/sr-SP.ini b/plugins/text-freetype2/data/locale/sr-SP.ini index 0fad95b..1ad9c97 100644 --- a/plugins/text-freetype2/data/locale/sr-SP.ini +++ b/plugins/text-freetype2/data/locale/sr-SP.ini @@ -3,6 +3,8 @@ Font="Фонт" Text="Текст" TextFile="Текстуални документ (UTF-8 или UTF-16)" TextFileFilter="Текстуалне датотеке (*.txt);;" +ChatLogMode="Режим пријављивања на сhat" +ChatLogLines="Број линија за пријаву на сhat" Color1="Боја 1" Color2="Боја 2" Outline="Ивице" diff --git a/plugins/text-freetype2/obs-convenience.h b/plugins/text-freetype2/obs-convenience.h index 7a717d7..3da3b98 100644 --- a/plugins/text-freetype2/obs-convenience.h +++ b/plugins/text-freetype2/obs-convenience.h @@ -15,6 +15,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ +#pragma once + #include gs_vertbuffer_t *create_uv_vbuffer(uint32_t num_verts, bool add_color); diff --git a/plugins/text-freetype2/text-freetype2.c b/plugins/text-freetype2/text-freetype2.c index 68dd655..9940ec7 100644 --- a/plugins/text-freetype2/text-freetype2.c +++ b/plugins/text-freetype2/text-freetype2.c @@ -28,6 +28,10 @@ FT_Library ft2_lib; OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("text-freetype2", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "FreeType2 text source"; +} uint32_t texbuf_w = 2048, texbuf_h = 2048; diff --git a/plugins/text-freetype2/text-freetype2.h b/plugins/text-freetype2/text-freetype2.h index a3fd49b..654b303 100644 --- a/plugins/text-freetype2/text-freetype2.h +++ b/plugins/text-freetype2/text-freetype2.h @@ -15,6 +15,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ +#pragma once + #include #include diff --git a/plugins/vlc-video/data/locale/ar-SA.ini b/plugins/vlc-video/data/locale/ar-SA.ini index 6802d44..94e77d3 100644 --- a/plugins/vlc-video/data/locale/ar-SA.ini +++ b/plugins/vlc-video/data/locale/ar-SA.ini @@ -1,3 +1,5 @@ VLCSource="مصدر فيديو VLC" Playlist="قائمة تشغيل" +SubtitleTrack="مسار الترجمة" +SubtitleEnable="تفعيل الترجمة" diff --git a/plugins/vlc-video/data/locale/bg-BG.ini b/plugins/vlc-video/data/locale/bg-BG.ini new file mode 100644 index 0000000..dd0ac7c --- /dev/null +++ b/plugins/vlc-video/data/locale/bg-BG.ini @@ -0,0 +1,15 @@ +VLCSource="Видеоизточник за VLC" +Playlist="Списък за изпълнение" +LoopPlaylist="Повтаряне на списъка за изпълнение" +Shuffle="Разбъркване на списъка за изпълнение" +PlaybackBehavior="Поведение при видимост" +PlaybackBehavior.StopRestart="Спиране, когато не се вижда, рестартиране когато се вижда" +PlaybackBehavior.PauseUnpause="Пауза, когато не се вижда, премахване на паузата, когато се вижда" +PlaybackBehavior.AlwaysPlay="Винаги да се пуска, дори когато не се вижда" +NetworkCaching="Мрежово кеширане (мс)" +PlayPause="Пускане/пауза" +Restart="Рестартиране" +Stop="Спиране" +PlaylistNext="Напред" +PlaylistPrev="Назад" + diff --git a/plugins/vlc-video/data/locale/ca-ES.ini b/plugins/vlc-video/data/locale/ca-ES.ini index 76247f9..19e2d79 100644 --- a/plugins/vlc-video/data/locale/ca-ES.ini +++ b/plugins/vlc-video/data/locale/ca-ES.ini @@ -12,4 +12,7 @@ Restart="Reinicia" Stop="Atura" PlaylistNext="Següent" PlaylistPrev="Anterior" +AudioTrack="Pista d'àudio" +SubtitleTrack="Pista de subtítols" +SubtitleEnable="Subtítols activats" diff --git a/plugins/vlc-video/data/locale/cs-CZ.ini b/plugins/vlc-video/data/locale/cs-CZ.ini index a7a10c7..0a4829c 100644 --- a/plugins/vlc-video/data/locale/cs-CZ.ini +++ b/plugins/vlc-video/data/locale/cs-CZ.ini @@ -12,4 +12,7 @@ Restart="Restartovat" Stop="Zastavit" PlaylistNext="Další" PlaylistPrev="Předchozí" +AudioTrack="Zvuková stopa" +SubtitleTrack="Stopa titulků" +SubtitleEnable="Zapnout titulky" diff --git a/plugins/vlc-video/data/locale/da-DK.ini b/plugins/vlc-video/data/locale/da-DK.ini index 402a022..28e8dc1 100644 --- a/plugins/vlc-video/data/locale/da-DK.ini +++ b/plugins/vlc-video/data/locale/da-DK.ini @@ -1,7 +1,7 @@ VLCSource="VLC-videokilde" Playlist="Afspilningsliste" LoopPlaylist="Gentag afspilningsliste" -Shuffle="Bland playliste" +Shuffle="Bland afspilningsliste" PlaybackBehavior="Synlighedsadfærd" PlaybackBehavior.StopRestart="Stop når ikke synlig, genstart når synlig" PlaybackBehavior.PauseUnpause="Pause når ikke synlig, genoptage når synlig" @@ -11,5 +11,8 @@ PlayPause="Afspil/Pause" Restart="Genstart" Stop="Stop" PlaylistNext="Næste" -PlaylistPrev="Forrige" +PlaylistPrev="Foregående" +AudioTrack="Lydspor" +SubtitleTrack="Undertekstspor" +SubtitleEnable="Undertekster aktiveret" diff --git a/plugins/vlc-video/data/locale/de-DE.ini b/plugins/vlc-video/data/locale/de-DE.ini index 2834a70..62d0d45 100644 --- a/plugins/vlc-video/data/locale/de-DE.ini +++ b/plugins/vlc-video/data/locale/de-DE.ini @@ -1,15 +1,18 @@ -VLCSource="VLC Videoquelle" +VLCSource="VLC-Videoquelle" Playlist="Wiedergabeliste" LoopPlaylist="Wiedergabeliste wiederholen" Shuffle="Wiedergabeliste zufällig wiedergeben" PlaybackBehavior="Sichtbarkeitsverhalten" -PlaybackBehavior.StopRestart="Anhalten wenn nicht sichtbar, neu starten wenn sichtbar" -PlaybackBehavior.PauseUnpause="Pausieren wenn nicht sichtbar, fortsetzen wenn sichtbar" +PlaybackBehavior.StopRestart="Anhalten, wenn nicht sichtbar, neustarten, wenn sichtbar" +PlaybackBehavior.PauseUnpause="Pausieren, wenn nicht sichtbar, fortsetzen, wenn sichtbar" PlaybackBehavior.AlwaysPlay="Immer abspielen, auch wenn nicht sichtbar" NetworkCaching="Netzwerkpuffer (ms)" PlayPause="Abspielen/Pausieren" -Restart="Neu starten" +Restart="Neustarten" Stop="Stop" PlaylistNext="Weiter" PlaylistPrev="Zurück" +AudioTrack="Audiospur" +SubtitleTrack="Untertitelspur" +SubtitleEnable="Untertitel aktivieren" diff --git a/plugins/vlc-video/data/locale/en-US.ini b/plugins/vlc-video/data/locale/en-US.ini index 7bf2004..c107364 100644 --- a/plugins/vlc-video/data/locale/en-US.ini +++ b/plugins/vlc-video/data/locale/en-US.ini @@ -12,3 +12,6 @@ Restart="Restart" Stop="Stop" PlaylistNext="Next" PlaylistPrev="Previous" +AudioTrack="Audio Track" +SubtitleTrack="Subtitle Track" +SubtitleEnable="Subtitles Enabled" diff --git a/plugins/vlc-video/data/locale/es-ES.ini b/plugins/vlc-video/data/locale/es-ES.ini index 8ff5597..35b0423 100644 --- a/plugins/vlc-video/data/locale/es-ES.ini +++ b/plugins/vlc-video/data/locale/es-ES.ini @@ -12,4 +12,7 @@ Restart="Reiniciar" Stop="Detener" PlaylistNext="Siguiente" PlaylistPrev="Anterior" +AudioTrack="Pista de audio" +SubtitleTrack="Rastreador de subtítulos" +SubtitleEnable="Subtítulos activados" diff --git a/plugins/vlc-video/data/locale/eu-ES.ini b/plugins/vlc-video/data/locale/eu-ES.ini index 5ba5fdb..37d38dd 100644 --- a/plugins/vlc-video/data/locale/eu-ES.ini +++ b/plugins/vlc-video/data/locale/eu-ES.ini @@ -12,4 +12,7 @@ Restart="Berrabiarazi" Stop="Gelditu" PlaylistNext="Hurrengoa" PlaylistPrev="Aurrekoa" +AudioTrack="Audio-pista" +SubtitleTrack="Azpitituluen pista" +SubtitleEnable="Azpitituluak gaituta" diff --git a/plugins/vlc-video/data/locale/fa-IR.ini b/plugins/vlc-video/data/locale/fa-IR.ini new file mode 100644 index 0000000..3a98f47 --- /dev/null +++ b/plugins/vlc-video/data/locale/fa-IR.ini @@ -0,0 +1,14 @@ +VLCSource="منبع ویدئو VLC" +Playlist="لیست پخش" +LoopPlaylist="فهرست پخش چرخشی" +Shuffle="پخش درهم" +PlaybackBehavior="کنش های دیداری" +PlaybackBehavior.StopRestart="توقف زمانی که قابل مشاهده نیست، راه اندازی مجدد زمانی که قابل مشاهده است" +PlaybackBehavior.PauseUnpause="توقف زمانی که قابل مشاهده نیست، راه اندازی مجدد زمانی که قابل مشاهده است" +NetworkCaching="ذخیره سازی اینترنت (ms)" +PlayPause="پخش/توقف" +Restart="راه اندازی مجدد" +Stop="توقف" +PlaylistNext="بعدی" +PlaylistPrev="قبلی" + diff --git a/plugins/vlc-video/data/locale/fi-FI.ini b/plugins/vlc-video/data/locale/fi-FI.ini index 869f774..5c604f7 100644 --- a/plugins/vlc-video/data/locale/fi-FI.ini +++ b/plugins/vlc-video/data/locale/fi-FI.ini @@ -12,4 +12,7 @@ Restart="Aloita alusta" Stop="Pysäytä" PlaylistNext="Seuraava" PlaylistPrev="Edellinen" +AudioTrack="Ääniraita" +SubtitleTrack="Tekstitysraita" +SubtitleEnable="Tekstitykset käytössä" diff --git a/plugins/vlc-video/data/locale/fr-FR.ini b/plugins/vlc-video/data/locale/fr-FR.ini index d5b6eea..eaa219b 100644 --- a/plugins/vlc-video/data/locale/fr-FR.ini +++ b/plugins/vlc-video/data/locale/fr-FR.ini @@ -1,15 +1,18 @@ VLCSource="Source vidéo VLC" Playlist="Liste de lecture" LoopPlaylist="Répéter la liste de lecture" -Shuffle="Playlist Hazard" +Shuffle="Lecture aléatoire" 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" -PlaybackBehavior.AlwaysPlay="Toujours jouer même lorsqu'elle n'est pas visible" +PlaybackBehavior.StopRestart="Arrêter quand la source n'est pas visible, redémarrer lorsqu'elle est visible" +PlaybackBehavior.PauseUnpause="Mettre en pause lorsque la source n'est pas visible, reprendre lorsqu'elle est visible" +PlaybackBehavior.AlwaysPlay="Toujours lire même lorsque la source n'est pas visible" NetworkCaching="Mise en cache réseau (ms)" PlayPause="Lecture/Pause" Restart="Redémarrer" Stop="Arrêter" PlaylistNext="Suivant" PlaylistPrev="Précédent" +AudioTrack="Piste audio" +SubtitleTrack="Piste de sous-titre" +SubtitleEnable="Activer les sous-titres" diff --git a/plugins/vlc-video/data/locale/he-IL.ini b/plugins/vlc-video/data/locale/he-IL.ini index b9c8430..d3ef14b 100644 --- a/plugins/vlc-video/data/locale/he-IL.ini +++ b/plugins/vlc-video/data/locale/he-IL.ini @@ -1,6 +1,6 @@ VLCSource="מקור וידאו VLC" Playlist="רשימת השמעה" -LoopPlaylist="לולאת רשימת השמעה" +LoopPlaylist="נגינה חוזרת של רשימת ההשמעה" Shuffle="ערבב רשימת השמעה" PlaybackBehavior="התנהגות ניראות" PlaybackBehavior.StopRestart="עצור כאשר אינו נראה, התחל מחדש כאשר נראה" diff --git a/plugins/vlc-video/data/locale/hu-HU.ini b/plugins/vlc-video/data/locale/hu-HU.ini index 1fa1ffe..28ef03c 100644 --- a/plugins/vlc-video/data/locale/hu-HU.ini +++ b/plugins/vlc-video/data/locale/hu-HU.ini @@ -12,4 +12,7 @@ Restart="Újraindítás" Stop="Leállítás" PlaylistNext="Következő" PlaylistPrev="Előző" +AudioTrack="Hangsáv" +SubtitleTrack="Feliratsáv" +SubtitleEnable="Feliratok engedélyezése" diff --git a/plugins/vlc-video/data/locale/it-IT.ini b/plugins/vlc-video/data/locale/it-IT.ini index 5a8e1cc..d71f06d 100644 --- a/plugins/vlc-video/data/locale/it-IT.ini +++ b/plugins/vlc-video/data/locale/it-IT.ini @@ -1,15 +1,18 @@ -VLCSource="Source Video VLC" +VLCSource="Fonte video di VLC" Playlist="Playlist" -LoopPlaylist="Riproduci playlist di continuo" -Shuffle="Playlist casuale" +LoopPlaylist="Ripeti la playlist" +Shuffle="Mescola la playlist" PlaybackBehavior="Comportamento visibilità" -PlaybackBehavior.StopRestart="Interrompi quando non visibile, riavvia quando visibile" +PlaybackBehavior.StopRestart="Interrompi quando non visibile, ricomincia quando visibile" PlaybackBehavior.PauseUnpause="Pausa quando non visibile, riprendi quando visibile" -PlaybackBehavior.AlwaysPlay="Continua anche quando non visibile" -NetworkCaching="Network Caching (ms)" -PlayPause="Play/Pausa" -Restart="Riavvia" +PlaybackBehavior.AlwaysPlay="Continua sempre anche quando non visibile" +NetworkCaching="Caching di rete (in ms)" +PlayPause="Riproduci/pausa" +Restart="Ricomincia" Stop="Interrompi" PlaylistNext="Successivo" PlaylistPrev="Precedente" +AudioTrack="Traccia audio" +SubtitleTrack="Traccia sottotitoli" +SubtitleEnable="Sottotitoli attivati" diff --git a/plugins/vlc-video/data/locale/ja-JP.ini b/plugins/vlc-video/data/locale/ja-JP.ini index 0812205..732733c 100644 --- a/plugins/vlc-video/data/locale/ja-JP.ini +++ b/plugins/vlc-video/data/locale/ja-JP.ini @@ -12,4 +12,7 @@ Restart="再開" Stop="停止" PlaylistNext="次へ" PlaylistPrev="前へ" +AudioTrack="音声トラック" +SubtitleTrack="字幕トラック" +SubtitleEnable="字幕を有効にする" diff --git a/plugins/vlc-video/data/locale/ka-GE.ini b/plugins/vlc-video/data/locale/ka-GE.ini index 3f39162..d21f9aa 100644 --- a/plugins/vlc-video/data/locale/ka-GE.ini +++ b/plugins/vlc-video/data/locale/ka-GE.ini @@ -12,4 +12,7 @@ Restart="თავიდან გაშვება" Stop="შეწყვეტა" PlaylistNext="შემდეგი" PlaylistPrev="წინა" +AudioTrack="ხმოვანი ბილიკი" +SubtitleTrack="სუბტიტრების ჩანაწერი" +SubtitleEnable="სუბტიტრები ჩართულია" diff --git a/plugins/vlc-video/data/locale/ko-KR.ini b/plugins/vlc-video/data/locale/ko-KR.ini index 20d85e0..c2cc00c 100644 --- a/plugins/vlc-video/data/locale/ko-KR.ini +++ b/plugins/vlc-video/data/locale/ko-KR.ini @@ -12,4 +12,7 @@ Restart="재시작" Stop="중단" PlaylistNext="다음" PlaylistPrev="이전" +AudioTrack="음성 트랙" +SubtitleTrack="자막 트랙:" +SubtitleEnable="자막 활성화" diff --git a/plugins/vlc-video/data/locale/nb-NO.ini b/plugins/vlc-video/data/locale/nb-NO.ini index 59c3280..5f9a1ff 100644 --- a/plugins/vlc-video/data/locale/nb-NO.ini +++ b/plugins/vlc-video/data/locale/nb-NO.ini @@ -12,4 +12,7 @@ Restart="Start på nytt" Stop="Stopp avspilling" PlaylistNext="Neste" PlaylistPrev="Forrige" +AudioTrack="Lydspor" +SubtitleTrack="Undertekstspor" +SubtitleEnable="Undertekster aktivert" diff --git a/plugins/vlc-video/data/locale/nl-NL.ini b/plugins/vlc-video/data/locale/nl-NL.ini index 1516ac0..c9f6370 100644 --- a/plugins/vlc-video/data/locale/nl-NL.ini +++ b/plugins/vlc-video/data/locale/nl-NL.ini @@ -12,4 +12,7 @@ Restart="Herstart" Stop="Stop" PlaylistNext="Volgende" PlaylistPrev="Vorige" +AudioTrack="Geluidsspoor" +SubtitleTrack="Ondertitelspoor" +SubtitleEnable="Ondertitels ingeschakeld" diff --git a/plugins/vlc-video/data/locale/pl-PL.ini b/plugins/vlc-video/data/locale/pl-PL.ini index 60f8922..c329796 100644 --- a/plugins/vlc-video/data/locale/pl-PL.ini +++ b/plugins/vlc-video/data/locale/pl-PL.ini @@ -12,4 +12,7 @@ Restart="Zrestartuj" Stop="Zatrzymaj" PlaylistNext="Następny" PlaylistPrev="Poprzedni" +AudioTrack="Ścieżka audio" +SubtitleTrack="Ścieżka napisów" +SubtitleEnable="Napisy włączone" diff --git a/plugins/vlc-video/data/locale/pt-BR.ini b/plugins/vlc-video/data/locale/pt-BR.ini index 8cdc6eb..23f0d0c 100644 --- a/plugins/vlc-video/data/locale/pt-BR.ini +++ b/plugins/vlc-video/data/locale/pt-BR.ini @@ -12,4 +12,7 @@ Restart="Reiniciar" Stop="Parar" PlaylistNext="Próximo" PlaylistPrev="Anterior" +AudioTrack="Faixa de áudio" +SubtitleTrack="Faixa de legenda" +SubtitleEnable="Legendas ativadas" diff --git a/plugins/vlc-video/data/locale/ro-RO.ini b/plugins/vlc-video/data/locale/ro-RO.ini index 21720d9..1988c04 100644 --- a/plugins/vlc-video/data/locale/ro-RO.ini +++ b/plugins/vlc-video/data/locale/ro-RO.ini @@ -1,2 +1,15 @@ VLCSource="Sursă Video VLC" +Playlist="Listă de redare" +LoopPlaylist="Repetă lista de redare" +Shuffle="Amestecă lista de redare" +PlaybackBehavior="Comportament vizibilitate" +PlaybackBehavior.StopRestart="Oprește cand nu este vizibil, repornește cand este vizibil" +PlaybackBehavior.PauseUnpause="Pauză cand nu este vizibil, continuă cand este vizibil" +PlaybackBehavior.AlwaysPlay="Redă întotdeauna chiar si când nu este vizibil" +NetworkCaching="Caching de rețea (ms)" +PlayPause="Redă/Pauză" +Restart="Repornește" +Stop="Oprește" +PlaylistNext="Următorul" +PlaylistPrev="Anteriorul" diff --git a/plugins/vlc-video/data/locale/ru-RU.ini b/plugins/vlc-video/data/locale/ru-RU.ini index a6c6b52..03c12b0 100644 --- a/plugins/vlc-video/data/locale/ru-RU.ini +++ b/plugins/vlc-video/data/locale/ru-RU.ini @@ -12,4 +12,7 @@ Restart="Перезапустить" Stop="Остановить" PlaylistNext="Следующий" PlaylistPrev="Предыдущий" +AudioTrack="Звуковая дорожка" +SubtitleTrack="Дорожка субтитров" +SubtitleEnable="Субтитры включены" diff --git a/plugins/vlc-video/data/locale/sk-SK.ini b/plugins/vlc-video/data/locale/sk-SK.ini index 516690b..0140a08 100644 --- a/plugins/vlc-video/data/locale/sk-SK.ini +++ b/plugins/vlc-video/data/locale/sk-SK.ini @@ -1,5 +1,5 @@ VLCSource="VLC Video zdroj" -Playlist="Playlist" +Playlist="Zoznam skladieb" LoopPlaylist="Opakovať zoznam skladieb" Shuffle="Prehrať zoznam skladieb náhodne" PlaybackBehavior="Fungovanie podľa viditeľnosti" @@ -12,4 +12,7 @@ Restart="Reštartovať" Stop="Zastaviť" PlaylistNext="Ďalší" PlaylistPrev="Predchádzajúci" +AudioTrack="Zvuková stopa" +SubtitleTrack="Stopa titulkov" +SubtitleEnable="Zapnuté titulky" diff --git a/plugins/vlc-video/data/locale/sr-CS.ini b/plugins/vlc-video/data/locale/sr-CS.ini index a136269..ff2ec80 100644 --- a/plugins/vlc-video/data/locale/sr-CS.ini +++ b/plugins/vlc-video/data/locale/sr-CS.ini @@ -1,8 +1,15 @@ VLCSource="VLC video izvor" Playlist="Plejlista" LoopPlaylist="Ponavljanje plejliste" +Shuffle="Puštaj nasumično" PlaybackBehavior="Ponašanje pri (ne)vidljivosti" PlaybackBehavior.StopRestart="Zaustavi kada se ne vidi, ponovi kad se vidi" PlaybackBehavior.PauseUnpause="Pauziraj kada se ne vidi, odpauziraj kada se vidi" PlaybackBehavior.AlwaysPlay="Uvek reprodukuj čak i kada se ne vidi" +NetworkCaching="Kešing mreže (ms)" +PlayPause="Pusti/Pauziraj" +Restart="Restartuj" +Stop="Zaustavi" +PlaylistNext="Sledeći" +PlaylistPrev="Prethodni" diff --git a/plugins/vlc-video/data/locale/sr-SP.ini b/plugins/vlc-video/data/locale/sr-SP.ini index 6a670cd..bba1c79 100644 --- a/plugins/vlc-video/data/locale/sr-SP.ini +++ b/plugins/vlc-video/data/locale/sr-SP.ini @@ -1,8 +1,15 @@ VLCSource="VLC видео извор" Playlist="Плејлиста" LoopPlaylist="Понављање плејлисте" +Shuffle="Пуштај насумично" PlaybackBehavior="Понашање при (не)видљивости" PlaybackBehavior.StopRestart="Заустави када се не види, понови када се види" PlaybackBehavior.PauseUnpause="Паузирај када се не види, одпаузирај када се види" PlaybackBehavior.AlwaysPlay="Увек репродукуј чак и када се не види" +NetworkCaching="Кешинг мреже (ms)" +PlayPause="Пусти/Паузирај" +Restart="Рестартуј" +Stop="Заустави" +PlaylistNext="Следећи" +PlaylistPrev="Претходни" diff --git a/plugins/vlc-video/data/locale/sv-SE.ini b/plugins/vlc-video/data/locale/sv-SE.ini index 3659b6d..9d1c603 100644 --- a/plugins/vlc-video/data/locale/sv-SE.ini +++ b/plugins/vlc-video/data/locale/sv-SE.ini @@ -12,4 +12,7 @@ Restart="Starta om" Stop="Stoppa" PlaylistNext="Nästa" PlaylistPrev="Föregående" +AudioTrack="Ljudspår" +SubtitleTrack="Undertextspår" +SubtitleEnable="Aktivera undertexter" diff --git a/plugins/vlc-video/data/locale/tr-TR.ini b/plugins/vlc-video/data/locale/tr-TR.ini index 6613990..1973625 100644 --- a/plugins/vlc-video/data/locale/tr-TR.ini +++ b/plugins/vlc-video/data/locale/tr-TR.ini @@ -12,4 +12,7 @@ Restart="Yeniden Başlat" Stop="Durdur" PlaylistNext="Sonraki" PlaylistPrev="Önceki" +AudioTrack="Ses Parçası" +SubtitleTrack="Altyazı Parçası" +SubtitleEnable="Altyazılar Etkin" diff --git a/plugins/vlc-video/data/locale/uk-UA.ini b/plugins/vlc-video/data/locale/uk-UA.ini index 0ee636f..009ac30 100644 --- a/plugins/vlc-video/data/locale/uk-UA.ini +++ b/plugins/vlc-video/data/locale/uk-UA.ini @@ -12,4 +12,7 @@ Restart="Грати з початку" Stop="Зупинити" PlaylistNext="Наступний" PlaylistPrev="Попередній" +AudioTrack="Аудіо доріжка" +SubtitleTrack="Доріжка субтитрів" +SubtitleEnable="Увімкнути субтитри" diff --git a/plugins/vlc-video/data/locale/zh-CN.ini b/plugins/vlc-video/data/locale/zh-CN.ini index d033d57..9e77e43 100644 --- a/plugins/vlc-video/data/locale/zh-CN.ini +++ b/plugins/vlc-video/data/locale/zh-CN.ini @@ -12,4 +12,7 @@ Restart="重启" Stop="停止" PlaylistNext="下一个" PlaylistPrev="上一个" +AudioTrack="音频轨道" +SubtitleTrack="字幕轨道" +SubtitleEnable="开启字幕" diff --git a/plugins/vlc-video/data/locale/zh-TW.ini b/plugins/vlc-video/data/locale/zh-TW.ini index c5c2664..b794fbd 100644 --- a/plugins/vlc-video/data/locale/zh-TW.ini +++ b/plugins/vlc-video/data/locale/zh-TW.ini @@ -12,4 +12,7 @@ Restart="重新開始" Stop="停止" PlaylistNext="下一個" PlaylistPrev="前一個" +AudioTrack="音樂曲目" +SubtitleTrack="字幕軌" +SubtitleEnable="字幕已啟用" diff --git a/plugins/vlc-video/vlc-video-plugin.c b/plugins/vlc-video/vlc-video-plugin.c index d87ae51..b0668e0 100644 --- a/plugins/vlc-video/vlc-video-plugin.c +++ b/plugins/vlc-video/vlc-video-plugin.c @@ -7,6 +7,10 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("vlc-video", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "VLC playlist source"; +} /* libvlc core */ LIBVLC_NEW libvlc_new_; diff --git a/plugins/vlc-video/vlc-video-source.c b/plugins/vlc-video/vlc-video-source.c index 09c6da4..a436e2e 100644 --- a/plugins/vlc-video/vlc-video-source.c +++ b/plugins/vlc-video/vlc-video-source.c @@ -18,6 +18,9 @@ #define S_BEHAVIOR_PAUSE_UNPAUSE "pause_unpause" #define S_BEHAVIOR_ALWAYS_PLAY "always_play" #define S_NETWORK_CACHING "network_caching" +#define S_TRACK "track" +#define S_SUBTITLE_ENABLE "subtitle_enable" +#define S_SUBTITLE_TRACK "subtitle" #define T_(text) obs_module_text(text) #define T_PLAYLIST T_("Playlist") @@ -28,6 +31,9 @@ #define T_BEHAVIOR_PAUSE_UNPAUSE T_("PlaybackBehavior.PauseUnpause") #define T_BEHAVIOR_ALWAYS_PLAY T_("PlaybackBehavior.AlwaysPlay") #define T_NETWORK_CACHING T_("NetworkCaching") +#define T_TRACK T_("AudioTrack") +#define T_SUBTITLE_ENABLE T_("SubtitleEnable") +#define T_SUBTITLE_TRACK T_("SubtitleTrack") /* ------------------------------------------------------------------------- */ @@ -176,7 +182,7 @@ static enum video_format convert_vlc_video_format(char *chroma, bool *full) /* 4:4:4 formats */ CHROMA_TEST("I444", VIDEO_FORMAT_I444); - CHROMA_CONV_FULL("J444", "J444", VIDEO_FORMAT_I444); + CHROMA_CONV_FULL("J444", "RGBA", VIDEO_FORMAT_RGBA); CHROMA_CONV("YUVA", "RGBA", VIDEO_FORMAT_RGBA); /* 4:4:0 formats */ @@ -403,7 +409,8 @@ static int vlcs_audio_setup(void **p_data, char *format, unsigned *rate, } static void add_file(struct vlc_source *c, struct darray *array, - const char *path, int network_caching) + const char *path, int network_caching, int track_index, + int subtitle_index, bool subtitle_enable) { DARRAY(struct media_file_data) new_files; struct media_file_data data; @@ -436,6 +443,19 @@ static void add_file(struct vlc_source *c, struct darray *array, network_caching_option.array); dstr_free(&network_caching_option); } + struct dstr track_option = { 0 }; + dstr_catf(&track_option, + ":audio-track=%d", track_index - 1); + libvlc_media_add_option_(new_media, track_option.array); + dstr_free(&track_option); + + struct dstr sub_option = {0}; + if (subtitle_enable) { + dstr_catf(&sub_option, + ":sub-track=%d", subtitle_index - 1); + } + libvlc_media_add_option_(new_media, sub_option.array); + dstr_free(&sub_option); data.path = new_path.array; data.media = new_media; @@ -490,6 +510,9 @@ static void vlcs_update(void *data, obs_data_t *settings) const char *behavior; size_t count; int network_caching; + int track_index; + int subtitle_index; + bool subtitle_enable; da_init(new_files); da_init(old_files); @@ -503,6 +526,12 @@ static void vlcs_update(void *data, obs_data_t *settings) network_caching = (int)obs_data_get_int(settings, S_NETWORK_CACHING); + track_index = (int)obs_data_get_int(settings, S_TRACK); + + subtitle_index = (int)obs_data_get_int(settings, S_SUBTITLE_TRACK); + + subtitle_enable = obs_data_get_bool(settings, S_SUBTITLE_ENABLE); + if (astrcmpi(behavior, S_BEHAVIOR_PAUSE_UNPAUSE) == 0) { c->behavior = BEHAVIOR_PAUSE_UNPAUSE; } else if (astrcmpi(behavior, S_BEHAVIOR_ALWAYS_PLAY) == 0) { @@ -540,13 +569,17 @@ static void vlcs_update(void *data, obs_data_t *settings) dstr_cat_ch(&dir_path, '/'); dstr_cat(&dir_path, ent->d_name); add_file(c, &new_files.da, dir_path.array, - network_caching); + network_caching, track_index, + subtitle_index, + subtitle_enable); } dstr_free(&dir_path); os_closedir(dir); } else { - add_file(c, &new_files.da, path, network_caching); + add_file(c, &new_files.da, path, network_caching, + track_index, subtitle_index, + subtitle_enable); } obs_data_release(item); @@ -833,6 +866,9 @@ static void vlcs_defaults(obs_data_t *settings) obs_data_set_default_string(settings, S_BEHAVIOR, S_BEHAVIOR_STOP_RESTART); obs_data_set_default_int(settings, S_NETWORK_CACHING, 400); + obs_data_set_default_int(settings, S_TRACK, 1); + obs_data_set_default_bool(settings, S_SUBTITLE_ENABLE, false); + obs_data_set_default_int(settings, S_SUBTITLE_TRACK, 1); } static obs_properties_t *vlcs_properties(void *data) @@ -902,6 +938,10 @@ static obs_properties_t *vlcs_properties(void *data) obs_properties_add_int(ppts, S_NETWORK_CACHING, T_NETWORK_CACHING, 100, 60000, 10); + obs_properties_add_int(ppts, S_TRACK, T_TRACK, 1, 10, 1); + obs_properties_add_bool(ppts, S_SUBTITLE_ENABLE, T_SUBTITLE_ENABLE); + obs_properties_add_int(ppts, S_SUBTITLE_TRACK, T_SUBTITLE_TRACK, + 1, 10, 1); return ppts; } From 34aa2c5c968a5b6fe942d70d8b812c4cf3abfe22 Mon Sep 17 00:00:00 2001 From: Simon Chopin Date: Sat, 27 Jul 2019 15:07:36 +0200 Subject: [PATCH 2/4] Refresh the license display patches They have been partially obsoleted by the following pull request upstream: https://github.com/obsproject/obs-studio/pull/1435 --- debian/patches/0001-Use-common-license.patch | 28 +++++++++++-------- ...Add-SONAME.patch => 0006-Add-SONAME.patch} | 0 ...not-display-license-on-first-startup.patch | 22 --------------- debian/patches/series | 3 +- 4 files changed, 17 insertions(+), 36 deletions(-) rename debian/patches/{0007-Add-SONAME.patch => 0006-Add-SONAME.patch} (100%) delete mode 100644 debian/patches/0006-Do-not-display-license-on-first-startup.patch diff --git a/debian/patches/0001-Use-common-license.patch b/debian/patches/0001-Use-common-license.patch index d9c1450..15168f0 100644 --- a/debian/patches/0001-Use-common-license.patch +++ b/debian/patches/0001-Use-common-license.patch @@ -8,18 +8,22 @@ copy UI/window-license-agreement.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) -diff --git a/UI/window-license-agreement.cpp b/UI/window-license-agreement.cpp -index a7a5f3b..60cad40 100644 ---- a/UI/window-license-agreement.cpp -+++ b/UI/window-license-agreement.cpp -@@ -13,9 +13,7 @@ OBSLicenseAgreement::OBSLicenseAgreement(QWidget *parent) +--- a/UI/window-basic-about.cpp ++++ b/UI/window-basic-about.cpp +@@ -149,15 +149,10 @@ + + void OBSAbout::ShowLicense() { - ui->setupUi(this); +- std::string path; ++ std::string path("/usr/share/common-licenses/GPL-2"); + QString error = "Error! File could not be read.\n\n \ + Go to: https://github.com/obsproject/obs-studio/blob/master/COPYING"; -- string path; -- if (!GetDataFilePath("license/gplv2.txt", path)) -- throw "Could not find license file"; -+ string path("/usr/share/common-licenses/GPL-2"); +- if (!GetDataFilePath("license/gplv2.txt", path)) { +- ui->textBrowser->setPlainText(error); +- return; +- } +- + BPtr text = os_quick_read_utf8_file(path.c_str()); - BPtr licenseText = os_quick_read_utf8_file(path.c_str()); - if (!licenseText || !*licenseText || strlen(licenseText) < 1000) + if (!text || !*text) { diff --git a/debian/patches/0007-Add-SONAME.patch b/debian/patches/0006-Add-SONAME.patch similarity index 100% rename from debian/patches/0007-Add-SONAME.patch rename to debian/patches/0006-Add-SONAME.patch diff --git a/debian/patches/0006-Do-not-display-license-on-first-startup.patch b/debian/patches/0006-Do-not-display-license-on-first-startup.patch deleted file mode 100644 index baad04b..0000000 --- a/debian/patches/0006-Do-not-display-license-on-first-startup.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: Chris Lamb -Date: Mon, 19 Feb 2018 21:32:07 +0100 -Subject: Do not display license on first startup - ---- - UI/obs-app.cpp | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp -index 52b15a0..186128c 100644 ---- a/UI/obs-app.cpp -+++ b/UI/obs-app.cpp -@@ -1216,8 +1216,7 @@ bool OBSApp::OBSInit() - { - ProfileScope("OBSApp::OBSInit"); - -- bool licenseAccepted = config_get_bool(globalConfig, "General", -- "LicenseAccepted"); -+ bool licenseAccepted = true; - OBSLicenseAgreement agreement(nullptr); - - if (licenseAccepted || agreement.exec() == QDialog::Accepted) { diff --git a/debian/patches/series b/debian/patches/series index 066dbfb..cf59670 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -3,5 +3,4 @@ 0003-Use-path-in-usr-lib.patch 0004-Link-with-libm-and-libdl-to-fix-underlinking.patch 0005-Link-with-xcb-and-X-libraries-to-fix-underlinking.patch -0006-Do-not-display-license-on-first-startup.patch -0007-Add-SONAME.patch +0006-Add-SONAME.patch From 53176e97fa28141c7a299342c3fc1e2afbcf7231 Mon Sep 17 00:00:00 2001 From: Simon Chopin Date: Sat, 27 Jul 2019 15:19:10 +0200 Subject: [PATCH 3/4] Refresh the use-path-in-usr-lib patch and the exec name and location Upstream has decided to now ship the ffmpeg-mux executable as a binary in /usr/bin. In order to keep the -plugins package Multiarch: Same, we still need to ship it in /usr/lib/x86_64, although we now use the new name to not diverge too much from upstream. --- debian/patches/0003-Use-path-in-usr-lib.patch | 6 ++---- debian/rules | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/debian/patches/0003-Use-path-in-usr-lib.patch b/debian/patches/0003-Use-path-in-usr-lib.patch index 8d657b7..ed4fa6d 100644 --- a/debian/patches/0003-Use-path-in-usr-lib.patch +++ b/debian/patches/0003-Use-path-in-usr-lib.patch @@ -6,18 +6,16 @@ Subject: Use path in /usr/lib plugins/obs-ffmpeg/obs-ffmpeg-mux.c | 4 ++++ 1 file changed, 4 insertions(+) -diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c -index 9b65276..60b0b8b 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c -@@ -237,7 +237,11 @@ static void build_command_line(struct ffmpeg_muxer *stream, struct dstr *cmd, +@@ -237,7 +237,11 @@ num_tracks++; } +#ifdef FFMPEG_MUX_FIXED + dstr_init_copy(cmd, FFMPEG_MUX_FIXED); +#else - dstr_init_move_array(cmd, obs_module_file(FFMPEG_MUX)); + dstr_init_move_array(cmd, os_get_executable_path_ptr(FFMPEG_MUX)); +#endif dstr_insert_ch(cmd, 0, '\"'); dstr_cat(cmd, "\" \""); diff --git a/debian/rules b/debian/rules index 34f6fbf..7704f71 100755 --- a/debian/rules +++ b/debian/rules @@ -4,7 +4,7 @@ include /usr/share/dpkg/architecture.mk include /usr/share/dpkg/pkg-info.mk export DEB_LDFLAGS_MAINT_APPEND=-Wl,--as-needed -export DEB_CPPFLAGS_MAINT_APPEND=-DFFMPEG_MUX_FIXED=\"/usr/lib/$(DEB_HOST_MULTIARCH)/obs-plugins/obs-ffmpeg/ffmpeg-mux\" +export DEB_CPPFLAGS_MAINT_APPEND=-DFFMPEG_MUX_FIXED=\"/usr/lib/$(DEB_HOST_MULTIARCH)/obs-plugins/obs-ffmpeg/obs-ffmpeg-mux\" %: dh $@ @@ -22,8 +22,8 @@ override_dh_auto_build: override_dh_install: mkdir -p debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/obs-plugins/obs-ffmpeg - mv debian/tmp/usr/share/obs/obs-plugins/obs-ffmpeg/ffmpeg-mux \ - debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/obs-plugins/obs-ffmpeg/ffmpeg-mux + mv debian/tmp/usr/bin/obs-ffmpeg-mux \ + debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/obs-plugins/obs-ffmpeg/obs-ffmpeg-mux dh_install rm -rf debian/obs-studio/usr/share/obs/obs-studio/license From 9b95788e00f94f04b38cfd272c6a00ccc37f34e8 Mon Sep 17 00:00:00 2001 From: Simon Chopin Date: Sun, 28 Jul 2019 20:09:35 +0200 Subject: [PATCH 4/4] Add libqt5svg5-dev as new build dep --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index e83a484..71f655c 100644 --- a/debian/control +++ b/debian/control @@ -23,6 +23,7 @@ Build-Depends: libjansson-dev (>= 2.5), libluajit-5.1-dev, libpulse-dev, + libqt5svg5-dev, libqt5x11extras5-dev, libspeexdsp-dev, libswresample-dev,